很久前看javascript函数式编程时,作者提到一段垃圾代码:
当时看到这段代码时,以为很高级,用到了高阶函数。其实不然,这是一段很经典的垃圾代码,因为经过一步步规约最后结果是f = f2
。事实上,初学者很容易写出这样的代码,当时也不是很明白,直到最近看到λ演算中η-变换才豁然开朗。
首先介绍一下λ演算中η-变换规则:λx.f x -> f
,用js描述就是var func = x => f(x)
,很明显可以得到func = f
,同理可以对上面那段代码进行η-变换规约。其等价λ演算λf1.f2 (λx.f1 x)
,利用η-变换进行规约如下:
由此可知,了解λ演算,对于书写良好的函数式代码有一定用处,因为λ演算是函数式编程最重要的基石。假设你有js基础,下面介绍一下λ演算比较重要的基础知识点,以方便大家入门学习。
a.λ演算公式规则
形式化定义 所有的lambda表达式可以通过下述以BNF范式表达的上下文无关文法描述:
规则说明:
- 函数的作用是左结合的
- λ操作符被绑定到它后面的整个表达式。举例说明一下:
λf1.f2 (λx.f1 x)
和es6箭头函数的等价形式f1 => f2(x => f1(x))
对于规则1,如果知道lisp的话,就知道函数调用方式如(func . params)
等价于js里的func(...params)
也就不难理解了
b.自由变量和绑定变量
如果一个变量x没有在λ表达式中被绑定,比如λy . f x y
,x和f是自由的,y被绑定的,在嵌套λ表达式中,相当于每一层形成一个闭包,内层自由变量可能被外层绑定而成为绑定变量。
c.α变换、β规约和η-变换
- α变换 名称不那么重要,如
λx.f x
<=>λy.f y
- β规约
λx.M)N -> M[x/N]
如果N中所有变量的自由出现都在M[x/N]中保持自由出现,比如(lx.ly.y x)(lz.u) -> ly.y(lz.u)
- η变换 比如
f(x) = g(x)
,也就是f = (x => g(x)) = g
,相反过程称为η展开
另外λ演算还可以用来描述其它比如自然数,逻辑运算等(邱奇编码),这里不详细展开,因为有很多我也没看懂,有兴趣可以自己搜索看一下。