5

我正在阅读“Scala 中的函数式编程”一书,并且遇到了一个我不完全理解的示例。

在关于严格/懒惰的章节中,作者描述了 Streams 的构造并有这样的代码:

sealed trait Stream[+A]
case object Empty extends Stream[Nothing]
case class Cons[+A](h: () => A, t: () => Stream[A]) extends Stream[A]

object Stream {
    def cons[A](hd: => A, tl: => Stream[A]) : Stream[A] = {
        lazy val head = hd
        lazy val tail = tl
        Cons(() => head, () => tail)
    }
    ...
}

我的问题是在智能构造函数 ( cons) 中,它调用Cons案例类的构造函数。用于传递headand tailvals 的特定语法对我来说没有意义。为什么不像这样调用构造函数:

Cons(head, tail)

head据我了解所使用的语法是强制创建两个仅返回和val的 Function0 对象tail。这与仅传递headtail(不带() =>前缀)有何不同,因为Cons案例类已经定义为按名称获取这些参数?这不是多余的吗?还是我错过了什么?

4

4 回答 4

9

区别在于=> A不等于() => A

前者是按名称传递,后者是一个不带参数并返回 A 的函数。

您可以在 Scala REPL 中对此进行测试。

scala> def test(x: => Int): () => Int = x
<console>:9: error: type mismatch;
 found   : Int
 required: () => Int
       def test(x: => Int): () => Int = x
                                        ^

在我的示例中简单地引用x会导致调用参数。在您的示例中,它正在构造一个延迟调用 x 的方法。

于 2014-10-30T21:19:27.883 回答
9

首先,您假设=> A() => A是相同的。但是,它们不是。例如,=> A只能在按名称传递参数的上下文中使用 - 不可能声明 aval类型=> A。由于case class参数总是vals(除非显式声明vars),所以很明显为什么case class Cons[+A](h: => A, t: => Stream[A])不起作用。

其次,只是将一个按名称的参数包装到一个具有空参数列表的函数中,这与上面的代码所完成的不同:使用lazy vals 可以确保两者hd和最多tl被评估一次。如果代码读取

Cons(() => hd, () => tl)

每次调用对象的方法(字段)时hd都会评估原始值。使用,仅在第一次调用此对象的方法时进行评估,并且在每次后续调用中都返回相同的值。hConslazy valhdhCons

在 REPL 中以精简的方式展示差异:

> def foo = { println("evaluating foo"); "foo" }
> val direct : () => String = () => foo
> direct()
evaluating foo
res6: String = foo
> direct()
evaluating foo
res7: String = foo
> val lzy : () => String = { lazy val v = foo; () => v }
> lzy()
evaluating foo
res8: String = foo
> lzy()
res9: String = foo

请注意第二次调用中的“评估 foo”输出lzy()是如何消失的,而不是第二次调用direct().

于 2014-10-30T21:20:03.327 回答
1

请注意,该方法的参数cons是按名称参数(hdtl)。这意味着如果您调用cons,则在调用之前不会评估参数cons;稍后将在您在内部使用它们时对它们进行评估cons

请注意,Cons构造函数采用两个类型的函数Unit => A,但不是按名称参数。因此,这些将在您调用构造函数之前进行评估。

如果你这样做Cons(head, tail)head并且tail将被评估,这意味着hd并且tl将被评估。

但这里的重点是避免调用hdtl直到有必要(当有人访问htCons对象中)。因此,您将两个匿名函数传递给Cons构造函数;h在有人访问或之前不会调用这些函数t

于 2014-10-30T21:21:59.263 回答
0

def cons[A](hd: => A, tl: => Stream[A]) : Stream[A]

的类型hdAtlStream[A]

而在case class Cons[+A](h: () => A, t: () => Stream[A]) extends Stream[A]

h 是类型Function0[A],t 是类型Function0[Stream[A]]

hd给定is的类型A,智能构造函数将 case 类调用为

 lazy val head = hd
 lazy val tail = tl
 Cons(() => head, () => tail) //it creates a function closure so that head is accessible within Cons for lazy evaluation
于 2020-03-18T13:12:28.297 回答