哈顿在 Haskell 中编程
一般来说,如果
#
是一个操作符,那么形式为(#)
,(x #)
和(# y)
for 参数x
的 表达式y
称为节,其作为函数的含义可以使用 lambda 表达式形式化,如下所示:(#) = \x -> (\y -> x # y) (x #) = \y -> x # y (# y) = \x -> x # y
“section”和“currying”有什么区别和关系?
部分是对多参数函数应用柯里化操作的结果吗?
谢谢。
哈顿在 Haskell 中编程
一般来说,如果
#
是一个操作符,那么形式为(#)
,(x #)
和(# y)
for 参数x
的 表达式y
称为节,其作为函数的含义可以使用 lambda 表达式形式化,如下所示:(#) = \x -> (\y -> x # y) (x #) = \y -> x # y (# y) = \x -> x # y
“section”和“currying”有什么区别和关系?
部分是对多参数函数应用柯里化操作的结果吗?
谢谢。
节只是将中缀运算符应用于单个参数的特殊语法。(# y)
是两者中更有用的一个,因为(x #)
它等效于(#) x
(只是以通常的方式将中缀运算符作为函数应用于单个参数)。
curry f x y = f (x,y)
. uncurry g (x,y) = g x y
.
(+ 3) 4 = (+) 4 3 = 4 + 3
. (4 +) 3 = (+) 4 3 = 4 + 3
.
部分是部分应用柯里化函数的结果: (+ 3) = flip (+) 3
, (4 +) = (+) 4
.
一个柯里化函数(如g
or (+)
)一次只需要一个参数。一个未柯里化的函数(如f
)期望它的参数在一个元组中。
要部分应用非柯里化函数,我们必须首先将其转换为柯里化函数,使用curry
. 要部分应用柯里化函数,我们不需要做任何事情,只需将其应用于参数即可。
curry :: ((a, b) -> c ) -> ( a -> (b -> c))
uncurry :: (a -> (b -> c)) -> ((a, b) -> c )
x :: a
g :: a -> (b -> c)
--------------------
g x :: b -> c
x :: a
f :: (a, b) -> c
---------------------------
curry f :: a -> (b -> c)
curry f x :: b -> c
左侧部分和右侧部分是用于将中缀运算符部分应用于单个参数的语法设备(另请参见chepner 的回答)。为了准确起见,我们应该注意,柯里化与部分应用不是一回事:
柯里化是将一个接受N个参数的函数转换为一个接受单个参数并返回一个接受N-1 个参数的函数的函数。
部分应用程序正在创建一个函数,该函数通过提供一个参数从一个接受 N 个参数的函数中取出N -1个参数。
在 Haskell 中,所有东西都是咖喱的。所有函数都只接受一个参数(即使 Haskell 中的非柯里化函数也接受一个元组,严格来说,它是一个参数——您可能想使用curry
anduncurry
函数来看看它是如何工作的)。尽管如此,我们仍然经常非正式地认为将函数返回为多个参数的函数。从这个有利的角度来看,默认柯里化的一个很好的结果是,将函数部分应用到它的第一个参数变得微不足道:例如,elem
接受一个值和一个容器并测试该值是否是容器的一个元素,elem "apple"
接受一个容器(字符串)并测试是否"apple"
是它的一个元素。
至于运算符,例如,当我们编写时...
5 / 2
...我们将运算符/
应用于参数5
和2
。运算符也可以以前缀形式使用,而不是中缀:
(/) 5 2
在前缀形式中,运算符可以以通常的方式部分应用:
(/) 5
然而,这可能看起来有点尴尬——毕竟,5
这里是分子,而不是分母。在这种情况下,我会说左部分语法更容易看:
(5 /)
此外,对第二个参数的部分应用并不那么简单,需要一个 lambda 或flip
. 在操作员的情况下,正确的部分可以帮助解决这个问题:
(/ 2)
请注意,部分也适用于通过反引号语法制成运算符的函数,所以这...
(`elem` ["apple", "grape", "orange"])
... 接受一个字符串并测试它是否可以在["apple", "grape", "orange"]
.