В Scala есть ли способ получить оцениваемые в данный момент элементы в потоке? - PullRequest
7 голосов
/ 14 мая 2010

Есть ли в Scala способ получить оцениваемые в данный момент элементы в потоке? Например в потоке

val s: Stream[Int] = Stream.cons(1, Stream.cons(2, Stream.cons(3, s.map(_+1))))

метод должен возвращать только List(1,2,3).

Ответы [ 4 ]

8 голосов
/ 14 мая 2010

В 2.8 есть защищенный метод с именем tailDefined, который вернет false, когда вы попадете в точку в потоке, которая еще не была оценена.

Это не слишком полезно (если вы не хотите писать собственный класс Stream), за исключением того, что Cons сам делает метод общедоступным. Я не уверен, почему это защищено в потоке, а не в минусах - я думаю, что один или другой может быть ошибкой. Но сейчас, по крайней мере, вы можете написать такой метод (запись функционального эквивалента оставлена ​​читателю в качестве упражнения):

def streamEvalLen[T](s: Stream[T]) = {
  if (s.isEmpty) 0
  else {
    var i = 1
    var t = s
    while (t match {
      case c: Stream.Cons[_] => c.tailDefined
      case _ => false
    }) {
      i += 1
      t = t.tail
    }
    i
  }
}

Здесь вы можете увидеть это в действии:

scala> val s = Stream.iterate(0)(_+1)
s: scala.collection.immutable.Stream[Int] = Stream(0, ?)

scala> streamEvalLen(s)
res0: Int = 1

scala> s.take(3).toList
res1: List[Int] = List(0, 1, 2)

scala> s
res2: scala.collection.immutable.Stream[Int] = Stream(0, 1, 2, ?)

scala> streamEvalLen(s)
res3: Int = 3
6 голосов
/ 15 мая 2010

Решение, основанное на ответе Рекса :

def evaluatedItems[T](stream: => Stream[T]): List[T] = {
  @tailrec
  def inner(s: => Stream[T], acc: List[T]): List[T] = s match {
    case Empty => acc
    case c: Cons[T] => if (c.tailDefined) {
      inner(c.tail, acc ++ List(c.head))
    } else { acc ++ List(c.head) }
  }
  inner(stream, List())
}
4 голосов
/ 14 мая 2010

Введите этот оператор в интерактивную оболочку, и вы увидите, что он оценивается как s: Stream[Int] = Stream(1, ?). Так что, на самом деле, два других элемента 2 и 3 еще не известны.

По мере доступа к другим элементам рассчитывается больше потока. Итак, теперь поместите s(3) в оболочку, которая вернет res0: Int = 2. Теперь поместите s в оболочку, и вы увидите новое значение res1: Stream[Int] = Stream(1, 2, 3, 2, ?).

Единственный метод, который я смог найти, который содержал информацию, которую вы хотели, был, к сожалению, s.toString. С помощью некоторого анализа вы сможете вернуть элементы обратно в строку. Это едва приемлемое решение с использованием только целых чисел, и я не мог представить никакого общего решения, использующего идею разбора строк.

0 голосов
/ 25 апреля 2014

Использование scanLeft

    lazy val s: Stream[Int] = 1 #:: s.scanLeft(2) { case (a, _) => 1 + a }
...