我有一个输入:
[ 8 `div` 2 + 1 .. ] !! 2 : [ 1 .. 3 ]
输出是:
[7,1,2,3]
但是.. Haskell 首先计算什么?
我不知道优先级,7从哪里来?
我有一个输入:
[ 8 `div` 2 + 1 .. ] !! 2 : [ 1 .. 3 ]
输出是:
[7,1,2,3]
但是.. Haskell 首先计算什么?
我不知道优先级,7从哪里来?
您的问题包含两个问题,所以我会尽力简要回答这两个问题。
给定一个表达式,例如2 + 4 * 3
,Haskell 会2+4
先计算然后将其乘以三,还是会在此4*3
之前计算然后加二?正如您可能已经猜到的那样,4*3
首先。
你怎么知道哪个先出现?您可以查看相关操作员的文档和/或源代码。或者你可以通过实验弄清楚。在你的例子中
[ 8 `div` 2 + 1 .. ] !! 2 : [ 1 .. 3 ]
我从经验中知道,如果你要在任何地方加上括号,它们会是这样的:
([ ((8 `div` 2) + 1) .. ] !! 2) : [ 1 .. 3 ]
但是,为了正确解决这个问题,您需要打开ghci
,然后键入
:info (+)
例如,它会说一些类似的东西
infixl 6 (+)
如果我们也为其他操作员这样做,我们可以为我们构建一个整洁的表。
infixl 7 `div`
infixl 6 +
infixr 5 :
infixl 9 !!
ghci
没有说什么(!!)
,但是我去前奏中的列表操作的源代码,找到了与我在表中显示的内容完全一致的行。然后你可以假设列表有点像括号,所以[]
方括号内的东西在它们之外的东西之前。
现在,infix
声明中操作符名称前的数字表示操作符的优先级有多高——数字越高,它越在其他事物之前。例如,在这种情况下,我们有
infixl 7 `div`
infixl 6 +
这意味着`div`
在 之前+
,实际上,在表达式中
8 `div` 2 + 1
我们发现 Haskell 将结果计算为
(8 `div` 2) + 1
因为`div`
有更高的优先级。如果您对表达式的其余部分执行此操作,您将得到与我在此答案开头所做的相同的括号。
不过你通常不必太在意,因为你写的 Haskell 越多,你就越能感觉到它是如何运作的。大多数情况下,如果你弄错了,你也会得到一个类型错误来提醒你你弄错了。如有疑问,请尝试带括号和不带括号,ghci
看看哪一个给出正确答案。
到目前为止,我已经回答了“Haskell 如何解释表达式?”这个问题。实际计算的顺序是一个完全不同的问题。大多数编程语言首先计算内括号——Haskell 恰恰相反!
给定表达式
([ ((8 `div` 2) + 1) .. ] !! 2) : [ 1 .. 3 ]
Haskell 一开始只会将其视为
<something>
然后当你要求价值时,它会呻吟一点,意识到它在说
<something> : <something>
它会意识到它需要进一步计算才能给你一个值,所以它会把它扩展成
(<something> !! <something>) : [ 1 .. 3 ]
(顺便说一句,这些<something>
s 通常被 Haskell 人称为thunk。)然后它必须更深入,把它变成
([ <something> .. ] !! 2) : [ 1 .. 3 ]
然后它需要此列表的第二个元素,因此它将尽可能多地扩展列表。
([ <something>
, (<something> + 1)
, (<something> + 2) .. ] !! 2) : [ 1 .. 3 ]
然后它可以减少!!
,返回列表的第三个元素(索引为 2),因此整个列表消失并被第三个元素替换。
(<something> + 2) : [ 1 .. 3 ]
然后它可以减少:
,所以结果是一个列表。
[ <something + 2>, 1, 2, 3 ]
至此,它终于要弄清楚那<something>
是什么了,于是又回到了它的定义,把它扩展成
[ (<something> + 1) + 2, 1, 2, 3 ]
接着
[ ((8 `div` 2) + 1) + 2, 1, 2, 3 ]
然后它开始计算第一个元素的实际值,给你
[ (4 + 1) + 2, 1, 2, 3 ]
[ 5 + 2, 1, 2, 3 ]
[ 7, 1, 2, 3 ]
值得一提的是,Haskell 试图不计算任何值,除非它绝对必须这样做。它尽可能地尝试只处理值的描述,而不是任何实际值。它最终会执行所有必要的计算。
如果你不要求列表的第一个值,它永远不会被计算出来。
这就是“惰性评估”的含义。
[ 8 `div` 2 + 1 .. ] !! 2 : [ 1 .. 3 ]
[ 4 + 1 .. ] !! 2 : [ 1 .. 3 ]
[ 5 .. ] !! 2 : [ 1 .. 3 ]
7 : [ 1 .. 3 ]
[7,1,2,3]
函数应用从左到右。
8 `div` 2 ≡ 4
,所以[ 8 `div` 2 + 1 .. ]
意思[ 5 .. ]
。这是一个无限列表[5,6,7,8 ..]
。
list !! n
– 从列表中取出第n个元素(从 0 开始),所以[5 .. ] !! 2 ≡ 7
.