实际使用咖喱功能?

时间:2011-02-03 16:04:32

标签: haskell functional-programming lisp scheme currying

有很多关于如何理解函数的教程,以及stackoverflow中的许多问题。然而,在阅读了The Little Schemer,几本书,教程,博客文章和stackoverflow主题之后,我仍然不知道这个简单问题的答案:“有什么关系?”我明白如何理解一个功能,而不是“为什么?”背后。

有人可以向我解释一下curried函数的实际用法(除了每个函数只允许一个参数的语言之外,使用currying的必要性当然非常明显。)

编辑:考虑到TLS的一些例子,

的好处是什么?
(define (action kind)
    (lambda (a b)
        (kind a b)))

而不是

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

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

10 个答案:

答案 0 :(得分:24)

curried函数的一个有效用途是减少代码量。

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

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

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

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

如果您的代码调用add,则代码会action调用+种类。与mul相同。

你定义了这些函数,就像你在许多命令式流行语言中所做的那样(其中一些已经包括lambdas,currying和功能世界中常见的其他功能,因为它们都非常方便)。

所有addsum都会使用相应的action将呼叫包裹到kind。现在,考虑这些函数的咖喱定义:

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

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

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

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

想象一下,函数action会被立即重写以接受另外3个参数。如果不做好准备,您将不得不重写addmul的实现:

(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))

但是,currying使你免于那种令人讨厌且容易出错的工作;您根本不需要在函数add-curriedmul-curried中重写符号,因为调用函数将提供传递给action的必要数量的参数。

答案 1 :(得分: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是什么以及是什么。

HTH。

答案 2 :(得分:4)

你可以看到currying是一种专业化。选择一些默认值并让用户(也许是你自己)使用专门的,更具有表现力的功能。

答案 3 :(得分:3)

我认为currying是处理一般n-ary函数的传统方法,只要你能定义的唯一函数是一元的。

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

(其他人已经介绍了这个决定的一些实际含义,所以我将在这里停止。)

答案 4 :(得分:2)

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

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

Haskell中缀操作符可以在任何一侧进行curry,因此您可以轻松地对elem函数(is-element-of)的针或容器侧进行curry。

答案 5 :(得分:2)

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

答案 6 :(得分:1)

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

enter image description here

答案 7 :(得分:0)

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

答案 8 :(得分:0)

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

答案 9 :(得分:0)

本身的currying是语法糖。语法糖就是你想要的容易。例如,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(即特定值)约束的g作为一个变量函数传递。

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

您自然希望在Perl中使用比较运算符(< =>)并且具有 sortordinal = sort(< =>) sortordinal在列表上工作的地方。要做到这一点,你将成为一个咖喱功能 事实上 在Perl中以这种方式定义了一个列表。

简而言之:currying是糖,使头等功能更自然。