Y-conbinator"有没有用"?并没有,在大多数支持函数式编程的语言里,你可以自由的使用递归,而这货只是作为理论基石弥散在函数式编程的血肉之中
这是数学笔记,这是数学笔记,这是数学笔记,和计算机半毛钱关系都没有,重要的话要说三遍(逃
1.λ-calculus
图灵一生开了好多脑洞,其中λ-calculus便是其中一个.(图灵说,要有一个和图灵机一样的世界,于是便有了λ-calculus)
λ-calculus的重要元素的便是λ表达式,在haskell中可以很方便的定义 比如(\x -> x+3) 它的意思便是定义一个f(x)=x+3,并且,这个函数是没有名字的!这点很重要(题外话在haskell中为什么是\呢,把眼镜脱掉你会觉的\和λ真的很难区别而后者在键盘上更难打233333)
图灵的写法是λx.x+3 然而这货事实上和f(x)=x+3没什么太大区别....λ表达式不那么高大上的说法是匿名函数,也就是对于一个函数它没有sin cos func 之类的名字,我们都叫红领巾(λ).要调用这个函数也很简单 比如 (λx.x+3 9) 等于12 理解上这和f(9)=12并没有太大区别
/**********************************************************************************
/* 数学上形式化定义是非常有用的,它避免了二义性,而自然语言却很容易出现.
/* 形式化描述:λ表达式可由递归定义
/* 1.一个变量x是一个λ表达式
/* 2.t是一个λ表达式,x是一个变量,那么(λx.t)是一个λ表达式
/* 3.茹果t s是λ表达式,那么(t s)也是λ表达式
/* 上面这段话写成bnf范式就是:(也就是你会在大部分中文资料中能找到的)
/* <expr> ::= <identifier>
/* <expr> ::= (λ <identifier> . <expr>)
/* <expr> ::= (<expr> <expr>)
/*
/* 其中第一二句定义了一个函数,第三句定义了一个调用
***********************************************************************************/
在定义了λ表达式后在上面定义了两条公理,α转化和β归约,(说人话:α转化就是f(x)=x*x 和f(y)=y*y 是一样一样的,β归约就是f(x)=x*x 那么f(5) 可以化成25(看起来很蠢的想法?实际上你学到的那么神奇的数论,也就是在皮亚诺公理系统5个看似很蠢的定义上导出))
注意到,其实原本由λ 表达式和两条公理...这个世界是没有数字的(比如某个热带雨林里的皮拉罕语语言中天生不存在数字,于是那一组的人民天生没有数字概念233333)也没有加减乘除,乘方,图灵不甘心,我造的世界怎么能这么不完美!于是图灵把我们世界的数字搬到了λ世界
皮亚诺是如何定义自然数的?首先要有一个头S,然后有它的后继SS以及它的后继SSS,SSSS....然后我们人为的把S记做 0 SS记做1 SSS记做2就可以了(事实上皮亚诺公理比这再稍微复杂一点,在此做了一些简化)
图灵把 λf.λx.x指派为0 λf.λx.f x指派为1 λf.λx.f(f x)为3依次类推事实上写成我们平时写惯的写法就是x是0 f(x)是1 f(f(x))是2...
有了数,便可以在上面定义加减乘除,这里不再赘述.
图灵把这些叫做丘奇编码,塞进了λ的世界,于是便有了数和四则运算,因此上面写的 λx.x+3才是正确的
是不是有点和哥德尔数有点像?哥德尔数把形式系统内的一切命题都和自然数做了一个双射,这样才能让后面命题G的自指成功,而图灵构造了一个集合使其和自然数构成双射,把自然数很自然的塞到了λ里,不得不说大师的脑洞总是惊人的相似
2.递归
有了自然数,剩下的好多东西都可以在λ-calculus的世界里定义了,于是图灵一下子把布尔数,选择结构等,通通在λ世界里找到了相应的表示方法表示.
比如if ,就可以在λ世界表示出来,我们知道if事实上就三个元素:表达式,如果表达式为真返回的值,如果表达式为假返回的值,那么就可以构造这样的λ表达式:let if = λ boolean a b.(boolean and a) or (not boolean and b) 比如 if (3>4) 3 4 =(false and 3) or (true and 4) = false or 4 = 4 注意这里的and or not不是位运算,而事实上haskell里if就的确就有返回值,就类似于一个函数.
终于有一个东西让他陷入了思考,递归
注意这里说递归和循环事实上差不多的的,尾递归和循环能够相互转化,编译器经常干这种事.
为什么塞不进呢?举个栗子.我们现在要实现阶乘函数,唔,用c写的话想法是这样的:int fac(int x){return (x==1)?1:x*fac(x-1);}
于是很容易的想法let fac = λx.if (x==1) 1 (x * fac x-1) 但是想想现代pc是怎样处理递归的?call 自己 就好了.call相当于push + jmp 也就是先把必要的参数push进栈里,然后jmp到调用函数的地址上.但是首先....call自己...call 自己...自己叫红领巾(大雾).....
匿名函数的性质决定了自己无论如何也是call不到自己的,而为啥能调用if 这里只是写着方便而已,相当于一个宏,只是为了书写方便,但你想把一个还没定义好的函数用什么替换呢?所以写递归在λ世界中,从来不是一件简单的事
3.不动点和Y-conbinator
-----------------------------------------------------------<未完待续 有空补(keng)>------------------------------------------------
reference:
http://www.zhihu.com/question/20115649
https://en.wikipedia.org/wiki/Lambda_calculus
https://en.wikipedia.org/wiki/Church_encoding
byvoid函数式编程讲稿