2011-07-13 157 views
8

我刚刚开始F#所以请善待,如果这是基本的。F#懒惰评估与非懒惰

我读过一个标记为lazy的函数只计算一次,然后缓存。例如:

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

比起这个版本这将每次叫时间实际运行:

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

此基础上,应所有功能进行懒惰?你什么时候不想?这是来自“Beginning F#”一书中的内容。

+0

这是什么版本的F#?我有一个懒惰行事的序列,但并没有公开这样创建。我试图强制它完成。 – octopusgrabbus

回答

13

首先,它可能是有益的注意,所有已定义的东西是一个功能 - eagerFunctheValue是类型的值intlazyFuncLazy<int>类型的值。鉴于

let lazyTwo = lazy (1 + 1) 

let eagerTwo = 1 + 1 

表达1 + 1评估一次以上,无论你有多少次使用eagerTwo。所不同的是1 + 1将被评估恰好一次时限定eagerTwo,但将被评估至多一次时lazyTwo使用(它会在第一时间被评价为Value属性被访问,并然后缓存以便进一步使用Value不需要重新计算它)。如果lazyTwoValue从未访问,那么它的身体1 + 1永远进行评估。

通常情况下,在F#等严格语言中使用惰性值时看不到太多好处。由于访问Value属性需要检查值是否已经计算,因此它们会添加少量开销。如果您有类似let lazyValue = lazy someVeryExpensiveCalculationThatMightNotBeNeeded()的东西,它们可能会为您节省一些计算费用,因为只有在实际使用该值时才会发生昂贵的计算。他们也可以使一些算法终止,否则不会,但这不是F#中的主要问题。例如:

// throws an exception if x = 0.0 
let eagerDivision x = 
    let oneOverX = 1.0/x 
    if x = 0.0 then 
     printfn "Tried to divide by zero" // too late, this line is never reached 
    else 
     printfn "One over x is: %f" oneOverX 

// succeeds even if x = 0.0, since the quotient is lazily evaluated 
let lazyDivision x = 
    let oneOverX = lazy (1.0/x) 
    if x = 0.0 then 
     printfn "Tried to divide by zero" 
    else 
     printfn "One over x is: %f" oneOverX.Value 
+0

不使用'()'暗示它是一个函数吗?无论如何,好点。你是对的,我的例子是非常微不足道的。 – Yuck

+0

@Yuck - 括号在不同的情况下意味着不同的事物。当你像'(1 + 1)'一样使用它们时,它们只是用来表示分组和优先。如果你定义了'let eagerFunc()= ...',那么括号表明'eagerFunc'是一个函数,但是请注意这与你写的不同。 – kvb

6

如果函数执行有副作用,看到的副作用每个函数被调用的时间是很重要的(说包装了一个I/O功能),你不会希望它是懒惰。

也有这么琐碎的是,执行他们每个时间比缓存value--

5

let eagerFunc = (1 + 1)快是让约束力,并且将只执行一次的功能。 let eagerFunc() = (1 + 1)是接受unit(无)并返回int的函数。它会在每次调用时执行。从某种意义上说,每个函数都是懒惰的,也就是说,它只在被调用时执行。然而,lazy关键字(和它返回的System.Lazy)将最多执行一次赋予它的表达式/函数。随后调用Value属性将返回缓存的结果。这在计算价值昂贵时很有用。

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