1

仅出于测试目的,我想懒惰地计算 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)但懒惰地评估参数?

谢谢

4

2 回答 2

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: _*)
  }
}

它可以在语法开销很小的情况下使用(您只需l在 vararg-list 中传递的每个块前添加一个字符):

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

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

摆脱lor{ () => ... }似乎是不可能的,因为不支持重复的按名称参数


编辑

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

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

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

于 2018-04-07T12:47:15.080 回答
1

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

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

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

makeStream(
  { () => Thread.sleep(2000); 1 },
  { () => Thread.sleep(2000); 2 }
).foreach(println)
于 2018-04-07T12:38:26.393 回答