F#懒惰评价与非懒惰评价

时间:2011-07-13 18:35:33

标签: f# lazy-evaluation

我刚刚开始F#所以如果这是基本的话,请善待。

我已经读过标记为lazy的函数只被评估一次然后被缓存。例如:

let lazyFunc = lazy (1 + 1)
let theValue = Lazy.force lazyFunc

与每次调用时实际运行的版本相比:

let eagerFunc = (1 + 1)
let theValue = eagerFunc

基于此,所有函数应该变得懒惰吗?你什么时候不想?这来自书中的材料“Beginning F#”

3 个答案:

答案 0 :(得分:15)

首先,注意到您定义的所有内容都不是函数可能会有所帮助 - eagerFunctheValue是类型int和{{1}的值}是类型lazyFunc的值。给定

Lazy<int>

let lazyTwo = lazy (1 + 1)

无论您使用let eagerTwo = 1 + 1 多少次,表达式1 + 1都会多次评估。不同之处在于,定义 eagerTwo时,1 + 1将被完全评估一次,但最多只会评估 eagerTwo 使用时(将在第一次访问lazyTwo属性时对其进行评估,然后进行缓存,以便进一步使用Value不需要重新计算它)。如果永远不会访问Value的{​​{1}},则其正文lazyTwo 永远不会进行评估。

通常情况下,在F#等严格语言中使用延迟值并不会带来太多好处。它们会增加少量开销,因为访问Value属性需要检查该值是否已经计算过。如果您有类似1 + 1的内容,它们可能会为您节省一些计算,因为只有在实际使用该值时才会进行昂贵的计算。它们也可以使一些算法终止,否则不会,但这不是F#中的主要问题。例如:

Value

答案 1 :(得分:6)

如果函数执行有副作用,每次调用函数时都会看到副作用很重要(比如它包装了I / O函数),你不会希望它是懒惰的。

还有一些功能非常简单,每次执行它们比缓存值更快 -

答案 2 :(得分:5)

let eagerFunc = (1 + 1)是一个let绑定,只执行一次。 let eagerFunc() = (1 + 1)是一个接受unit(无)并返回int的函数。它会在每次调用时执行。从某种意义上说,每个函数都是惰性的,也就是说,它只在被调用时执行。但是,lazy关键字(和它返回的System.Lazy)将执行最多一次给它的表达式/函数。对Value属性的后续调用将返回缓存的结果。当计算值很昂贵时,这很有用。

许多函数不适合与lazy一起使用,因为它们要么是非确定性的(可能在每次调用时返回不同的结果),要么参数化。当然,可以使用这些函数的完全应用(为每个参数提供一个值)版本,但通常需要可变性。