Haskell 功能评估顺序与惰性

Haskell 功能评估顺序与惰性,haskell,Haskell,关于函数的“惰性”和求值顺序的一点思考 考虑以下代码: testF x = ((length x) >= 1) && ((head x) == "foo") testG x = ((head x) == "foo") && ((length x) >= 1) testH x = False && ((head x) == "foo") testI x = ((head x) == "foo") && Fals

关于函数的“惰性”和求值顺序的一点思考

考虑以下代码:

testF x =  ((length x) >= 1) && ((head x) == "foo")

testG x =  ((head x) == "foo") && ((length x) >= 1)

testH x = False && ((head x) == "foo") 

testI x = ((head x) == "foo") && False
以及使用空列表执行的结果

*Main> testF []
False
*Main> testG []
*** Exception: Prelude.head: empty list
*Main> testH []
False
*Main> testI []
*** Exception: Prelude.head: empty list
关于testF和testG的问题:我读到Haskell计算最左边的最外层,这将保证结果;这是否是语言的要求,还是这个测试结果[1]实际上是未定义的行为(例如C++中的情况,直到C++ 11)。 关于testH和testI的问题:乍一看似乎很明显的东西并不那么明显:即使它评估的是最左边最外面的,我们有哪一个担保,而实现没有执行某种反向排序评估:

认为:

myAnd _ False = False
myAnd False _ = False
myAnd a b = (&&) a b

testJ x =  myAnd ((head x) == "foo") False 
testK x =  myAnd False ((head x) == "foo")  
使行为恢复正常

*Main> testJ []
False
*Main> testK []
*** Exception: Prelude.head: empty list

最后,上面所有的例子都使得(&&)操作符在交换性定律(a&&b==b&&a)方面失败,因此我最终认为,理想情况下,所有这些调用都应该计算两个参数并引发一个异常

Haskell并不是在所有情况下都首先评估最左端。正如你所看到的,这是由于你的定义。以
&&
的定义为例:

(&&) :: Bool -> Bool -> Bool
(&&) False _ = False
(&&) _ False = False
(&&) _ _     = True
请注意,它首先需要来自左参数的模式。这将强制首先计算左参数,并可能使第二个参数未计算。在
myAnd
函数中,根据模式匹配切换求值顺序。如果您交换了第2行和第3行(您这样做了),则评估顺序也将交换

这里绝对没有未定义的行为。不要让你的头卡在C里

但是,如果函数模式在两个(或更多)参数上匹配,那么它会首先计算最左边的参数。例如:

func1 True False = True -- Both arguments evaluated
func1 _ _        = False

test1 = func1 (error "leftmost") (error "rightmost")
-- Running test1 results in the error "leftmost"
你关于交换性的观点很尖锐。从技术上讲,这确实不允许交换性定律。然而,在对Haskell代码进行推理时,我们通常忽略底部(
error
undefined
给非数学家),原因有二。第一,它使事情变得更好,第二,无论如何,你不应该遇到错误:这意味着你错了。所以在实践中,no:
和&
在功能上是可交换的<代码>错误s不应该发生在Haskell代码中,因此无法捕获它们;它打破了你指出的好的代数性质。仅对不应发生的事件使用它们,如在
head
的实现中


还有许多其他类似于Haskell的语言,特别是Agda和Idris,它们完全出于这个原因而消除了
错误
未定义
,主要是因为它们有一个特定的逻辑一致性目标,以便能够从字面上证明您的代码是正确的。然而,Haskell(可能是为了保持更为主流)并没有这样做。

这种行为是有保证的

哈斯克尔2010年《序曲》第9节中的
|
&

-- Boolean functions
(&&), (||) :: Bool -> Bool -> Bool
True && x = x
False && _ = False
True || _ = True
False || x = x
模式匹配也是(3.17.2)与u匹配⊥:

图案 根据以下规则,从左到右、从外到内进行匹配:[……]将通配符模式u与任何值匹配始终成功,并且不进行任何绑定


在旧的Haskell 98规范中也是如此。

顺便说一句,
myAnd
函数的第三行可以(也应该)是
myAnd\uuu=true
而不是
myAnd a b=(&&&&)a b
。在
tesK
中,是“最左边的”是您的函数
myAnd
哪个模式首先与第二个参数匹配。这迫使对该术语的评估检查它是否为假,从而导致您的程序死亡。另外,您的问题到底是什么?你说了很多事情,但我不是100%确定你想要回答的确切问题是什么。只是一个侧面注释,<代码>和& <代码>在C++中总是被保证懒惰;code>testF将被完美定义。关于:交换性,请检查。特别是我们有
pand-False-undefined=pand-undefined-False=False
pand-True-undefined=pand-undefined-True=undefined