Haskell:(+;+;)和(:)运算符的求值顺序

Haskell:(+;+;)和(:)运算符的求值顺序,haskell,functional-programming,Haskell,Functional Programming,我有以下Haskell表达式: 3 : [40] ++ [50] ++ 5 : [60] 我想知道这个表达式是如何计算的。哪个运算符的优先级较高,:或++? 我认为表达式的结果是[3,40,50,5,60],但是我是这样做的: 3 : [40] ++ [50] ++ 5 : [60] 3 : [40] ++ [50] ++ [5,60] 3 : [40] ++ [50,5,60] 3: [40,50,5,60] [3,40,50,5,60] 以上是计算表达式的正确方法吗?任何见解都将不胜感激

我有以下Haskell表达式:

3 : [40] ++ [50] ++ 5 : [60]
我想知道这个表达式是如何计算的。哪个运算符的优先级较高,
++
? 我认为表达式的结果是[3,40,50,5,60],但是我是这样做的:

3 : [40] ++ [50] ++ 5 : [60]
3 : [40] ++ [50] ++ [5,60]
3 : [40] ++ [50,5,60]
3: [40,50,5,60]
[3,40,50,5,60]
以上是计算表达式的正确方法吗?任何见解都将不胜感激

(:)::a->[a]->[a]
函数都以
5
为优先顺序,正如我们在中所读到的,并且是右关联的

我们还可以通过
ghci
外壳中的
:i
获取该数据:

Prelude> :i (:)
data [] a = ... | a : [a]       -- Defined in ‘GHC.Types’
infixr 5 :
Prelude> :i (++)
(++) :: [a] -> [a] -> [a]       -- Defined in ‘GHC.Base’
infixr 5 ++
缩写为:

3 : ([40] ++ ([50] ++ (5 : [60])))
(++)
运算符是:

(:)
是一个数据构造函数,这意味着它不能被“进一步评估”

这是有意义的,因为这意味着这里的
++
仅适用于尾部,因此只要我们对头部感兴趣,就不需要评估该函数。因此,右关联的
(++)
通常比左关联的
(++)
便宜,尽管它会产生相同的列表

因此,如果我们希望评估完整列表,则评估为:

   3 : ([40] ++ ([50] ++ (5 : [60])))
-> 3 : (40 : ([] ++ ([50] ++ (5 : [60]))))
-> 3 : (40 : ([50] ++ (5 : [60])))
-> 3 : (40 : (50 : ([] ++ (5 : [60]))))
-> 3 : (40 : (50 : (5 : [60])))
或者更详细:

   3 : ((40: []) ++ ((50 : []) ++ (5 : (60 : []))))
-> 3 : (40 : ([] ++ ((50 : []) ++ (5 : (60 : [])))))
-> 3 : (40 : ((50 : []) ++ (5 : (60 : []))))
-> 3 : (40 : (50 : ([] ++ (5 : (60 : [])))))
-> 3 : (40 : (50 : (5 : (60 : []))))

@ceno980:不,请注意Haskell是懒惰的,而且
(:)
是一个数据构造函数,因此这意味着无法进一步计算
[60]
只是
60:[]
的语法糖,就像
[1,4,2,5]
(1:4:2:5:[])
的语法糖一样。我不完全理解上面的解释。我说最右边括号中的表达式首先求值,例如(5:[60]),对吗?我也不明白这行:3:(40:([]++([50]++(5:[60]))-为什么要在40后面引入:操作符,并在50之前放置[]++?@ceno980:因为这就是
(++)
的实现方式,请参见编辑。第二条规则触发,因此如果第一个参数是
(50:[])
,则第二个子句触发。“…正如您在Haskell'10报告中所读到的那样…”-除了没有人会这样做。为什么不解释一下
:i
?与严格的语言相比,Haskell的求值顺序经常出现颠倒。表达式的外部通常首先求值:
[50]+([10]+[5])
的外部有
[50]++\u等
,因此它求值为
50:([10]+[5])
,然后外部的
(:)
,因为它是一个数据构造函数,使您有机会停止求值。您可以选择停止,也可以继续深入,并以
50:10:5=[50,10,5]
结束。在类似Python的情况下,它会发生倒退:<代码> [50 ] ++([ 10 ] + + [ 5 ])< /> > > >代码[50 ] +[+] [4] < /> > >代码> [50, 10, 5 ] < /代码>,但这不是Python。一个常见的误解是考虑运算符优先级与评估顺序相同。在
f1+f2*f3
中,优先级要求先执行乘法,再执行加法,但这不会在
f1
f2
f3
之间施加任何求值顺序。在Haskell这样的纯语言中,评估顺序与最终结果基本无关(尽管在某些情况下可能会影响性能)。
   3 : ([40] ++ ([50] ++ (5 : [60])))
-> 3 : (40 : ([] ++ ([50] ++ (5 : [60]))))
-> 3 : (40 : ([50] ++ (5 : [60])))
-> 3 : (40 : (50 : ([] ++ (5 : [60]))))
-> 3 : (40 : (50 : (5 : [60])))
   3 : ((40: []) ++ ((50 : []) ++ (5 : (60 : []))))
-> 3 : (40 : ([] ++ ((50 : []) ++ (5 : (60 : [])))))
-> 3 : (40 : ((50 : []) ++ (5 : (60 : []))))
-> 3 : (40 : (50 : ([] ++ (5 : (60 : [])))))
-> 3 : (40 : (50 : (5 : (60 : []))))