29

有大量关于如何 curry 函数的教程,以及在 stackoverflow 上的许多问题。然而,在阅读了 The Little Schemer、几本书、教程、博客文章和 stackoverflow 线程之后,我仍然不知道这个简单问题的答案:“currying 有什么意义?” 我确实了解如何咖喱功能,而不是“为什么?” 在它后面。

有人可以向我解释一下柯里化函数的实际用途(在每个函数只允许一个参数的语言之外,其中使用柯里化的必要性当然非常明显。)

编辑:考虑到 TLS 的一些例子,有什么好处

(define (action kind)
    (lambda (a b)
        (kind a b)))

(define (action kind a b)
    (kind a b))

我只能看到更多代码而没有增加灵活性......

4

10 回答 10

24

柯里化函数的一种有效用途是减少代码量。

考虑三个函数,其中两个几乎相同:

(define (add a b)
  (action + a b))

(define (mul a b)
  (action * a b))

(define (action kind a b)
  (kind a b))

如果您的代码调用add,它会依次调用actionkind +。与 相同mul

您定义了这些函数,就像在许多可用的命令式流行语言中所做的那样(其中一些已经包括 lambdas、currying 和通常在函数世界中发现的其他特性,因为所有这些都非常方便)。

All addand sumdo,就是action用适当的kind. 现在,考虑这些函数的柯里化定义:

(define add-curried
  ((curry action) +))

(define mul-curried
  ((curry action) *))

它们变得相当短。我们只是action通过只传递一个参数来对函数进行 curried,the kind,并得到接受其余两个参数的 curried 函数。

这种方法允许您编写更少的代码,并具有高度的可维护性。

试想一下,该函数action将立即被重写以接受另外 3 个参数。如果没有柯里化,你将不得不重写你的addand实现mul

(define (action kind a b c d e)
  (kind a b c d e))

(define (add a b c d e)
  (action + a b c d e))

(define (mul a b c d e)
  (action * a b c d e))

但是柯里化让你免于那些讨厌且容易出错的工作;您甚至不必重写函数中的符号add-curriedmul-curried因为调用函数将提供传递给action.

于 2011-02-03T16:47:40.193 回答
11

它们可以使代码更易于阅读。考虑以下两个 Haskell 片段:

lengths :: [[a]] -> [Int]
lengths xs = map length xs

lengths' :: [[a]] -> [Int]
lengths' = map length

为什么要为您不打算使用的变量命名?

Curried 函数在以下情况下也有帮助:

doubleAndSum ys = map (\xs -> sum (map (*2) xs) ys

doubleAndSum' = map (sum . map (*2))

删除这些额外的变量使代码更易于阅读,并且您无需在脑海中清楚什么是 xs 和什么是 ys。

HTH。

于 2011-02-03T17:28:27.057 回答
4

您可以将咖喱视为一种专业化。选择一些默认值,并为用户(可能是您自己)留出专门的、更具表现力的功能。

于 2011-02-03T17:07:39.240 回答
3

我认为柯里化是处理一般 n 元函数的传统方法,前提是您可以定义的唯一函数是一元的。

例如,在 lambda 演算(函数式编程语言源于此)中,只有一个变量抽象(在 FPL 中转换为一元函数)。关于 lambda 演算,我认为证明这种形式主义更容易,因为您实际上不需要处理 n 元函数的情况(因为您可以通过柯里化用多个一元函数表示任何 n 元函数) .

(其他人已经涵盖了这个决定的一些实际影响,所以我会在这里停下来。)

于 2011-02-04T02:41:58.657 回答
2

all :: (a -> Bool) -> [a] -> Bool与咖喱谓词一起使用。

all (`elem` [1,2,3]) [0,3,4,5]

Haskell 中缀运算符可以在任一侧进行柯里化,因此您可以轻松地柯里化elem功能的针或容器侧(is-element-of)。

于 2011-10-04T22:44:23.873 回答
2

我们不能直接组合带有多个参数的函数。由于函数组合是函数式编程中的关键概念之一。通过使用 Currying 技术,我们可以组合带有多个参数的函数。

于 2017-05-11T12:44:28.887 回答
1

我想在@Francesco 答案中添加示例。

在此处输入图像描述

于 2015-04-09T09:28:51.970 回答
0

所以你不必用一点 lambda 来增加样板文件。

于 2011-02-03T16:07:54.737 回答
0

创建闭包非常容易。我不时使用 SRFI-26。它真的很可爱。

于 2011-02-03T16:15:27.883 回答
0

柯里化本身就是语法糖。语法糖就是你想让它变得简单的一切。例如,C 想要在汇编语言中制作“便宜”的指令,例如递增、简单,因此它们具有用于递增的语法糖,即 ++ 表示法。

 t = x + y
 x = x + 1

替换为 t = x++ + y

函数式语言可以很容易地拥有类似的东西。

f(x,y,z) = abc 
g(r,s)(z) = f(r,s,z). 
h(r)(s)(z) = f(r,s,z)

但它是全自动的。这允许将由特定 r0、s0(即特定值)绑定的 ag 作为单变量函数传递。

以 perl 的排序函数为例,它采用排序子列表,其中 sub 是两个变量的函数,计算结果为布尔值,列表是任意列表。

您自然会希望在 Perl 中使用比较运算符 (<=>) 并使用 sortordinal = sort (<=>),其中 sortordinal 用于列表。为此,您将排序为一个咖喱函数。
事实上,某种列表在 Perl 中正是以这种方式定义的。

简而言之:咖喱是让一流的功能更自然的糖。

于 2011-03-09T03:18:32.743 回答