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
。