什么是递归?
(define length(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l)))))))
以上是length函数的实现用递归的形式计算出数据集合l的长度。
如果没有define这种赋值操作我们怎么定义length函数?换句话说我们怎么使用匿名函数完成递归。
(lambda (l)(cond((null? l) 0)(else (add1 (enternity (cdr l))))))
这个匿名函数判断如果入参列表长度是0则返回0,如果入参列表长度超过0则不会有结果返回,所以这个匿名函数只能计算长度为0的列表的长度值,为了方便称呼可以为其起名length0。
注:enternity是无限递归函数,不会有结果返回。实现见补充1。
(lambda (l)(cond((null? l) 0)(else (add1 (length0 (cdr l))))))
因为无法使用length0这个名字,所以讲length0替换成对应的匿名函数:
(lambda (l)(cond((null? l) 0)(else (add1((lambda (l)(cond((null ? l) 0)(else (add1(enternity (cdr l))))))(cdr l))))))
这个匿名函数可以称为length≤1,因为它只能判断长度小于等于1列表。
根据这个规律还可以写出来length≤2:
(lambda (l)(cond((null? l) 0)(else (add1((lambda (l)(cond((null ? l) 0)(else (add1((lambda (l)(cond(null ? l) 0)(else (add1 (enternity (cdr l)))))(cdr l))))))(cdr l))))))
这样的代码可以一直写下去,但是又无法确保写出来的函数一定够用。
通过观察可以发现每个匿名函数实现的功能都是入参列表出参列表长度,所以可以给匿名函数起一个名字length。将length0做一些改变:
(lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l))))))
enternity)
再对length≤1做一些改变
(lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l))))))
(lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l))))))
enternity))
通过这个方法还可以对length≤2进行修改
(lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l))))))
(lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l))))))
(lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l))))))
enternity)))
通过引入length参数改造后的函数还是可以看到明显重复。
(lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l))))))
这部分被重复了很多遍的函数入参是一个length函数,实现的功能是生产一个length函数,所以可以为其起名mk-length。
(lambda (mk-length)(mk-length enternity)
(lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l))))))))
以上是修改后的length0函数
length≤1:
(lambda (mk-length)(mk-length(mk-length enternity))
(lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l))))))))
length≤2:
(lambda (mk-length)(mk-length(mk-length(mk-length enternity)))
(lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l))))))))
到这里要增加函数处理列表的长度只需要增加一级mk-length函数调用,但还是没有解决面对未知长度的列表该函数总有调用结束的时候。
简单解析一下length≤2的执行步骤:
首先执行的是最里层的 (mk-length enternity)
得到length0
,length0就是mk-length函数返回的匿名函数,只是这个匿名函数上下文中的length是enterity。
其次执行的是中间层的 (mk-length length0)
得到length≤1
,length≤1是mk-length函数返回的匿名函数,只是这个匿名函数上下文中的length是length0。
最后执行的是最外面层级 mk-length length≤1
得到length≤2
,length≤2是mk-length函数返回的匿名函数,只是这个匿名函数上下文中的length是length≤1。
PS:mk-length返回的函数就是length函数,也就是上面提到的length0、length≤1和length≤2等。
就这样length≤2函数制作完成。接下来简单介绍下length≤2使用列表(a b c)
作为入参的执行过程。
首先执行函数length≤2,(a b c)
列表不为空,将(length≤1 (b c))返回结果加1返回。
其次执行函数length≤1,(b c)
列表不为空,将(length0 ©)返回结果加1返回。
再次执行函数length0,(c)
列表不为空,将(enterity ())返回结果加1返回。
因为enterity无限循环无法退出导致这个length≤2函数入参(a b c)没有结果。
这是一个从右往左安装,从左往右执行的过程。length≤1等函数中的length是上一步传进来的函数,但是这里的上一步指的是安装过程的上一步,不是执行过程的上一步。
通过对上面length≤2函数执行过程的解析发现当执行到enternity整个过程就中断了。怎么才能让length一直执行下去?要执行length就需要先mk-length,但是mk-length目前无法引用到自己。怎么办?
(lambda (mk-length)(mk-length enternity)
(lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l))))))))
因为函数enternity是无限递归不会返回,所以将enternity替换为mk-length貌似对结果也没啥影响。
(lambda (mk-length)(mk-length mk-length)
(lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l))))))))
只要mk-length函数内名字统一就不会影响函数运行,所以可以将length替换为名字mk-length。
(lambda (mk-length)(mk-length mk-length)
(lambda (mk-length)(lambda (l)(cond((null? l) 0)(else (add1 (mk-length (cdr l))))))))
到这里解决了mk-length无法引用自身的问题,接下来要解决不可以无限循环问题。
现在已经将length改名为mk-length,那么mk-length是生产length的,在需要使用length的地方可以使用mk-length来生成一个length。
(lambda (mk-length)(mk-length mk-length)
(lambda (mk-length)(lambda (l)(cond((null? l) 0)(else (add1 ((mk-length enterity) (cdr l))))))))
使用(apples)
作为参数拆解上面函数的执行步骤。
第一步,将mk-length作为入参执行 (mk-length mk-length)
((lambda (mk-length)(lambda (l)(cond((null? l) 0)(else (add1 ((mk-length enterity) (cdr l)))))))
(lambda (mk-length)(lambda (l)(cond((null? l) 0)(else (add1 ((mk-length enterity) (cdr l))))))))
第二步,将第二个mk-length作为入参传入第一个mk-length
(lambda (l)(cond((null? l) 0)(else (add1 ((lambda (mk-length)(lambda (l)(cond((null? l) 0)(else (add1 ((mk-length enterity) (cdr l)))))))enterity) (cdr l)))))
第三步,将enterity作为mk-length传入lambda (mk-length)
(lambda (l)(cond((null? l) 0)(else (add1 ((lambda (l)(cond((null? l) 0)(else (add1 ((enterity enterity) (cdr l)))))))(cdr l)))))
第四步,将列表(apples)
带入以上函数得到结果1
如果传入的是
(apples bananas)
,执行会中断在(enterity enterity)
再看下刚才得到的函数
(lambda (mk-length)(mk-length mk-length)
(lambda (mk-length)(lambda (l)(cond((null? l) 0)(else (add1 ((mk-length enterity) (cdr l))))))))
稍微做一些修改
((lambda (mk-length)(mk-length mk-length))
(lambda (mk-length)(lambda (l)(cond((null? l) 0)(else (add1 ((mk-length mk-length) (cdr l))))))))
这样会发现,每当要枯竭时(mk-length mk-length)会计算出一个新了函数以供使用。是的(mk-length mk-length)的返回值看起来像一个length函数。
到这里已经完成了匿名函数递归实现length的需求,但是我们还要对它做一些简化归纳出一个通用的模式,实现任何匿名函数都可以完成递归的需求。
将(mk-length mk-length)提取出来作为length传入:
(lambda (mk-length)(mk-length mk-length)
(lambda (mk-length)(lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l)))))))(mk-length mk-length)))
拆解一下上面的函数会得到
((lambda (mk-length)(lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l)))))))(mk-length mk-length))
(lambda (mk-length)(lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l)))))))(mk-length mk-length)))
((lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l)))))))
((lambda (mk-length)(lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l)))))))(mk-length mk-length))
(lambda (mk-length)(lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l)))))))(mk-length mk-length))))
((lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l)))))))
((lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l))))))
((lambda (mk-length)(lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l)))))))(mk-length mk-length))
(lambda (mk-length)(lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l)))))))(mk-length mk-length))))))
这样下去永无止境,将mk-length传给mk-length又立马执行(mk-length mk-length)会继续得到(mk-length mk-length),需要继续执行(mk-length mk-length),这样一直停不下来。
需要回到最初正确的方式中。
((lambda (mk-length)(mk-length mk-length))
(lambda (mk-length)(lambda (l)(cond((null? l) 0)(else (add1 ((mk-length mk-length) (cdr l))))))))
上面方式的提取会导致无限递归是因为(mk-length mk-length)的调用会立马得到一个(mk-length mk-length)这样会一直无限递归下去。
出了(mk-length mk-length)这样立即调用的提取方式还可以延迟调用, (lambda (l) ((mk-length mk-length) l))
这样只会在需要调用length时才会调用(mk-length mk-length)
((lambda (mk-length)(mk-length mk-length))
(lambda (mk-length)((lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l)))))))(lambda (l) ((mk-length mk-length) l)))))
到这里基本上完成了匿名函数length递归的简化,还需要最后一步将其提取出来。
((lambda (le)((lambda (mk-length)(mk-length mk-length))(lambda (mk-length)(le (lambda (l) ((mk-length mk-length) l))))))
(lambda (length)(lambda (l)(cond((null? l) 0)(else (add1 (length (cdr l))))))))
再看下其中的通用模式
(lambda (le)((lambda (mk-length)(mk-length mk-length))(lambda (mk-length)(le (lambda (l)((mk-length mk-length) l))))))
这个模式被命名为Y组合子
(define Y (lambda (le)((lambda (f) (f f))(lambda (f)(le (lambda (x) ((f f) x)))))))
以上就是Y组合子的来龙去脉。
补充1:
(define enternity(lambda (l)(enternity l)))