我在课本上看到了这段代码:
double :: (Num a) => a -> a
double x = x * 2
map (double.double) [1,2,3,4]
我没有得到的是,如果功能组合运算具有最高优先级,为什么要使用括号来包含double.double?如果删除这些括号,则会收到错误消息。那么功能组合的优先级到底是什么?
答案 0 :(得分:9)
所有内置运算符各自的优先级和固定性可以在Haskell Report section 4.4.2中找到。特别是.
和!!
的优先级为9,这是运算符中最高的。但是,功能应用程序不是运算符。功能应用程序经过专门设计,具有比 any 运算符更高的优先级,因此
map (double.double) [1,2,3,4]
这会将功能double . double
应用于列表[1, 2, 3, 4]
的每个元素
map double.double [1,2,3,4]
这是试图组成功能map double
和double [1, 2, 3, 4]
的功能,虽然不太可能成功(尽管从技术上讲并非不可能),但这是不可能的。
答案 1 :(得分:2)
优先级(和关联性)是解决表达式中多个中缀运算符之间歧义的方法。如果操作数旁边有两个运算符,则优先级(和关联性)会告诉您其中哪个将操作数作为参数,而哪个将另一个Applied-operator表达式作为参数。例如,在表达式1 + 2 * 3
中,2
紧挨+
和*
。 *
的较高优先级意味着*
将2
作为其左引数,而+
将整个2 * 3
子表达式作为其右引数。
但是map double.double [1, 2, 3, 4]
中不是这种情况。仅有一个运算符,两侧有两个操作数,因此毫无疑问优先为我们提供答案。这两个操作数是map double
和double [1, 2, 3, 4]
;操作数是一个或多个项的序列,而不仅仅是紧接的左项和右项。如果有多个术语,则将该序列解析为简单的链式函数应用程序(即a b c d
是((a b) c) d
)。
另一种思考方式是,存在一个“邻接运算符”,其优先级高于可以分配给任何其他运算符的优先级,并且不可见地出现在任何两个非运算符项之间(但在其他任何地方都没有)。用这种思维方式,map double.double [1, 2, 3, 4]
实际上类似于map @ double . double @ [1, 2, 3, 4]
(在这里我将这个“邻接运算符”写为@
)。由于@
的优先级高于.
,因此优先级为(map @ double) . (double @ [1, 2, 3, 4])
。 1
无论选择哪种解释方式,都有一个简单的结果。除非在运算符应用程序周围有括号,否则 any 应用的运算符表达式根本不可能在非运算符函数应用程序中作为参数传递。如果表达式中任何括号之外都有运算符,则表达式的最外层始终是运算符应用程序。
1 这种邻接运算符的解释似乎很常见。我个人认为这对如何解析表达式的解释不佳,因为您需要部分解析一个表达式才能知道在何处插入邻接运算符。
它通常被称为“空白运算符”,因为不是每个空格都代表该运算符,而且您并不总是需要空格才能使它出现,所以它更加令人困惑。例如length"four" + 1