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") && False

以及空列表的执行结果

*Main> testF []
False
*Main> testG []
*** Exception: Prelude.head: empty list
*Main> testH []
False
*Main> testI []
*** Exception: Prelude.head: empty list

关于 testF 和 testG 的问题:我读到 Haskell 评估最左边的最外层,这将保证结果;这确实是语言的要求,还是 testF [] 评估实际上是未定义的行为(例如,在 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 函数中,您可以根据模式匹配切换求值顺序。如果你交换了第二行和第三行(你这样做了),评估的顺序也会交换。

这里绝对没有发生未定义的行为。不要把你的头困在 C 语言中!

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

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

test1 = func1 (error "leftmost") (error "rightmost")
-- Running test1 results in the error "leftmost"

你关于交换律的观点很尖锐。从技术上讲,这确实不允许交换律。然而,在推理 Haskell 代码时,我们通常会忽略底部(对于非数学家来说错误未定义),原因有两个。第一,它让事情变得更好,第二,无论如何你都不应该遇到错误:这意味着你出错了。所以在实践中,不: && 在功能上是可交换的。 错误不应该发生在你的Haskell代码中,并且由于这个原因没有办法捕获它们;正如您所指出的,它破坏了良好的代数性质。仅将它们用于不应发生的事件,如 head 的实现。

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

关于haskell - 函数求值顺序和惰性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49886606/

相关文章:

algorithm - 合并排序比插入排序更快的方式让我困惑

haskell - 如何读取 ghci 类型错误?

haskell - 重载(+)

haskell - 在 NixOs 上安装 Haskell 软件包 Euterpea 失败

haskell - 也许和递归在列表中

haskell - 静态强制两个对象是从同一个 (Int) "seed"创建的

.net - F# Seq 的一个实现问题

haskell - 为什么 Stackage 目前停留在 cabal 1.18 上?

c# - C# 中的重要空格,如 Python 或 Haskell?

haskell - 在类型化模板 Haskell 中使用约束