views:

372

answers:

3

Hello StackOverflow,

I have problems with writing an specific application in a scala-esque and elegant way. I tried this for some time now, but I cannot find a "good" solution to this Problem:

Given that I have the following List:

List("foo", "bar", "baz", "blah")

I want to Iterate over this list, not only giving me the current element for each Iteration but also the element before and after the current element. This might be a Tuple3 but is not required to. This could be the Tuple signature:

(Option[T], T, Option[T])

To clarify what i mean, this is the proposed Tuple for each iteration over a List[String], ending after the fourth.

Iteration 1: (None, "foo", Some("bar"))

Iteration 2: (Some("foo"), "bar", Some("baz"))

Iteration 3: (Some("bar"), "baz", Some("blah"))

Iteration 4: (Some("baz"), "blah", None)

How could I achieve such a result? Again: I am not bound to the Tuple3, any other solution is also very appreciated!

Thanks!

+6  A: 

Here's one approach. It uses a new Scala 2.8 collection method sliding.

def window[A](l: List[A]): Iterator[List[Option[A]]] = 
   (None :: l.map(Some(_)) ::: List(None)) sliding 3

window(List(1, 2, 3, 4, 5)).toList

// List(List(None, Some(1), Some(2)), List(Some(1), Some(2), Some(3)), List(Some(2), Some(3), Some(4)), List(Some(3), Some(4), Some(5)), List(Some(4), Some(5), None))

Update: Heres a version that works for Streams.

def windowS[A](s: Stream[A]): Stream[List[Option[A]]] = 
  (None #:: s.map(Some(_): Option[A]) #::: Stream(None: Option[A])).sliding(3).toStream.map(_.toList)  

val posInts = Stream.range(1, Integer.MAX_VALUE)
windowS(posInts).take(5).toList
retronym
I'm sure that this works, but my Version of Scala does not seem to have sliding definied. I'm using 2.8.0.Beta1-RC7, which Version is required to use sliding?
Malax
I'm using 2.8.0.Beta1-RC8
retronym
Seems that RC8 is required, works now. Thank you! :-)
Malax
+1  A: 

Retronym's answer works well if you're using 2.8. If you're using 2.7.x, there isn't a great stock solution, but you can build your own easily. For example, if you want only triples where before and after exist, you can do something like this:

class Tuple3Iterator[T](solo: Iterator[T]) extends Iterator[(T,T,T)] {
  var current = if (solo.hasNext) Some(solo.next) else None
  var future = if (solo.hasNext) Some(solo.next) else None
  def hasNext = solo.hasNext
  def next = {
    val past = current
    current = future
    future = Some(solo.next)
    (past.get,current.get,future.get)
  }
}
class IteratorToT3[T](it: Iterator[T]) {
  def treble = new Tuple3Iterator[T](it)
}
implicit def allowTrebling[T](it: Iterable[T]) = new IteratorToT3[T](it.elements)

scala> List("Hi","there",5,"you").treble.foreach(println(_))         
(Hi,there,5)
(there,5,you)

If you prefer to allow before and after to stay Options, (edit: I didn't really give a complete or error-free set of changes before) then instead use

class Tuple3Iterator[T](solo: Iterator[T]) extends Iterator[(Option[T],T,Option[T])] {
  var current = None:Option[T]
  var future = if (solo.hasNext) Some(solo.next) else None
  def hasNext = (solo.hasNext || future!=None)
  def next = {
    val past = current
    current = future
    future = if (solo.hasNext) Some(solo.next) else None
    (past,current.get,future)
  }
}

scala> List("Hi","there",5,"you").treble.foreach(println(_))
(None,Hi,Some(there))
(Some(Hi),there,Some(5))
(Some(there),5,Some(you))
(Some(5),you,None)
Rex Kerr
Even if I'm using Scala 2.8 already, this is a very nice bit of code for learning. Thank you for this contribution!
Malax
+2  A: 

Better use Scala 2.8 and retronym's solution, of course, but here is my solution for Scala 2.7:

class MyIterator[T](l: List[T]) extends Iterator[(Option[T],T,Option[T])] {
  var last: Option[T] = None
  var curr = l
  def hasNext = !curr.isEmpty
  def next = {
    val t = curr match {
      case first :: second :: tail => (last, first, Some(second))
      case first :: Nil => (last, first, None)
      case Nil => throw new java.util.NoSuchElementException
    }
    last = Some(curr.head)
    curr = curr.tail
    t
  }
}
Daniel