【问题标题】:What are real use cases of currying?柯里化的真实用例是什么?
【发布时间】:2015-10-11 15:28:37
【问题描述】:

我一直在阅读很多关于柯里化的文章,但几乎所有文章都具有误导性,将柯里化解释为部分函数应用程序,并且所有示例通常都是关于 arity 为 2 的函数,例如 add 函数或其他东西。

JavaScript 中curry 函数的许多实现使其在每个部分应用程序中接受多个参数(请参阅lodash),当Wikipedia article 清楚地表明currying 是关于:

将接受多个参数(或参数元组)的函数的评估转换为评估一系列函数,每个函数都有一个参数(部分应用)

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

【问题讨论】:

  • 是什么让您认为该术语的这些其他用法是“错误的”?

标签: functional-programming currying


【解决方案1】:

currying 的真正用例是部分应用。

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

您可以在任何支持一流函数的语言中为柯里化和部分应用程序定义高阶函数,但这与您获得的每个函数都被柯里化时所获得的灵活性相去甚远,因此部分适用而无需您做任何东西。

因此,如果您看到人们将柯里化和部分应用混为一谈,那是因为这些概念之间的联系非常紧密——因为柯里化无处不在,除了将柯里化函数应用于连续参数之外,您真的不需要其他形式的部分应用。

【讨论】:

【解决方案2】:

传递上下文很有用。

考虑“地图”功能。它接受一个函数作为参数:

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]

【讨论】:

    【解决方案3】:

    轴承柯里化对代码的影响可以分为两组问题(我用Haskell来说明)。 语法,实现。

    语法问题 1:

    Currying 在某些情况下可以提高代码清晰度。 清晰度是什么意思?读取函数可以清楚地指示其功能。 例如地图功能。

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

    以这种方式阅读,我们看到 map 是一个高阶函数,它将将as 转换为bs 的函数提升为将[a] 转换为[b] 的函数。

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

    map (map (+1))
    

    内部映射具有上述[a] -> [b] 的类型。 为了弄清楚外部地图的类型,我们递归地应用我们的直觉。外部地图因此将[a] -> [b] 提升到[[a]] -> [[b]]

    这种直觉会让你前进很多。 一旦我们将map 泛化为fmapmap 在任意容器上,读取这样的表达式就变得非常容易(注意,为了例子)。

    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 中,无点样式(通过柯里化)可以帮助我们优化函数。以无点形式编写函数可以让我们记住它。

    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)
    

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

    【讨论】:

      猜你喜欢
      • 2021-04-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-22
      相关资源
      最近更新 更多