Haskell curried函数的实际使用?
stackoverflow上有很多关于如何使用函数的教程,也有很多问题。然而,在阅读了《小阴谋家》、几本书、教程、博客文章和stackoverflow线程之后,我仍然不知道这个简单问题的答案:“咖喱有什么意义?”我确实理解如何咖喱函数,只是不知道它背后的“为什么” 有人能给我解释一下咖喱函数的实际用途吗(除了每个函数只允许一个参数的语言之外,使用咖喱函数的必要性当然很明显。)Haskell curried函数的实际使用?,haskell,functional-programming,lisp,scheme,currying,Haskell,Functional Programming,Lisp,Scheme,Currying,stackoverflow上有很多关于如何使用函数的教程,也有很多问题。然而,在阅读了《小阴谋家》、几本书、教程、博客文章和stackoverflow线程之后,我仍然不知道这个简单问题的答案:“咖喱有什么意义?”我确实理解如何咖喱函数,只是不知道它背后的“为什么” 有人能给我解释一下咖喱函数的实际用途吗(除了每个函数只允许一个参数的语言之外,使用咖喱函数的必要性当然很明显。) 编辑:考虑到TLS中的一些示例,使用 (define (action kind) (lambda (a b)
编辑:考虑到TLS中的一些示例,使用
(define (action kind)
(lambda (a b)
(kind a b)))
相对于
(define (action kind a b)
(kind a b))
我只能看到更多的代码,没有增加灵活性…因此,您不必增加带有一点lambda的样板文件。创建闭包非常容易。我时常使用SRFI-26。它真的很可爱。一个有效使用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
,使用kind+
。与mul
相同
您定义了这些函数,就像您在许多可用的命令式流行语言中所做的一样(其中一些已经包括了lambdas、curry和函数世界中常见的其他特性,因为所有这些都非常方便)
所有的add
和sum
do都在用相应的种类
包装对action
的调用。现在,考虑一下这些函数的定义:
(define add-curried
((curry action) +))
(define mul-curried
((curry action) *))
它们变得相当短了。我们只是通过只传递一个参数,kind
来实现函数action
,得到了接受其余两个参数的curried函数
这种方法允许您编写更少的代码,具有较高的可维护性
想象一下,函数action
将立即被重写,以接受另外3个参数。如果不使用curry,您必须重写add
和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))
但是咖喱使你免于那个讨厌的、容易出错的工作;您甚至不必重写函数
addcurried
和mul curried
中的符号,因为调用函数将提供传递给action的必要数量的参数,您可以将curried视为一种特殊化。选择一些默认值,给用户(可能是您自己)留下一个专门的、更具表现力的函数。它们可以使代码更易于阅读。考虑下面两个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.我认为,如果您只能定义一元函数,则curry是处理一般n元函数的传统方法
例如,在lambda演算(函数式编程语言的起源)中,只有一个变量抽象(在FPLs中转换为一元函数)。关于lambda演算,我认为证明这种形式主义更容易,因为你实际上不需要处理n元函数的情况(因为你可以用一元函数来表示任意n元函数)
(其他人已经讨论了这个决定的一些实际影响,所以我就到此为止。)咖喱本身就是一种语法上的糖分。语法糖就是你想让什么变得简单。例如,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是两个变量的函数,其计算结果为布尔和
列表是一个任意列表
您自然希望在Perl中使用比较运算符()并具有
sortordinal=sort()
sortordinal在列表中工作。要做到这一点,您将被归类为一个咖喱函数。
事实上
在Perl中,列表的排序就是这样定义的
简言之:咖喱是一种糖,它能使一流的功能更加自然 使用all::(a->Bool)->[a]->Bool
和一个curry谓词
all (`elem` [1,2,3]) [0,3,4,5]
Haskell中缀运算符可以在任意一侧使用,因此您可以轻松地在elem
函数(是的元素)的指针或容器一侧使用。我想为@Francesco answer添加示例
我们不能直接组合使用多个参数的函数。因为函数组合是函数编程的关键概念之一。通过使用Currying技术,我们可以组合包含多个参数的函数。考虑到TLS中的一些示例和您上面的示例,(定义(动作种类)(lambda(a b)(种类a b))
相对于(定义(动作种类a b)(种类a b))
?我只能看到更多的代码,没有更多的灵活性…@Philip:除了味道和击键次数之外,没有任何好处:-)请看“。我不是说lambdas。。。如果我理解正确,第一个函数是curry,第二个不是。再说一次,如果我没有弄错的话,两者都做了完全相同的事情,但是咖喱版本在定义和调用时都更长-((action*)5 5)
,而不是(action*5)
@Philip: