我一直在阅读很多关于currying的文章,但几乎所有这些都是误导性的,将currying解释为部分函数应用程序,并且所有示例通常都是关于arity为2的函数,如add
功能什么的。
JavaScript中的curry
函数的许多实现使得每个部分应用程序接受多于1个参数(参见lodash),当Wikipedia article清楚地告知currying是关于:
将带有多个参数(或参数元组)的函数的求值转换为评估函数序列,每个函数都有一个参数(部分应用程序)
所以基本上currying是一系列部分应用程序,每个应用程序都有一个参数。我真的想知道用任何语言对它的真实用法。
答案 0 :(得分:7)
currying的实际用例是部分应用。
单独讨论并不是非常有趣。有趣的是,如果你的编程语言默认支持currying,就像在F#或Haskell中那样。
您可以使用任何支持头等功能的语言定义更高阶函数以进行currying和部分应用,但它与您获得的每个函数都具有的灵活性相差甚远,因此在没有您的情况下部分适用不得不做任何事。
因此,如果你看到人们将currying和部分应用混为一谈,那是因为这些概念与那些概念紧密相关 - 因为currying是无处不在的,你并不需要其他形式的部分应用而不是应用curried函数到连续的参数。
答案 1 :(得分:5)
传递上下文很有用。
考虑'map'功能。它需要一个函数作为参数:
map : (a -> b) -> [a] -> [b]
给定一个使用某种形式的上下文的函数:
f : SomeContext -> a -> b
这意味着您可以优雅地使用map函数,而无需声明'a'参数:
map (f actualContext) [1,2,3]
没有currying,你将不得不使用lambda:
map (\a -> f actualContext a) [1,2,3]
注意:
map
是一个函数,它采用包含值a
,函数f
的列表。它通过采用每个a
并对其应用f
来构建新列表,从而生成b
e.g。 map (+1) [1,2,3] = [2,3,4]
答案 2 :(得分:1)
轴承currying上的代码可以分为两组问题(我用Haskell来说明)。 句法,实施。
语法问题1:
在某些情况下,Currying可以提高代码清晰度。 清晰度意味着什么?读取该功能可清楚地显示其功能。 例如地图功能。
map : (a -> b) -> ([a] -> [b])
以这种方式阅读,我们看到地图是一个更高阶的函数,它将一个将as
转换为bs
的函数提升为一个将[a]
转换为[b]
的函数。
这种直觉在理解这些表达时特别有用。
map (map (+1))
内部地图的类型高于[a] -> [b]
。
为了弄清楚外部地图的类型,我们从上面递归地应用了我们的直觉。外部地图因此将[a] -> [b]
提升为[[a]] -> [[b]]
。
这种直觉会让你前进很多。
一旦我们将map
概括为fmap
,将map
概括为任意容器,就可以很容易地读取这样的表达式(注意我已经将每个fmap
的类型单一化了为了这个例子而改为另一种类型。)
showInt : Int -> String
(fmap . fmap . fmap) showInt : Tree (Set [Int]) -> Tree (Set [String])
希望上面说明fmap
提供了将香草函数提升到某个任意容器上的函数的概括概念。
语法问题2:
Currying还允许我们以无点形式表达函数。
nthSmallest : Int -> [Int] -> Maybe Int
nthSmallest n = safeHead . drop n . sort
safeHead (x:_) = Just x
safeHead _ = Nothing
以上通常被认为是好的风格,因为它说明了根据功能管道而不是显式操纵数据的思考。
<强>实施强>:
在Haskell中,点自由风格(通过currying)可以帮助我们优化功能。以点自由形式编写函数将允许我们记住它。
memoized_fib :: Int -> Integer
memoized_fib = (map fib [0 ..] !!)
where fib 0 = 0
fib 1 = 1
fib n = memoized_fib (n-2) + memoized_fib (n-1)
not_memoized_fib :: Int -> Integer
not_memoized_fib x = map fib [0 ..] !! x
where fib 0 = 0
fib 1 = 1
fib n = not_memoized_fib (n-2) + not_memoized_fib (n-1)
将其写为curoized函数,如在memoized版本中将curried函数视为实体,因此将其记忆。