柯里化的实际用例是什么?

What are real use cases of currying?

我读了很多关于柯里化的文章,但几乎所有文章都是误导性的,将柯里化解释为部分函数应用程序,并且所有示例通常都是关于元数为 2 的函数,例如 add函数什么的。

JavaScript 中 curry 函数的许多实现也使得每个部分应用程序接受超过 1 个参数(请参阅 lodash), when Wikipedia article 清楚地表明柯里化是关于:

translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument (partial application)

基本上柯里化是一系列部分应用程序,每个应用程序都有一个参数。我真的很想知道它在任何语言中的实际用途。

柯里化的实际用例是部分应用。

柯里化本身并不是很有趣。有趣的是,如果您的编程语言默认支持柯里化,就像 F# 或 Haskell 中的情况一样。

您可以使用任何支持第一个 class 函数的语言为柯里化和部分应用程序定义高阶函数,但这与您获得的每个函数都被柯里化并因此部分地获得的灵活性相去甚远您无需执行任何操作即可适用。

因此,如果您看到人们将柯里化和偏应用混为一谈,那是因为这些概念之间的联系有多紧密 - 由于柯里化无处不在,除了将柯里化函数应用于连续参数之外,您实际上不需要其他形式的偏应用.

传递上下文很有用。

考虑 'map' 函数。它接受一个函数作为参数:

map : (a -> b) -> [a] -> [b]

给定一个使用某种形式上下文的函数:

f    : SomeContext -> a -> b

这意味着您可以优雅地使用 map 函数而无需声明 'a' 参数:

map (f actualContext) [1,2,3]

如果没有柯里化,您将不得不使用 lambda:

map (\a -> f actualContext a) [1,2,3]

备注:

map 是一个函数,它接受一个包含 a 值的列表,一个函数 f。它通过获取每个 a 并对其应用 f 来构建一个新列表,从而生成 b

的列表

例如map (+1) [1,2,3] = [2,3,4]

bearing currying on code可以分为两组问题(我用Haskell来说明)。 语法,实现。

语法问题 1:

柯里化在某些情况下可以使代码更加清晰。 清晰度是什么意思?阅读函数可以清楚地表明其功能。 例如地图功能。

map : (a -> b) -> ([a] -> [b])

这样读,我们看到map是一个高阶函数,将asbs的函数提升为[a]到[=18=的函数].

这种直觉在理解此类表达式时特别有用。

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:

柯里化还允许我们以无点形式表达函数。

nthSmallest : Int -> [Int] -> Maybe Int
nthSmallest n = safeHead . drop n . sort

safeHead (x:_) = Just x
safeHead _     = Nothing

以上通常被认为是好的风格,因为它说明了从函数管道的角度思考,而不是对数据的显式操作。

实施:

在Haskell中,point free style(通过柯里化)可以帮助我们优化函数。以点自由形式编写函数将使我们能够记住它。

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)

将它写成一个柯里化函数,就像在记忆化版本中一样,会将柯里化函数视为一个实体,因此将其记忆化。