Clojure迭代在Scala中等效吗?

clojure iterate equivalent in scala?

递归很酷,但是当您被高阶库函数包围时,递归有点低。我试图避免为依赖于最后生成的值的过程编写递归函数。

我通常会在Clojure中对最后一个值和当前参数的"压缩"列表使用iterate函数。 Scala的collection API中有等效的功能吗?

这里是一些疯狂的伪代码中的一个抽象示例的尝试:

说你有

  • 输入列表:Seq(1,2,3)
  • 您对生成的最后一个值和列表中的下一项执行的某些操作:

    lastValue ^ 2 + nextInt(i)

,您想累积所有生成的值。

我正在尝试避免编写类似于以下内容的内容:

1
2
3
4
5
6
7
8
9
10
11
12
def f(ls:Seq[Int]):Seq[Float] = {

  def g(pos:Int, lastGen:Float):Seq[Float] = {
    val v = gen(lastGen, ls(pos))
    if( end(v) )
      Seq(v)
    else
      Seq(v) ++ g(pos+1, v)
  }

  f(0, 1)
}

我在Haskell中定义了Fibonacci的懒惰流版本时看到了类似的东西,因此,假设我可以使用自己引用的懒惰流,但是比Clojure的迭代更难缠我的脑筋了。


这是您要找的吗?与Clojure中的iterate基本相同:

1
2
List.iterate(1, 5) { _ + 1 }
// res1: List[Int] = List(1, 2, 3, 4, 5)

我认为Listiterate定义来自GenTraversableFactory

唯一的缺点是第二个参数len是所需的参数数量,因此它不会像Clojure中的iterate那样返回无限序列。

更新:

刚学到新东西! Stream对象也具有iterate方法,这使您可以创建无限的惰性流:

1
2
(Stream.iterate(1) { _ * 2 } take 5).toList
// res1: List[Int] = List(1, 2, 4, 8, 16)


您显示的代码基本上等同于以下内容:

1
ls.foldLeft(List(1.0))((a, b) => gen(a.head, b) :: a).reverse

这听起来像您想要的高阶函数是scanLeft,就像一个记住其中间步骤的折叠。例如,假设您具有以下内容:

1
2
val ls = Seq(1, 2, 3)
def gen(lastValue: Double, n: Int) = math.pow(lastValue, 2) + n

然后您可以将它们与scanLeft组合在一起:

1
2
scala> ls.scanLeft(1.0)(gen)
res0: Seq[Double] = List(1.0, 2.0, 6.0, 39.0)

或多或少与Apocalisp的foldLeft等效,除了scanLeft会为您保持中间值。