Есть ли в Scala способ получить текущие оцениваемые элементы в потоке? Например в потоке
val s: Stream[Int] = Stream.cons(1, Stream.cons(2, Stream.cons(3, s.map(_+1))))
метод должен возвращать только List(1,2,3)
.
Есть ли в Scala способ получить текущие оцениваемые элементы в потоке? Например в потоке
val s: Stream[Int] = Stream.cons(1, Stream.cons(2, Stream.cons(3, s.map(_+1))))
метод должен возвращать только List(1,2,3)
.
В версии 2.8 есть защищенный метод с именем tailDefined
, который вернет false, когда вы дойдете до точки потока, которая еще не была оценена.
Это не слишком полезно (если только вы не хотите написать свой собственный класс Stream
), за исключением того, что сам Cons
делает метод общедоступным. Я не уверен, почему он защищен в 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
tailDefined
общедоступен как в Cons
, так и в Empty
, поэтому я не думаю, что это ошибка. Я не замечал этого раньше. Я могу адаптировать ваше решение для решения моей проблемы.
- person Abhinav Sarkar; 15.05.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())
}
Введите этот оператор в интерактивную оболочку, и вы увидите, что он оценивается как s: Stream[Int] = Stream(1, ?)
. Так что на самом деле два других элемента 2 и 3 пока неизвестны.
По мере доступа к дополнительным элементам вычисляется большая часть потока. Итак, теперь поместите s(3)
в оболочку, которая вернет res0: Int = 2
. Теперь поместите s
в оболочку, и вы увидите новое значение res1: Stream[Int] = Stream(1, 2, 3, 2, ?)
.
Единственный метод, который я смог найти, который содержал нужную вам информацию, был, к сожалению, s.toString
. С помощью некоторого синтаксического анализа вы сможете вернуть элементы из строки. Это едва ли приемлемое решение только с целыми числами, и я не мог представить какое-либо универсальное решение, использующее идею синтаксического анализа строк.
Использование сканированияЛево
lazy val s: Stream[Int] = 1 #:: s.scanLeft(2) { case (a, _) => 1 + a }