在Scala中懒洋洋地用几个元素创建一个Stream

时间:2018-04-07 12:18:37

标签: scala lazy-evaluation scala-streams

出于测试目的,我想计算懒惰的2个元素:

  Stream(
    { Thread.sleep(2000); 1 },
    { Thread.sleep(2000); 2 },
    Stream.empty[Int]
  ).foreach(println)

但是运行此代码不会产生预期的结果。值似乎在输出中同时显示所有值。原因是Stream()构造函数正在采用必须急切计算的数组。所以要解决这个问题,我不得不采用手动创建流的thunk:

  (
    { Thread.sleep(2000); 1 } #::
    { Thread.sleep(2000); 2 } #::
    Stream.empty[Int]
  ).foreach(println)

现在完全按照预期工作,但不是特别漂亮。

是否有一种更清晰,更方便的方法,可以在语法上与Stream(a, b, c)类似,但是懒惰地评估这些参数?

由于

2 个答案:

答案 0 :(得分:2)

如果你创建一个特殊的类,它可以保存生成A的名字块,以及一个帮助实例化这个类的简短助手方法:

class ByNameArg[+A](a: => A) {
  def unpack: A = a
}

/** Single lazy by-name argument wrapper */
def l[A](a: => A) = new ByNameArg(a)

然后您可以定义以下工厂方法:

def lazyStreamInit[A](xs: ByNameArg[A]*): Stream[A] = {
  if (xs.isEmpty) {
    Stream.empty[A]
  } else {
    xs.head.unpack #:: lazyStreamInit(xs.tail: _*)
  }
}

它可以用很少的语法开销(你只需要在vararg-list中传递的每个块前加一个字符l):

lazyStreamInit(
  l{ Thread.sleep(2000); 1 },
  l{ Thread.sleep(2000); 2 }
)

与通常的() => A - 解决方法相比,这会直接产生Stream[A],而不是Stream[() => A]

摆脱l{ () => ... }似乎不可能,因为repeated by-name arguments are not supported

修改

通过“通常() => A - 解决方法”我的意思是

Stream(
  { () => Thread.sleep(2000); 1 },
  { () => Thread.sleep(2000); 2 }
).map{ _() }.foreach(println)

请注意,您必须附加一个map步骤才能使其成为Stream[A]

答案 1 :(得分:1)

这将需要重复的名称参数,这些参数目前不受支持(http://docs.scala-lang.org/sips/repeated-byname.htmlhttps://github.com/scala/bug/issues/5787)。不过,它们可用in Dotty

可以使用重复的() => A,但是你需要写

def makeStream[A](xs: (() => A)*) = ...

makeStream(
  { () => Thread.sleep(2000); 1 },
  { () => Thread.sleep(2000); 2 }
).foreach(println)