Перебирать список, возвращая текущий, следующий и элемент перед текущим - PullRequest
11 голосов
/ 25 января 2010

У меня проблемы с написанием конкретного приложения в стиле scala-esque и элегантно. Я пытался это в течение некоторого времени, но я не могу найти «хорошее» решение этой проблемы:

Учитывая, что у меня есть следующий список:

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

Я хочу перебрать этот список, предоставляя мне не только текущий элемент для каждой итерации, но и элемент до и после текущего элемента. Это может быть Tuple3, но это не обязательно. Это может быть подпись Tuple:

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

Чтобы прояснить, что я имею в виду, это предлагаемый кортеж для каждой итерации на List[String], заканчивающийся после четвертой.

Итерация 1: (None, "foo", Some("bar"))

Итерация 2: (Some("foo"), "bar", Some("baz"))

Итерация 3: (Some("bar"), "baz", Some("blah"))

Итерация 4: (Some("baz"), "blah", None)

Как я мог достичь такого результата? Опять же: я не связан с Tuple3, любое другое решение также очень ценится!

Спасибо!

Ответы [ 3 ]

16 голосов
/ 25 января 2010

Вот один из подходов. Он использует новый метод сбора Scala 2.8 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))

Обновление : Вот версия, которая работает для потоков.

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
3 голосов
/ 25 января 2010

Retronym ответ хорошо работает, если вы используете 2.8. Если вы используете 2.7.x, не существует отличного решения, но вы можете легко создать свое собственное. Например, если вы хотите только тройки, где до и после существуют, вы можете сделать что-то вроде этого:

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)

Если вы предпочитаете оставить опции до и после того, как останетесь, (изменить: я действительно не давал полный или безошибочный набор изменений до этого), вместо этого используйте

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)
2 голосов
/ 26 января 2010

Лучше использовать Scala 2.8 и решение Retronym , конечно, но вот мое решение для 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
  }
}
...