【问题标题】:Scala, repeat a finite list infinitelyScala,无限重复有限列表
【发布时间】:2011-01-07 01:24:08
【问题描述】:

我想在 scala 中使用 Stream 类来无限重复给定的列表。

例如列表 (1,2,3,4,5) 我想创建一个流给我 (1,2,3,4,5,1,2,3,4,5,1, 2,3....)

这样我就可以包装 take 操作。我知道这可以通过其他方式实现,但出于某种原因我想这样做,请幽默:)

所以想法是,通过从某个列表创建这个无限循环,我可以使用 take 操作,当它到达列表的末尾时,它会循环。

如何制作一个简单重复给定列表的流?

【问题讨论】:

    标签: list scala stream infinite


    【解决方案1】:

    另一种方法是将输入的.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)
    

    【讨论】:

    • 我认为这是最好的方法,因为它会生成一个适合恒定空间的循环结构。
    • 小调整。为了让这个技巧编译 xs 应该是一个按名称赋值 def xs: Stream[Int] = ...
    • 在 Scala 2.13+ 中使用 LazyList 代替 Stream 也可以做到这一点
    • 回想起来,val 版本似乎可以在 Scala 2.12 中编译。也许该示例应该更新为使用 2.13 LazyList,这确实需要使用 def
    • @SethTisue,如果你的意思是它有确定的大小,相应的 API 要求不同
    【解决方案2】:

    与@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)
    

    【讨论】:

    • 我最喜欢这个答案,一直在 Stream 伴生对象上确实是我一直在寻找的。还结合 flatten 和 tolist 我得到了我想要的 :) BTW,somestream.join.take(10).toList,有人可以详细说明 object.function() 表示法的需要以及为什么需要它吗?通常你可以有“seq take 10”,像“seq take 10.toList”这样的工作吗?
    • 我认为值得注意的是,“Stream.continually()”显然不存在于 2.8 之前的版本中
    • 在 2.7 中,Stream.continuallyStream.conststreams.flattenStream.concat(streams)
    • 也许你可以移动带有和不带有'.'的方法调用的比较。到一个新问题。
    • 我认为值得注意的是,Stream.continually(...).flatten 方法会导致具有无限潜在空间消耗的重复结构,而不是适合恒定空间的循环结构(如 Volcan 的回答)。
    【解决方案3】:

    在 scala 2.8 中有一个使用 Stream#flatten 的简单方法

    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)
    

    【讨论】:

    • Stream.from(0) 只是为了提供无限的对象流 - 任何其他任何类型的无限对象流都可以工作。然后调用 flatten(asTraversable) 将每个对象(无论它是什么)转换为流。在这种情况下,asTraversable 将对象转换为原始序列 seq。这是 seq ++ seq ++ seq 的一种有趣的方式...
    【解决方案4】:

    这是一个不假设 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)都应该在恒定时间内运行,并且只会施加恒定的时间开销来填充每个元素。此外,它甚至适用于无限序列。因此,您可以在无限的Stream 上调用rep,得到的Stream 将等同于输入。

    【讨论】:

      【解决方案5】:

      从优秀的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 中测试。

      【讨论】:

      • “当然,除非它们不能被多次读取”但是当你谈论序列时,你应该总是假设是这种情况。
      猜你喜欢
      • 2021-05-12
      • 2019-09-09
      • 1970-01-01
      • 2016-01-06
      • 2012-02-13
      • 2011-04-04
      • 1970-01-01
      • 1970-01-01
      • 2018-01-22
      相关资源
      最近更新 更多