这个系列有个显著的特点,那就是标题越来越长。忽然发现今天是读书节,读书节多读书。

==下面是没有意义的一段话================================================

我是一个喜欢从学习知识中获得乐趣并乐于分享这种乐趣的人。我认为大部分知识只要花点时间都是能学会的。几年前,我迷上微分几何。我对每个朋友说这东西很有意思花点时间精力就能学会。他们回答说唉没时间时间不知去哪儿了。后来,我迷上量子力学。我对每个朋友说这东西值得一学,只要花点时间精力。他们回答说唉烦心事太多没精力去学。再后来,我迷上程序语言。我对每个朋友说只要使劲挤点时间就能掌握这个。他们回答说唉浑浑噩噩不知道一直在忙什么没时间唉。现在,他们都结婚了,我依然单身。

==然后,正文开始======================================================

不动点

唉,又是一个数学概念, 又是没有实际意义的一节。 但是作为递归相关的常识,还是得提一下。 话说回来,把这玩意儿当作常识,我大概是价值观扭曲,重要性排行颠倒吧。 据说很多研究程序语言,研究偏微分方程的人都这样。

然后,一个——呃——东西$x$被称为函数$f$的不动点,如果它满足下面条件: \[ (f \; x) = x \]

不动点和递归有什么关系呢? $({Y} \; f)$不仅是关于辅助函数$f$的递归函数,同时也是$f$的不动点。 证明如下: \begin{eqnarray*} ({Y} \; f) &=& (\lambda x.(\lambda v.(v \; v) \; \lambda f.(x \; (f \; f))) \; f) \\               &=& (\lambda x.(\lambda v.(v \; v) \; \lambda p.(x \; (p \; p))) \; f) \\               &=& (\lambda v.(v \; v) \; \lambda p.(f \; (p \; p))) \\               &=& (\lambda p.(f \; (p \; p)) \; \lambda p.(f \; (p \; p))) \\               &=& (f \; (\lambda p.(f \; (p \; p)) \; \lambda p.(f \; (p \; p)))) \\               &=& (f \; (\lambda v.(v \; v) \; \lambda p.(f \; (p \; p)))) \\               &=& (f \; (\lambda x.(\lambda v.(v \; v) \; \lambda p.(x \; (p \; p))) \; f)) \\               &=& (f \; ({Y} \; f)) \end{eqnarray*} 由于递归函数肯定能写成$({Y} \; f)$的形式(参见之前mkdouble的构造方法),递归函数必定是某个函数$f$的不动点。

加入fix表达式

用Y组合子构造递归函数总归太麻烦。 所以需要能直接构造递归函数的表达式。 先来看这个表达式需要哪些元素。 首先,递归函数要调用自己,需要一个指向自己的变量,记为$X_1$。 其次,递归函数有个参数,记为$X_2$。 最后,递归函数的函数体,记为$M$。 构造递归函数其实是寻找不动点的过程,所以这个表达式叫fix表达式(不动点叫fixed point)。 fix表达式长这个样: \[ ({fix} \; X_1 \; X_2 \; M) \] 加入fix表达式最简单的方法是定义为宏: \[ ({fix} \; X_1 \; X_2 \; M) = ({Y} \; \lambda X_1.\lambda X_2.M) \] 但是这样做又重新引入了Y组合子。 这次采用另一种方法,不定义宏,而是把fix表达式加入语法: \begin{eqnarray*}   M, N, L &=& ... \\           &|& ({fix} \; X_1 \; X_2 \; M) \end{eqnarray*}

现在考察fix表达式的求值过程。 fix表达式的求值结果是个函数,参数是$X_2$: \[ eval(({fix} \; X_1 \; X_2 \; M)) = \lambda X_2.? \] 最适合放在问号处的函数体的是$M$,但是$M$包含了自由变量$X_1$,使用$M$之前得先想方法除掉$X_1$。 $X_1$代表递归函数本身,也就是$({fix} \; X_1 \; X_2 \; M)$。 为了去掉$M$中的自由变量$X_1$,将$X_1$替换为递归函数$({fix} \; X_1 \; X_2 \; M)$。 综上,fix表达式的求值过程为: \[ eval(({fix} \; X_1 \; X_2 \; M)) = \lambda X_2.M[X_1 \leftarrow ({fix} \; X_1 \; X_2 \; M)] \] 代码:

加入了新语法,要添加相应的替换过程(这个替换过程和$\lambda X.M$的替换过程类似,但是麻烦了点): \begin{eqnarray*}   ({fix} \; X_1 \; X_2 \; M)[X_1 \leftarrow N] &=& ({fix} \; X_1 \; X_2 \; M) \\   ({fix} \; X_1 \; X_2 \; M)[X_2 \leftarrow N] &=& ({fix} \; X_1 \; X_2 \; M) \\   ({fix} \; X_1 \; X_2 \; M)[X_3 \leftarrow N] &=& ({fix} \; X_4 \; X_5 \; M[X_2 \leftarrow X_5][X_1 \leftarrow X_4][X_3 \leftarrow N]) \\   &其中&X_3 \neq X_1, X_3 \neq X_2, \\   &&X_4 \notin FV(N), X_4 \notin FV(M)\backslash\{X_1\}, \\   &&X_5 \notin FV(N), X_5 \notin FV(M)\backslash\{X_2\} \end{eqnarray*} 代码:

测试一下:

'((fix f n (if (iszero n) 0 (+ 2 (f (- n 1))))) 4)

>> 8

加入letrec表达式

let表达式不能定义递归函数,所以有个letrec表达式专门用来定义递归函数。 letrec表达式长这个样: \[ ({letrec} \; X_1 \; X_2 \; N \; M) \] $X_1$,$X_2$和$N$定义了一个递归函数,$M$是用到这个递归函数的一个表达式。

类似let表达式,将letrec表达式定义为宏: \[ ({letrec} \; X_1 \; X_2 \; N \; M) = (\lambda X_1.M \; ({fix} \; X_1 \; X_2 \; N)) \] 代码:

简单易懂的程序语言入门小册子(5):基于文本替换的解释器,递归,不动点,fix表达式,letrec表达式的更多相关文章

  1. 简单易懂的程序语言入门小册子(3):基于文本替换的解释器,let表达式,布尔类型,if表达式

    let表达式 let表达式用来声明一个变量. 比如我们正在写一个模拟掷骰子游戏的程序. 一个骰子有6个面. 所以这个程序多次用到了6这个数字. 有一天,我们忽然改变主意,要玩12个面的骰子. 于是我们 ...

  2. 简单易懂的程序语言入门小册子(1):基于文本替换的解释器,lambda演算

    最近比较闲,打算整理一下之前学习的关于程序语言的知识.主要的内容其实就是一边设计程序语言一边写解释器实现它.这些知识基本上来自Programming Languages and Lambda Calc ...

  3. 简单易懂的程序语言入门小册子(1.5):基于文本替换的解释器,递归定义与lambda演算的一些额外说明

    这一篇接在第一篇lambda演算的后面.讲讲一些数学知识. 经常有些看似很容易理解的东西,一旦要描述得准确无误,就会变得极为麻烦. 软件工程里也有类似情况:20%的代码实现了核心功能,剩下80%的代码 ...

  4. 简单易懂的程序语言入门小册子(6):基于文本替换的解释器,引入continuation

    当我写到这里的时候,我自己都吃了一惊. 环境.存储这些比较让人耳熟的还没讲到,continuation先出来了. 维基百科里对continuation的翻译是“延续性”. 这翻译看着总有些违和感而且那 ...

  5. 简单易懂的程序语言入门小册子(7):基于文本替换的解释器,加入continuation,重构解释器

    或许在加入continuation之前要先讲讲费这么大劲做这个有什么意义. 毕竟用不用continuation的计算结果都是一样的. 不过,这是一个兴趣使然的系列,学习这些知识应该完全出于好奇与好玩的 ...

  6. 简单易懂的程序语言入门小册子(4):基于文本替换的解释器,递归,如何构造递归函数,Y组合子

    递归.哦,递归. 递归在计算机科学中的重要性不言而喻. 递归就像女人,即令人烦恼,又无法抛弃. 先上个例子,这个例子里的函数double输入一个非负整数$n$,输出$2n$. \[ {double} ...

  7. Go语言入门篇-gRPC基于golang & java简单实现

    一.什么是RPC 1.简介: RPC:Remote Procedure Call,远程过程调用.简单来说就是两个进程之间的数据交互. 正常服务端的接口服务是提供给用户端(在Web开发中就是浏览器)或者 ...

  8. C语言入门(2)——安装VS2013开发环境并编写第一个C语言程序

    在C语言入门系列中,我们使用Visual studio 2013 Professional作为开发工具.本篇详细介绍如何安装Visualstudio 2013 Professional并写出我们第一个 ...

  9. 《Java从入门到失业》第一章:计算机基础知识(三):程序语言简介

    1.3程序语言简介 我们经常会听到一些名词:低级语言.高级语言.编译型.解释型.面向过程.面向对象等.这些到底是啥意思呢?在正式进入Java世界前,笔者也尝试简单的聊一聊这块东西. 1.3.1低级语言 ...

随机推荐

  1. MyBatis源码解析(四)——DataSource数据源模块

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6634880.html 1.回顾 上一文中解读了MyBatis中的事务模块,其实事务操作无非 ...

  2. Back-off pulling image \"registry.access.redhat.com/rhel7/pod-infrastructure:latest

    Error syncing pod, skipping: failed to "StartContainer" for "POD" with ImagePull ...

  3. python的Web框架,Django自定义过滤器及标签

    代码布局 有的时候框架给的过滤器不够用,需要自定义一些过滤器,所以就需要我们自己来定义一些过滤器等 自定义代码放置的路径 某个app特用(独有)的 - app 目录下的 templatetags文件夹 ...

  4. Linux下的java虚拟机性能监控与故障处理命令

    java包中提供了很多监控JVM的工具类,作为java程序员必须得掌握常用的几个工具,下面是几个常用的JVM性能监控与故障处理工具的介绍与使用. 1.jstack 该命令用于生成当前时刻虚拟机的线程快 ...

  5. javase基础回顾(三) 动态代理

    动态代理是大型框架中经常用到的经典的技术之一,博主在理解spring的控制反转(依赖注入)的思想时回头着重复习了一下java的动态代理. 在说动态代理之前我们先简单说一说代理是用来干什么的,用于什么样 ...

  6. 设计shell脚本选项:getopt

    man 1 getopt翻译:https://www.cnblogs.com/f-ck-need-u/p/9757959.html 写shell脚本的时候,通过while.case.shift来设计脚 ...

  7. OJ:析构函数实现多态

    Description 下面程序的输出结果是: destructor B destructor A 请完整写出 class A. 限制条件:不得为 class A 编写构造函数. #include & ...

  8. 本人开源项目 Lu-Rpc

    Lu-Rpc 是个专为学习者准备的 RPC 框架, 初始架构非常简单, 可供初学者扩展和学习. Lu 可以认为是中文世界的撸, 即撸 Rpc--- 造个 Rpc 轮子. Lu-Rpc 架构图如下: L ...

  9. js_jquery_创建cookie有效期问题_时区问题

    用jquery设置Cookie过期的两种方式: $.cookie('名', '值', { expires: 过期时间(DateTime), path: "/", domain: w ...

  10. PHP学习笔记(3)-Zend Studio安装和汉化

    下载 因为FQ也慢,所以还是在百度软件中心下载快一些.地址:http://rj.baidu.com/soft/detail/15423.html?ald 因为下载不是最新版本,虽然因为强迫症FQ在官网 ...