关于“懒惰”和评价函数的顺序的一些思考
考虑以下代码:
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)中失败,所以我最终认为所有这些调用,理想情况下,应评估两个参数并引发异常。
答案 0 :(得分:11)
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
到非数学家)有两个原因。一,它使事情更好,两个,你不应该遇到error
:这意味着你出错了。所以在实践中,不:&&
在功能上是可交换的。不应该在你的Haskell代码中发生error
,并且由于这个原因无法捕获它们;正如你所指出的,它打破了很好的代数性质。仅将其用于不应发生的事件,如head
的实现。
还有许多类似于Haskell的其他语言,尤其是Agda和Idris,由于这个原因,它们完全取消error
和undefined
,主要是因为它们具有逻辑一致性的特定目标,为了能够完全证明您的代码是正确的。然而,Haskell(可能是为了保持更主流)并不这样做。
答案 1 :(得分:9)
这种行为是有保障的。
Haskell 2010第9节中的前奏are defined to behave like this中的 public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath ) // This line is wrong, but the correct path would be set here
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
string myString = Configuration["blah:blah:blah"];
和||
:
&&
模式匹配也是defined to work left-to-right(3.17.2),_匹配⊥:
模式 根据以下规则,匹配从左到右,从外到内进行匹配:[...]将通配符模式_与任何值匹配总是成功,并且不进行绑定。
旧的Haskell 98规范也是如此。