在Scheme定义一个函数,通常写法如下:
1 | (define (sum x y) |
还有一种类似于定义变量的方式:
1 | (define sum (lambda (x y) |
利用lambda表达式,把sum和一个匿名函数联系在一起。
这种方式有点类似于JS:
1 | var sum = function(x, y) { return x + y; } |
利用lambda表达式,能够简化代码,下面举一个简单的例子。
1 | (define (translate point delta) |
修改后:
1 | (define (translate point delta) |
很显然,第二种更简洁直观。
Scheme还内置了let关键字,给人的感觉是创建了变量,其实不然,只是把某个表达式给了个名字罢了。
(let ((x exp1) (y exp2) (z exp3)) (fn x y z))
等价于
((lambda (x y z) (fn x y z)) exp1 exp2 exp3)
需要注意的是,let里面是个list,不保证求值顺序,所以exp3里面不能使用x和y。
好了,现在开始实现一个方法,返回集合的所有子集。
1 | (define (ps set) |
现在解释一下整个方法的思路:首先,如果set为空,返回一个由空列表组成的列表;下面运用递归的思想,定义ps-rest是set排除第一个元素的集合的所有子集,比如传入的set是’(1 2 3),排除第一个元素就是’(2 3),它的所有子集组成的列表是’(() (3) (2) (2 3)),列表里面共四个元素,这就是ps-rest。我们往ps-rest里面append包含第一个元素1的集合。具体append什么呢?我们mapps-rest,在每一项前面加上第一个元素1,ps-rest第一项是空集,连接完之后是(1),append到ps-rest里面,第二项是(3),连接完之后是(1 3),放到ps-rest里面,第三项第四项类似。最终,我们就得到了结果’(() (3) (2) (2 3) (1) (1 3) (1 2) (1 2 3))。
下面实现算法相对更复杂的全排列。
1 | (define (permute items) |
解释下算法。从apply开始说吧。我们把一系列结果append起来,也就是把后面map里面的求值结果串起来。map的求值结果是什么呢?它遍历了输入的items,比如我们输入’(1 2 3),第一次遍历使用的是1,也就是向map的第一个参数lambda表达式传入了1,也就是ele是1。接着,对1做了什么呢?又是一个map,遍历了(permute (remove ele items)),remove是从items中移出ele,这个表达式求值结果是’((2 3) (3 2)),遍历它,把1加到每项的前面,得到的结果是’((1 2 3) (1 3 2))。至此,第一次遍历完成,回到外层的map,开始第二次遍历,传入ele为2,后续就不再赘述了。最终得到的结果是’((1 2 3) (1 3 2) (2 1 3) (2 3 1) (3 1 2) (3 2 1))。