【问题标题】:A variable used in its own definition?在自己的定义中使用的变量?
【发布时间】:2015-11-05 03:44:05
【问题描述】:

无限流:

val ones: Stream[Int] = Stream.cons(1, ones)

如何在自己的声明中使用值?看起来这应该会产生编译器错误,但它确实有效。

【问题讨论】:

  • 递归函数的工作方式相同。认为ones 是一个零参数函数。

标签: scala functional-programming lazy-evaluation delayed-execution


【解决方案1】:

它并不总是递归定义。这实际上有效并产生 1:

val a : Int = a + 1
println(a)

变量a 是在您键入val a: Int 时创建的,因此您可以在定义中使用它。 Int 默认初始化为 0。一个类将为空。

正如@Chris 所指出的,Stream 接受=> Stream[A],因此应用了一些其他规则,但我想解释一般情况。这个想法仍然是一样的,但是变量是按名称传递的,所以这使得计算递归。鉴于它是按名称传递的,它是惰性执行的。 Stream 一个接一个地计算每个元素,因此它每次需要下一个元素时都会调用ones,从而再次生成相同的元素。这有效:

val ones: Stream[Int] = Stream.cons(1, ones)
println((ones take 10).toList) // List(1, 1, 1, 1, 1, 1, 1, 1, 1, 1)

尽管您可以使无限流更容易:Stream.continually(1) 更新正如 @SethTisue 在 cmets 中指出的那样,Stream.continuallyStream.cons 是两种完全不同的方法,结果非常不同,因为 cons 采用 @ 987654334@ 当continually=>A 时,这意味着continually 每次重新计算元素并将其存储在内存中,当cons 可以避免存储它n 次,除非您将其转换为其他结构如@987654339 @。仅当您需要生成不同的值时才应使用continually。有关详细信息和示例,请参阅@SethTisue 评论。

但请注意,您需要指定类型,与递归函数相同。

你可以让第一个例子递归:

lazy val b: Int = b + 1
println(b)

这将堆栈溢出。

【讨论】:

  • 请注意,Stream.cons(1, ones) 是使用有限内存的循环结构,Stream.continually(1) 是使用潜在无限内存的线性结构。
  • @SethTisue 你能解释一下吗?有什么实际区别吗?不知道你是什么意思。如果你这样做 Stream.continually(1).take(10000000) 它仍然不会分配所有这些数字
  • 仅使用几个内存字的内容与可能消耗整个堆的内容之间存在巨大的实际差异。 (但是请注意,一旦您调用take,无论哪种方式,您最终都会得到相同的结构,所以它是否重要取决于您对流的实际操作。)进一步阅读:gist.github.com/SethTisue/ce598578874accba98c0groups.google.com/d/msg/scala-user/3yypUKJBP04/Q_bowgIry44J
  • @SethTisue 哇!我没有意识到cons 需要A,而continually 需要=>A。我同意这使这两种方法完全不同。
【解决方案2】:

Stream.cons.apply的签名:

apply[A](hd: A, tl: ⇒ Stream[A]): Cons[A]

第二个参数上的 表示它具有按名称调用的语义。因此,您的表达式 Stream.cons(1, ones) 没有被严格评估;在作为tl 的参数传递之前,不需要计算参数ones

【讨论】:

    【解决方案3】:

    这不会产生编译器错误的原因是因为Stream.consCons 都是non-strictlazily evaluate 它们的第二个参数。

    ones 可以在它自己的定义中使用,因为对象 cons 有一个 apply 方法定义如下:

    /** A stream consisting of a given first element and remaining elements
     *  @param hd   The first element of the result stream
     *  @param tl   The remaining elements of the result stream
     */
    def apply[A](hd: A, tl: => Stream[A]) = new Cons(hd, tl)
    

    Cons 是这样定义的:

    final class Cons[+A](hd: A, tl: => Stream[A]) extends Stream[A]
    

    请注意,它的第二个参数tl 是按名称(=> Stream[A])而不是按值传递的。换句话说,参数tl 在函数中使用之前不会被计算。

    使用这种技术的一个优点是您可以编写可能只进行部分计算的复杂表达式

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-19
      • 2021-01-07
      相关资源
      最近更新 更多