严格vs非

我有一个关于严格与非严格的定义的问题。 Haskell Laziness的wiki书(http://en.wikibooks.org/wiki/Haskell/Laziness)在“黑盒子严格性分析”一节的下面提出了以下断言:

[假设函数f只有一个参数。]函数f是一个严格的函数,当且仅当f未定义时会导致错误被打印并暂停程序。

wiki将constid对比,分别显示非严格和严格的函数。

我的问题是,我的印象是,foldl是以非严格的方式进行评估,造成不良的空间泄漏,而foldl'则是严格的。

然而,上述测试似乎断言foldl和foldl'都是严格的。 如果任何参数未定义,那么这两个函数都会产生未定义的值:

> Data.List.foldl (+) undefined [1,2,3,4]
    Prelude.undefined
> Data.List.foldl' (+) 0 undefined
    Prelude.undefined
> Data.List.foldl' (+) undefined [1,2,3,4]
    Prelude.undefined
> Data.List.foldl (+) 0 undefined
    Prelude.undefined

有人能解释我错过了什么吗?

谢谢!


更好的定义是:如果在未定义该参数的情况下未定义函数,则该函数在参数中被认为是严格的。

让我们看看foldl的以下定义:

 foldl f s [] = s
 foldl f s (x:xs) = foldl f (f s x) xs

由此可推导出以下内容:

  • f它并不严格,因为在第一个方程中f s的值是无关紧要的。
  • 它不是严格s两种,由于该表可以是非空和f可能是不严格的在它的第一个参数(想flip const )。
  • 然而,在列表论证中这是严格的,因为无论fs是什么,这个论证都必须被评估为所谓的弱头范式。 如果可以告诉最外层的构造函数,那么代数数据类型的值在WHNF中。 换言之,foldl无法避免查看列表参数是否为空列表。 因此,它至少必须对列表值进行评估。 但是如果这个值是未定义的,那么这两个方程都不适用,因此这个foldl应用本身是不确定的。
  • 此外,从事实foldl是不是在累加器严格s我们也可以了解为什么它在许多情况下,一个坏主意,使用foldl :系统认为没有理由居然评价术语fsx ,因为它传递给函数这在相应的论点中并不严格。 事实上,如果它评估了它,那将违反非严格性原则。 因此,根据列表的长度,累加器中会累积一个巨大的thunk:

    ((((s `f` x_1) `f` x_2) `f` x_3) ....) `f` x_n)
    

    现在,如果f本身在左边的参数中是严格的(+)比如(+) ,那么任何试图根据需要评估foldl的结果都需要堆栈空间与列表的长度成正比。 对于f ... x_n评估,其中...代表左手侧必须首先评估左手侧,依此类推,直到fs x_1并返回。

    因此,我们有这个

    foldl (flip const) 0 [1..n]
    

    n ,而

    foldl const 0 [1..n]
    

    将堆栈溢出足够大n 。 这是一个可靠的指标,在​​这里人类比计算机更擅长计算,因为在第二个例子中,列表的长度和内容完全不相关,大多数人会立即看到结果必须是0。


    你引用的wiki部分假设你正在处理一个只有一个参数的函数。 foldl的情况不同,首先是因为它需要多个参数,但更重要的是因为其中一个参数是一个函数。 让我们来看看几个例子,看看foldl在参数中的确切时间。 (请注意,以下内容也适用于foldl' 。)

    > foldl undefined 0 []
    0
    > foldl undefined 0 [1]
    *** Exception: Prelude.undefined
    

    对于空列表, foldl不需要传递给它的组合函数。 在这种情况下,第一个参数并不严格。

    > foldl (+) undefined [1, 2, 3]
    *** Exception: Prelude.undefined
    > foldl (+) 0 undefined
    *** Exception: Prelude.undefined
    

    当你作为组合函数传入(+) ,它在起始值和列表中是严格的。 这是另一个例子,具有不同的组合功能:

    > foldl (flip (:)) undefined [1, 2, 3]
    [3,2,1*** Exception: Prelude.undefined
    

    有趣。 起始值是未定义的,但似乎在抛出异常之前调用foldl产生了一些结果。 那这个呢:

    > let (x:xs) = foldl (flip (:)) undefined [1, 2, 3]
    > x
    3
    

    那里没有例外。 所以我们可以在没有碰到可怕的Prelude.undefined情况下得到foldl的部分结果。 为什么?

    那么,唯一改变的是我们传递给foldl的组合函数。 (+)具有类型(大致) Int -> Int -> Intflip (:)具有类型[a] -> a -> [a] 。 而3 + ((2 + 1) + undefined)不是弱头标准形式,并且必须降低以使得undefined被评估, (3:(2:(1:undefined)))这不需要进一步评估,并且特别不需要评估undefined

    链接地址: http://www.djcxy.com/p/42993.html

    上一篇: strict vs non

    下一篇: What are Haskell's strictness points?