Scala, повторяйте конечный список бесконечно

Я хочу использовать класс Stream в scala для бесконечного повторения заданного списка.

Например список (1,2,3,4,5) Я хочу создать поток, который дает мне (1,2,3,4,5,1,2,3,4,5,1,2,3 ....)

Чтобы я мог обернуть операцию взятия. Я знаю, что это можно реализовать и по-другому, но я почему-то хочу сделать так, просто пошутите :)

Итак, идея состоит в том, что с этим бесконечным циклом, созданным из некоторого списка, я могу использовать операцию взятия, и когда она достигает конца списка, она зацикливается.

Как создать поток, который просто повторяет заданный список?


person Felix    schedule 19.01.2010    source источник


Ответы (5)


Очень похоже на @Eastsun, но немного больше раскрывает намерения. Протестировано в Scala 2.8.

scala> val l  = List(1, 2, 3)
l: List[Int] = List(1, 2, 3)

scala> Stream.continually(l.toStream).flatten.take(10).toList
res3: List[Int] = List(1, 2, 3, 1, 2, 3, 1, 2, 3, 1)

Альтернативно, со Scalaz:

scala> import scalaz._
import scalaz._

scala> import Scalaz._
import Scalaz._

scala> val l = List(1, 2, 3)
l: List[Int] = List(1, 2, 3)

scala> l.toStream.repeat[Stream].join.take(10).toList
res7: List[Int] = List(1, 2, 3, 1, 2, 3, 1, 2, 3, 1)
person retronym    schedule 20.01.2010
comment
Мне понравился этот ответ больше всего, постоянно на объекте-компаньоне Stream - это действительно то, что я искал. Кроме того, в сочетании с flatten и tolist я получаю именно то, что хочу :) Кстати, somestream.join.take(10).toList, может ли кто-нибудь уточнить необходимость нотации object.function() и почему она здесь нужна? Обычно у вас может быть seq take 10, и будет ли работать что-то вроде seq take 10.toList? - person Felix; 21.01.2010
comment
Я думаю, стоит отметить, что Stream.continually() явно не присутствовал в предыдущих выпусках от 2.8. - person Felix; 21.01.2010
comment
В версии 2.7 Stream.continually было Stream.const, а streams.flatten было Stream.concat(streams). - person retronym; 21.01.2010
comment
Возможно, вы могли бы перенести сравнение вызова метода с символом '.' и без него. на новый вопрос. - person retronym; 21.01.2010
comment
для записи Stream.concat() не работает, это то, что я попробовал в первую очередь. concat охотно обрабатывает свои аргументы, поэтому никогда не возвращает результирующий поток - person Silly Freak; 18.01.2015
comment
flatten работает с Travesables, поэтому вместо l.toStream должно быть достаточно l. Отличный ответ, кстати, он работает и с Iterable.continually! - person Filippo Vitale; 16.03.2015
comment
Я думаю, стоит отметить, что подход Stream.continually(...).flatten приводит к повторяющейся структуре с неограниченным потенциальным потреблением пространства, а не к циклической структуре, которая помещается в постоянное пространство (например, ответ Волкана). - person Seth Tisue; 20.07.2015

Альтернативный метод — рекурсивное объединение .toStream входных данных с самим собой. То есть,

scala> def xs: Stream[Int] = List(1, 2, 3).toStream #::: xs
xs: Stream[Int]

scala> xs.take(10).toList
res1: List[Int] = List(1, 2, 3, 1, 2, 3, 1, 2, 3, 1)
person Volkan Yazıcı    schedule 01.02.2013
comment
Я думаю, что это лучший подход, потому что он генерирует циклическую структуру, которая помещается в постоянное пространство. - person Seth Tisue; 20.07.2015
comment
Небольшая доработка. Чтобы этот трюк компилировался, xs должно быть присваиванием по имени def xs: Stream[Int] = ... - person SimY4; 01.12.2018
comment
То же самое можно сделать, используя LazyList вместо Stream в Scala 2.13+. - person ig-dev; 31.12.2019
comment
Оглядываясь назад, можно сказать, что версия val компилируется в Scala 2.12. Возможно, пример следует обновить, чтобы использовать 2.13 LazyList, что требует использования def. - person László van den Hoek; 12.04.2021
comment
@SethTisue, если вы имеете в виду, что он имеет определенный размер, соответствующий API просит отличаться - person juanchito; 21.06.2021
comment
Он никогда не заканчивается при пересечении, поэтому не имеет определенного размера. Он все еще может поместиться в конечной памяти. - person Seth Tisue; 21.06.2021

Есть простой способ с Stream#flatten в scala 2.8.

Welcome to Scala version 2.8.0.r20542-b20100116020126 (Java HotSpot(TM) Client VM, Java 1.6.0_18).
Type in expressions to have them evaluated.
Type :help for more information.

scala> def cycle[T](seq: Seq[T]) = Stream.from(0).flatten(_ => seq)
cycle: [T](seq: Seq[T])scala.collection.immutable.Stream[T]

scala> cycle(1::2::3::Nil)
res0: scala.collection.immutable.Stream[Int] = Stream(1, ?)

scala> res0.take(10)
res1: scala.collection.immutable.Stream[Int] = Stream(1, ?)

scala> res0.take(10).toList
res2: List[Int] = List(1, 2, 3, 1, 2, 3, 1, 2, 3, 1)
person Eastsun    schedule 20.01.2010
comment
Stream.from(0) просто обеспечивает бесконечный поток объектов — подойдет любой другой бесконечный поток объектов любого типа. Затем вы вызываете flatten(asTraversable), чтобы превратить каждый объект (независимо от того, что это такое) в поток. В этом случае asTraversable превращает объект в исходную последовательность seq. Это интересный способ сделать seq ++ seq ++ seq... - person James Moore; 13.11.2011

Вот реализация, которая не предполагает, что length эффективна:

def rep[A](seq: Seq[A]) = {
  def inner(proj: Seq[A]): Stream[A] = {
    if (proj.isEmpty)
      inner(seq)
    else
      Stream.cons(proj.first, inner(proj drop 1))
  }

  if (seq.isEmpty)
    Stream.empty
  else
    inner(seq)
}

Это должно выполняться за постоянное время для любого Seq (включая List или даже Stream) и требует постоянного времени только для заполнения каждого элемента. Кроме того, он работает даже для бесконечных последовательностей. Итак, вы можете вызвать rep для бесконечного Stream, и результирующий Stream будет эквивалентен вводу.

person Daniel Spiewak    schedule 20.01.2010

Нагло украдено из превосходной книги Scala by Example, глава 12, и с небольшими изменениями:

def repeatedSeq(idx: Int, lst:Seq[Int]): Stream[Int] = Stream.cons(lst(idx), repeatedSeq((idx + 1)%lst.length, lst))

for(i <- repeatedSeq(1,List(1,1,2,3,5))) println(i)

Это работает для всех типов Seq (если, конечно, их нельзя прочитать несколько раз). Может быть неэффективным, если вызов .length медленный. Протестировано в Scala 2.7.7.

person Justin W    schedule 19.01.2010
comment
если, конечно, их нельзя прочитать несколько раз. Но когда вы говорите о последовательностях, вы всегда должны предполагать, что это так. - person James Moore; 13.05.2010