Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's a good and functional way to swap collection elements in Scala?

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.

like image 830
Andreas Eisele Avatar asked Jul 26 '10 13:07

Andreas Eisele


People also ask

Which of the following method make use of Scala collections?

Scala Collections - Array with Range Use of range() method to generate an array containing a sequence of increasing integers in a given range.


2 Answers

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)
}
like image 174
venechka Avatar answered Oct 22 '22 17:10

venechka


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.

like image 38
Apocalisp Avatar answered Oct 22 '22 18:10

Apocalisp