In a project of mine one common use case keeps coming up. At some point I've got a sorted collection of some kind (List, Seq, etc... doesn't matter) and one element of this collection. What I want to do is to swap the given element with it's following element (if this element exists) or at some times with the preceding element.
I'm well aware of the ways to achieve this using procedural programming techniques. My question is what would be a good way to solve the problem by means of functional programming (in Scala)?
Thank you all for your answers. I accepted the one I myself did understand the most. As I'm not a functional programmer (yet) it's kind of hard for me to decide which answer was truly the best. They are all pretty good in my opinion.
Scala Collections - Array with Range Use of range() method to generate an array containing a sequence of increasing integers in a given range.
The following is the functional version of swap with the next element in a list, you just construct a new list with elements swapped.
def swapWithNext[T](l: List[T], e : T) : List[T] = l match {
case Nil => Nil
case `e`::next::tl => next::e::tl
case hd::tl => hd::swapWithNext(tl, e)
}
A zipper is a pure functional data structure with a pointer into that structure. Put another way, it's an element with a context in some structure.
For example, the Scalaz library provides a Zipper
class which models a list with a particular element of the list in focus.
You can get a zipper for a list, focused on the first element.
import scalaz._
import Scalaz._
val z: Option[Zipper[Int]] = List(1,2,3,4).toZipper
You can move the focus of the zipper using methods on Zipper
, for example, you can move to the next offset from the current focus.
val z2: Option[Zipper[Int]] = z >>= (_.next)
This is like List.tail
except that it remembers where it has been.
Then, once you have your chosen element in focus, you can modify the elements around the focus.
val swappedWithNext: Option[Zipper[Int]] =
for (x <- z2;
y <- x.delete)
yield y.insertLeft(x.focus)
Note: this is with the latest Scalaz trunk head, in which a bug with Zipper's tail-recursive find
and move
methods has been fixed.
The method you want is then just:
def swapWithNext[T](l: List[T], p: T => Boolean) : List[T] = (for {
z <- l.toZipper
y <- z.findZ(p)
x <- y.delete
} yield x.insertLeft(y.focus).toStream.toList) getOrElse l
This matches an element based on a predicate p
. But you can go further and consider all nearby elements as well. For instance, to implement an insertion sort.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With