递归。哦,递归。 递归在计算机科学中的重要性不言而喻。 递归就像女人,即令人烦恼,又无法抛弃。

先上个例子,这个例子里的函数double输入一个非负整数$n$,输出$2n$。 \[ {double} = \lambda n.({if} \; ({iszero} \; n) \; 0 \; (+ \; 2 \; ({double} \; (- \; n \; 1)))) \]

现在的问题是,这个递归函数在我们的语言里没法直接定义。 我说的直接定义是指像这个用let表达式: \[ ({let} \; {double} \; \lambda n.({if} \; ({iszero} \; n) \; 0 \; (+ \; 2 \; ({double} \; (- \; n \; 1)))) \; M) \] 把这个let表达式宏展开会看得更清楚些: \[ (\lambda {double}.M \; \lambda n.({if} \; ({iszero} \; n) \; 0 \; (+ \; 2 \; ({double} \; (- \; n \; 1))))) \] $\lambda n.({if} \; ({iszero} \; n) \; 0 \; (+ \; 2 \; ({double} \; (- \; n \; 1))))$ 里的double是个自由变量。 解释器求值到这里的时候,根本不知道double指的是什么函数。

如何构造递归函数

获得递归的一个关键是如何在函数体中找到自己(结合一开始的比喻,这句话好像蕴含了其他意义深远的意思)。 一个简单的方法是在double上增加一个参数(一般就是第一个参数),把自己传入参数。 把这个修改后的函数叫做mkdouble1吧。 先不考虑mkdouble1的定义,先观察mkdouble1的行为。 因为调用mkdouble1要把自己作为第一个参数传入,所以调用递归函数应该这样写: \[ (({mkdouble1} \; {mkdouble1}) \; n) \] 也就是说,double就是$({mkdouble1} \; {mkdouble1})$。 \begin{eqnarray*}   {double} &=& ({mkdouble1} \; {mkdouble1}) \\               &=& (\lambda v.(v \; v) \; {mkdouble1}) \end{eqnarray*} 最后一步变换是为了让mkdouble1只出现一次。

现在来考虑mkdouble1的定义。 在double上增加一个参数$f$: \[ {mkdouble1} = \lambda f.\lambda n.({if} \; ({iszero} \; n) \; 0 \; (+ \; 2 \; ({double} \; (- \; n \; 1)))) \] 函数调用的时候传入参数$f$的是mkdouble1。 也就是说$f$代表的是mkdouble1。 因此,函数体里递归调用的double用$(f \; f)$替换: \[ {mkdouble1} = \lambda f.\lambda n.({if} \; ({iszero} \; n) \; 0 \; (+ \; 2 \; ((f \; f) \; (- \; n \; 1)))) \] 所以double的定义是: \[ {double} = (\lambda v.(v \; v) \; \lambda f.\lambda n.({if} \; ({iszero} \; n) \; 0 \; (+ \; 2 \; ((f \; f) \; (- \; n \; 1))))) \] 这个定义可以用之前实现的解释器运行。 测试一下:

'(let double ((lambda v (v v))
(lambda f
(lambda n
(if (iszero n) 0 (+ 2 ((f f) (- n 1)))))))
(double 4)) >> 8

Y组合子

这一小节比较理论,知道个思路就行了。所以我就随便写写。 好学的人可以自己查资料(Programming Languages and Lambda Calculi, The Little Schemer)。

mkdouble1并不能让人很满意,因为它不优雅(都是时臣的错)。 mkdouble1递归调用的地方用的是$(f \; f)$,而比较好看比较符合直觉的应该只有一个$f$。 定义这个所谓的比较好看的函数mkdouble如下: \[ {mkdouble} = \lambda f.\lambda n.({if} \; ({iszero} \; n) \; 0 \; (+ \; 2 \; (f \; (- \; n \; 1)))) \] 我们希望能从mkdouble得到递归函数double。 这是能做到的。只要在利用mkdouble1的double定义上做几个简单的推导就行了: \begin{eqnarray*} {double} &=& (\lambda v.(v \; v) \; \lambda f.\lambda n.({if} \; ({iszero} \; n) \; 0 \; (+ \; 2 \; ((f \; f) \; (- \; n \; 1))))) \\ &=& (\lambda v.(v \; v) \; \lambda f.(\lambda f.\lambda n.({if} \; ({iszero} \; n) \; 0 \; (+ \; 2 \; (f \; (- \; n \; 1)))) \; (f \; f))) \\ &=& (\lambda x.(\lambda v.(v \; v) \; \lambda f.(x \; (f \; f))) \; \lambda f.\lambda n.({if} \; ({iszero} \; n) \; 0 \; (+ \; 2 \; (f \; (- \; n \; 1))))) \\ &=& (\lambda x.(\lambda v.(v \; v) \; \lambda f.(x \; (f \; f))) \; {mkdouble}) \end{eqnarray*}

$\lambda x.(\lambda v.(v \; v) \; \lambda f.(x \; (f \; f)))$被称作Y组合子,记为Y。 然后有: \[ {double} = ({Y} \; {mkdouble}) \]

Y组合子可以用来构造递归函数。 不过上面的定义在call-by-value的调用方式下会进入无限循环。 具体原因就不讲了,只讲结论:问题出在$(f \; f)$这里,对$(f \; f)$做一个$\eta$逆归约就行了。 修改后的Y组合子记为${Y}_{v}$: \[ {Y}_{v} = \lambda x.(\lambda v.(v \; v) \; \lambda f.(x \; (\lambda u.((f \; f) \; u)) \]

测试一下。 Call-by-value的测试:

'(let Y (lambda x
((lambda v (v v))
(lambda f (x (lambda u ((f f) u))))))
(let mkdouble (lambda f
(lambda n
(if (iszero n)
0
(+ 2 (f (- n 1))))))
((Y mkdouble) 4))) >> 8

Call-by-name的测试:

'(let Y (lambda x
((lambda v (v v))
(lambda f (x (f f)))))
(let mkdouble (lambda f
(lambda n
(if (iszero n)
0
(+ 2 (f (- n 1))))))
((Y mkdouble) 4))) >> 8

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

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

    这个系列有个显著的特点,那就是标题越来越长.忽然发现今天是读书节,读书节多读书. ==下面是没有意义的一段话============================================== ...

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

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

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

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

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

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

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

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

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

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

  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. LINUX服务器搭建和常用配置介绍

    服务器搭建 : 搭建私有CA服务器 : http://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_linux_011_ca.html搭建samba服务器 : h ...

  2. MyEclipse2017破解设置与maven项目搭建

    下载 版本:MyEclipse2017 Stable 2.0 百度网盘链接:https://pan.baidu.com/s/1vpIMKq9FfMMbhXzkmft_8A 密码:xfbv myecli ...

  3. 教你用Python创建瀑布图

    介绍 对于绘制某些类型的数据来说,瀑布图是一种十分有用的工具.不足为奇的是,我们可以使用Pandas和matplotlib创建一个可重复的瀑布图. 在往下进行之前,我想先告诉大家我指代的是哪种类型的图 ...

  4. Java——static关键字

    前言 static关键字算是Java中比较复杂的关键字之一,它可以修饰变量.方法.类以及代码块.下面将介绍static的具体使用. static引入的目的 static的作用 static修饰变量 s ...

  5. MySQL中间件之ProxySQL(9):ProxySQL的查询缓存功能

    返回ProxySQL系列文章:http://www.cnblogs.com/f-ck-need-u/p/7586194.html ProxySQL支持查询缓存的功能,可以将后端返回的结果集缓存在自己的 ...

  6. python按引用赋值和深、浅拷贝

    按引用赋值而不是拷贝副本 在python中,无论是直接的变量赋值,还是参数传递,都是按照引用进行赋值的. 在计算机语言中,有两种赋值方式:按引用赋值.按值赋值.其中按引用赋值也常称为按指针传值(当然, ...

  7. 翻译:DECLARE Variable(已提交到MariaDB官方手册)

    本文为mariadb官方手册:DECLARE Variable的译文. 原文:https://mariadb.com/kb/en/library/declare-variable/我提交到MariaD ...

  8. QT 完美实现圆形按钮

    QT 版本:5.6.0 官方的按钮有些普通,如果我们想要换成自己喜欢的按钮而却无从下手,那么请继续往下阅读(皮一下). 首先,可以在网络上搜索一下自己喜欢的按钮图形(或者可以自行绘制),我以下面的图形 ...

  9. Jquery里的特定小技巧

    jQuery 动态设置样式:                      https://blog.csdn.net/xiaoyuncc/article/details/70854925 jquery如 ...

  10. 《Office 365开发入门指南》上市说明和读者服务

    写在最开始的话 拙作<Office 365开发入门指南>上周开始已经正式在各大书店.在线商城上市,欢迎对Office 365的开发.生态感兴趣的开发者.项目经理.产品经理参考本书,全面了解 ...