我在教科书上看到了这段代码:
double :: (Num a) => a -> a
double x = x * 2
map (double.double) [1,2,3,4]
我没有得到的是,如果功能组合操作具有最高优先级,为什么要使用括号来包含double.double?如果我删除这些括号,我会收到错误消息。那么功能组合的优先级究竟是什么呢?
所有内置运算符各自的优先级和固定性都可以在Haskell Report section 4.4.2中找到。特别是,.
和!!
优先9,这是运营商中最高的。但是,功能应用程序不是运营商。功能应用程序专门设计为优先于任何运算符,因此
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]
,这不太可能成功(虽然从技术上讲不可能)。
优先级(和关联性)是解决表达式中多个中缀运算符之间的歧义的方法。如果操作数旁边有两个运算符,则precedence(和associativity)会告诉您哪个操作数作为参数,哪个操作符将另一个apply-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。
无论您选择何种方式来解释它,都会产生一个简单的结果。除非操作符应用程序周围有括号,否则任何应用的运算符表达式都不可能作为非运算符函数应用程序中的参数传递。如果在任何括号外的表达式中有运算符,则表达式的最外层始终是运算符应用程序。
1这种邻接运算符的解释似乎很常见。我个人认为对于如何解析表达式是一个很差的解释,因为你需要部分解析表达式以知道插入邻接运算符的位置。
它通常被称为“空白操作符”,这更令人困惑,因为并非每个空格都代表这个操作符,并且你并不总是需要空格来存在它。例如length"four" + 1