The Little Schemer-周而复始之Y组合子由来

什么是递归?

(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)))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/361898.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

霸主–统治和管理API的地方

今天我们生活在一个越来越分散的世界中。 如今的计算机系统不再是在随机桌子下面的某些硬件上运行单个部门项目,而是大规模,集中甚至分散地运行。 监视和管理的需求从未改变,但是随着时间的推移变得越来越复杂。 如果将所有这些跨功能功能都放…

Django操作与内容

一、路由系统 1.如何实现伪静态 在cnblogs中:https://www.cnblogs.com/wangwei5979/p/11160708.html 而我们自己写的: http://127.0.0.1:8000/up_studnet/?id12同样是网页 为何cnblogs这样设计 原因: 1就是因为比较美观 2由于使用搜索引擎来…

监控 SQL Server 的运行状况

Microsoft SQL Server 2005 提供了一些工具来监控数据库。方法之一是动态管理视图。动态管理视图 (DMV) 和动态管理函数 (DMF) 返回的服务器状态信息可用于监控服务器实例的运行状况、诊断问题和优化性能。 常规服务器动态管理对象包括: dm_db_*:数据库和…

RFC2616-HTTP1.1-Header Field Definitions(头字段规定部分—单词注释版)

part of Hypertext Transfer Protocol -- HTTP/1.1RFC 2616 Fielding, et al. 14 Header Field Definitions(规定) This section(部分,章节) defines(规定定义) the syntax(语法) and semantics(语意) of all standard(标准) HTTP/1.1 header fields. For entity-…

Java开发人员应该知道的5种错误跟踪工具

随着Java生态系统的不断发展,可满足不断增长的请求和用户对高性能需求的Web应用程序成为了新型的现代开发工具。 具有快速新部署的快速节奏环境需要跟踪错误并获得应用程序行为的洞察力,而传统方法无法维持这种水平。 在这篇文章中,我们决定收…

Emacs中的Color Theme以及字体设置

先上一张效果图: Color Theme用的是gnome2, 字体用的是Visual Studio自带的Consolas。我使用的环境是WindowsCygwinEmacs23.2。 1,安装Color Theme插件 首先,从http://download.savannah.gnu.org/releases/color-theme/下载color theme 6.6.0版本。 接着…

JavaScript与 HTML表单的交互过程,想要学习动态网页但是无从下手的新手看看。...

最近开始了 java web的学习在学习中学到了很多有用的东西 比如说 html 表单如何 和 javascript进行交互的 读完本篇文章后相信初学者会有一个 清晰的理解 。。。 对应下面的代码看我写的问题 。 其实表单的交互操作很简单 就是在 提交表单数据的时候调用一个 javascrip…

问题记录之前端路由系统

概要: 公司的一个项目中使用了根据路由配置生成对应的Route,而配置会存在一份在store中,当store中的RouteConfig变化时,会根据最新的配置来生成最新的试图。 因为路由配置系统实现上的一些缺陷本次需要对其就行性能上的一些优化…

vue兼容ie10问题并且node——module中出现es6语法如何解决

一、首先进行安装babel-polyfill,如果你用yarn安装babel-polyfill的话需要yarn add babel-polyfill进行安装 二、在babel.config.js中加入 三、在ie浏览器中找到报错的文件,然后将文件加入其中 转载于:https://www.cnblogs.com/changhuanran/p/11193149.…

2个在Java中将Byte []数组转换为String的示例

将字节数组转换为String似乎很容易,但是很难做到正确。 每当字节转换为String或char时,许多程序员都会犯忽略字符编码的错误,反之亦然。 作为程序员,我们都知道计算机只能理解二进制数据,即0和1。我们看到和使用的所有…

Linux文件IO-例会笔记总结

上周日实验室例会主要涉及linux文件操作的内核实现。主要讨论了linux下对文件进行操作时,系统内部调用了那些函数以及它们是怎么相互配合的。 linux系统是怎样对不同介质和不同的文件系统提供统一的文件操作接口呢?答案是:VFS。系统中所有文件…

js算法初窥03(搜索及去重算法)

前面我们了解了一些常用的排序算法,那么这篇文章我们来看看搜索算法的一些简单实现,我们先来介绍一个我们在实际工作中一定用到过的搜索算法——顺序搜索。 1、顺序搜索 其实顺序搜索十分简单,我们还是以第一篇文章写好的架子作为基础&#…

nginx try_files流程解析

前端部署单页应用时在nginx上经常用到try_files指令,而对于try_files并不知道其所以然,所以花时间整理总结如下。 Syntax: try_files file … uri; try_files file … code; Default: — Context: server, location 根据root和alias指令提供的值按照tr…

javascript中编码与解码的decodeURI()、decodeURIComponent()区别

1、 定义和用法 decodeURI() 函数可对 encodeURI() 函数编码过的 URI 进行解码。decodeURIComponent() 函数可对 encodeURIComponent() 函数编码的 URI 进行解码。 从W3C的定义和用法来看,两者没有什么区别,但是两者的参数是有区别的:decodeU…

vb 类模拟 引用

引用:http://wenku.baidu.com/view/f434ea26a5e9856a56126008.html Class1中 Option Explicit Public Sub test() Form1.TextForIpAddressAdd.Text "123"End Sub Form1中 Option Explicit Private test As New Class1 Private Sub Form_Load() 初始化 te…

用js来实现那些数据结构12(散列表)

上一篇写了如何实现简单的Map结构,因为东西太少了不让上首页。好吧。。。 这一篇文章说一下散列表hashMap的实现。那么为什么要使用hashMap?hashMap又有什么优势呢?hashMap是如何检索数据的?我们一点一点的来解答。 在我们学习一门…

如何自定义Hibernate脏检查机制

介绍 在上一篇文章中,我描述了Hibernate自动脏检查机制。 尽管您应该始终喜欢它,但是有时您可能想添加自己的自定义污垢检测策略。 自定义脏检查策略 Hibernate提供以下定制机制: 休眠拦截器#findDirty() CustomEnt…

读vue【深入响应式系统】后整理

一直以来对vue的依赖自动追踪的原理很感兴趣,像魔法一样。对于交给vue的对象返回后,在哪里使用了这个返回的对象vue会自动追踪,等这个对象改变时vue会自动通知到之前使用改变量的方法,整个过程和react很不一样,react的…

萌新自我介绍

第一次用博客,多有不会,可能向各位大佬请教,谢谢!!!!转载于:https://www.cnblogs.com/fakerOrz/p/11194872.html

使用select一个表更新另一个表(批量更新)

update a set a2b.b2, a3b.b3, ... from b where a.a1b.b1 转载于:https://www.cnblogs.com/haver/articles/2244740.html