对“组合者”的好解释(非数学家)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了对“组合者”的好解释(非数学家)相关的知识,希望对你有一定的参考价值。
任何人都对“组合器”(Y-combinators等而不是the company)有一个很好的解释?
我正在寻找一个了解递归和高阶函数的实用程序员,但没有强大的理论或数学背景。
(注意:我说的是these things)
除非你深入理论,否则你可以将Y组合器视为一个功能齐全的技巧,比如monad。
Monads允许您链接动作,Y组合器允许您定义自递归函数。
Python内置了对自递归函数的支持,因此您可以在不使用Y的情况下定义它们:
> def fun():
> print "bla"
> fun()
> fun()
bla
bla
bla
...
fun
可以在fun
本身访问,所以我们可以很容易地称之为。
但是,如果Python不同,fun
内部无法访问fun
怎么办?
> def fun():
> print "bla"
> # what to do here? (cannot call fun!)
解决方案是将fun
本身作为fun
的参数传递:
> def fun(arg): # fun receives itself as argument
> print "bla"
> arg(arg) # to recur, fun calls itself, and passes itself along
Y使这成为可能:
> def Y(f):
> f(f)
> Y(fun)
bla
bla
bla
...
它所做的就是将一个函数称为参数。
(我不知道Y的这个定义是否100%正确,但我认为这是一般的想法。)
Reginald Braithwaite(又名Raganwald)在他的新博客homoiconic上撰写了一篇关于Ruby组合器的精彩系列。
虽然他(据我所知)没有看到Y-combinator本身,但他确实看过其他组合器,例如:
- Kestrel
- Thrush
- Cardinal
- Obdurate Kestrel
- 其他古怪的鸟类
引用维基百科:
组合子是一个高阶函数,它只使用函数应用程序和早期定义的组合器来定义其参数的结果。
现在这是什么意思?这意味着组合器是一个函数(输出仅由其输入确定),其输入包括函数作为参数。
这些功能看起来像什么,它们用于什么?这里有些例子:
(f o g)(x) = f(g(x))
这里o
是一个组合器,它接收2个函数,f
和g
,并返回一个函数作为其结果,f
与g
的组成,即f o g
。
组合器可用于隐藏逻辑。假设我们有一个数据类型NumberUndefined
,其中NumberUndefined
可以采用数值Num x
或值Undefined
,其中x
a是Number
。现在我们要为这个新的数字类型构造加法,减法,乘法和除法。语义与Number
的语义相同,除非Undefined
是输入,输出也必须是Undefined
,当除以数字0
时,输出也是Undefined
。
人们可以编写如下繁琐的代码:
Undefined +' num = Undefined
num +' Undefined = Undefined
(Num x) +' (Num y) = Num (x + y)
Undefined -' num = Undefined
num -' Undefined = Undefined
(Num x) -' (Num y) = Num (x - y)
Undefined *' num = Undefined
num *' Undefined = Undefined
(Num x) *' (Num y) = Num (x * y)
Undefined /' num = Undefined
num /' Undefined = Undefined
(Num x) /' (Num y) = if y == 0 then Undefined else Num (x / y)
注意所有关于Undefined
输入值的逻辑如何。只有分工才能做得更多。解决方案是通过使其成为组合器来提取逻辑。
comb (~) Undefined num = Undefined
comb (~) num Undefined = Undefined
comb (~) (Num x) (Num y) = Num (x ~ y)
x +' y = comb (+) x y
x -' y = comb (-) x y
x *' y = comb (*) x y
x /' y = if y == Num 0 then Undefined else comb (/) x y
这可以推广到所谓的Maybe
monad,程序员可以使用像Haskell这样的函数式语言,但我不会去那里。
组合器的功能是没有自由变量。这意味着,除其他外,组合器不依赖于函数外部的事物,仅依赖于函数参数。
使用F#这是我对组合器的理解:
let sum a b = a + b;; //sum function (lambda)
在上面的例子中,sum是一个组合子,因为a
和b
都与函数参数绑定。
let sum3 a b c = sum((sum a b) c);;
上述函数不是组合子,因为它使用sum
,它不是绑定变量(即它不是来自任何参数)。
我们可以通过简单地将sum函数作为参数之一来使sum3成为一个组合器:
let sum3 a b c sumFunc = sumFunc((sumFunc a b) c);;
这样sumFunc
被绑定,因此整个函数是组合子。
所以,这是我对组合器的理解。另一方面,它们的重要性仍然让我失望。正如其他人指出的那样,定点组合器允许人们在没有explicit
递归的情况下表达递归函数。即而不是调用自身的recusrsive函数调用作为参数之一传入的lambda。
这是我发现的最易理解的组合子派生之一:
http://mvanier.livejournal.com/2897.html
这看起来很好:http://www.catonmat.net/blog/derivation-of-ycombinator/
我在理论方面很缺乏,但我可以举一个例子来说明我的想象力,这可能对你有所帮助。最简单有趣的组合可能是“测试”。
希望你了解Python
tru = lambda x,y: x
fls = lambda x,y: y
test = lambda l,m,n: l(m,n)
用法:
>>> test(tru,"goto loop","break")
'goto loop'
>>> test(fls,"goto loop","break")
'break'
如果第一个参数为true,则test测试第二个参数,否则测试第三个参数。
>>> x = tru
>>> test(x,"goto loop","break")
'goto loop'
整个系统可以由几个基本的组合器构建。
(这个例子或多或少地复制了Benjamin C. Pierce的类型和编程语言)
这是一个很好的article。代码示例在方案中,但它们不应该很难遵循。
简而言之,Y组合子是一个更高阶函数,用于实现lambda表达式(匿名函数)的递归。查看Mike Vanier的文章How to Succeed at Recursion Without Really Recursing - 这是我见过的最好的实际解释之一。
另外,扫描SO档案:
以上是关于对“组合者”的好解释(非数学家)的主要内容,如果未能解决你的问题,请参考以下文章