Haskell 为什么评估会停止?
我有以下代码:Haskell 为什么评估会停止?,haskell,Haskell,我有以下代码: foldr const 0 ([1] ++ undefined) 我得到的结果是1 我知道,foldr是正确关联的。写出上面的例子: const 1 (const undefined 0) 为什么括号中的表达式不作为第一个表达式计算 因为函数应用程序的优先级高于括号 为什么括号中的表达式不作为第一个表达式计算 Haskell使用惰性评估。这意味着当函数使用参数时,将对参数进行求值。这与在调用函数之前执行函数的所有参数的急切求值不同 因为函数应用程序的优先级高于括号 优先权与此
foldr const 0 ([1] ++ undefined)
我得到的结果是1
我知道,foldr
是正确关联的。写出上面的例子:
const 1 (const undefined 0)
为什么括号中的表达式不作为第一个表达式计算
因为函数应用程序的优先级高于括号
为什么括号中的表达式不作为第一个表达式计算
Haskell使用惰性评估。这意味着当函数使用参数时,将对参数进行求值。这与在调用函数之前执行函数的所有参数的急切求值不同
因为函数应用程序的优先级高于括号
优先权与此无关。优先级只是决定了操作数对什么(即“x???y!!!z”是运算符
?
对操作数x
和y!!!z
还是运算符!!!
对x???y
和z
的应用)。它不影响评估顺序。我相信您期望的是所谓的“应用评估顺序”。换句话说,在f(gx)
中,您希望首先计算gx
,然后对结果调用f
在您的示例中,这意味着评估将从评估0
、[1]
和未定义的
开始,我相信您预计会在这三个方面发生崩溃
Haskell有所谓的“按需调用”或“惰性评估”。在f(gx)
中,首先计算f
,将其作为参数提供一个程序(通常称为“thunk”)来计算gx
。这意味着,如果f
从不需要gx
的值,则不会对其进行计算
下面是如何评估您的示例(我将foldr
的定义放在下面以供参考):
下面是foldr
和(++)
的定义:
要正确计算
常量1(const undefined 0)
,我们需要了解常量的定义、GHC使用的计算策略,以及(似乎)使用多个参数的函数中的curry行为。(不一定按那个顺序。)
const的定义
const的定义是
const c _ = c
咖喱
似乎包含多个参数的函数实际上是curry:
我们可以使用eta抽象(fx=y
变成f=\x->y
)显式curryconst
),将其转换为一个函数,该函数接受一个参数并给出另一个函数:
const c _ = c
= { eta abstraction }
const c = \_ -> c
将这两个概念结合起来意味着每当我们看到const x y
,我们都可以用(\\\ux->y
)替换它:
const x y
= { currying }
(const x) y
= { const c = \_ -> c }
(\_ -> x) y
最外层评估和短路
Haskell*中的计算从最外层的可约表达式(redex)开始,并由模式匹配触发。这意味着在f(gx)
中,f
在gx
之前进行评估(它是最外层的)<然后,仅当f
的评估需要时,才会评估code>gx
。然后,反过来,x
仅在g
的评估需要时才进行评估
最外层的第一次求值的属性之一是*短路(:如果外部表达式的求值不需要内部表达式的结果,则永远不会对内部表达式求值。特别是,这意味着(\ \ ux->x)y=x
无论y
是什么,因为\\ux
不要求对y
求值
计算常数1(常数未定义0)
把这些放在一起,我们可以计算const 1(const undefined 0)
一旦你理解了这一点,就很容易跳过咖喱,只需写下:
const 1 (const undefined 0)
= { definition of const }
1
因为const c_=c
,c=1
,我们知道第二个参数永远不会被计算
(您还询问了优先级,但优先级与计算常量1(常量未定义0)
无关)
*严格地说**,Haskell没有定义求值顺序。它指定求值必须是非严格的,并将此语义的实现留给Haskell实现。GHC使用惰性求值,这是与共享相结合的最外层第一求值(也就是说,
x
将在中最多计算一次,让x*x中的x=1+1
)
**是的,这是一个双关语。注意求值顺序和优先级是两个不同的概念,经常被错误地识别。即使在命令式语言中,
f()+g()*h()
也可以通过先调用g()
,然后调用f()
,然后调用h()
(或任何其他顺序)来求值,然后执行乘法,最后是加法。你说的“上面”是指sepp2k的答案吗?不要假设一页上的答案有任何特定的顺序。
const c _ = c
= { eta abstraction }
const c = \_ -> c
const x y
= { currying }
(const x) y
= { const c = \_ -> c }
(\_ -> x) y
const 1 (const undefined 0)
= { const x y = (\_ -> x) y }
(\_ -> 1) (const undefined 0)
= { (\_ -> x) y = x }
1
const 1 (const undefined 0)
= { definition of const }
1