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

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

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

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


person Abhinav Sarkar    schedule 14.05.2010    source источник


Ответы (4)


В версии 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
person Rex Kerr    schedule 14.05.2010
comment
Метод 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())
}
person Abhinav Sarkar    schedule 15.05.2010

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

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

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

person erisco    schedule 14.05.2010

Использование сканированияЛево

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