先约定几个记号:

  1. 定义用一个冒号加等号表示“:=”,
  2. 表达式全等用两个等号表示“==”,
  3. 归约意义上的相等用一个等号表示“=”,“==”蕴涵“=”。

由于lambda演算不能定义符号,所以像这样的递归定义是不能被求值的:

f := (lambda () (f))

如何在lambda验算中实现递归?从最简单的递归函数开始。希望能带来一些启发。

寻找 \(\Omega\)

首先,我们的目标是找出一个无限循环的lambda表达式。另外额外要求这个表达式最短。

一个lambda表达式只可能有三种形式:

  1. 符号(x, y, z等等),
  2. 函数(如(lambda (x) x)),
  3. 应用(英文application,即函数调用,如调用函数func,参数x写作:(func x))。

第1、2种形式又称为值(value),因为他们不能被归约了。一个无限循环的表达式只能是第3种形式:

(exp1 exp2)

考虑exp1。exp1是个函数,或者能被归约成函数的应用。如果exp1是应用,那么exp1要么会归约到一个函数,要么无限循环。如果exp1无限循环,就和我们的最短假设矛盾,所以exp1会归约到一个函数。如果exp1会归约到一个函数,那么和exp1是一个函数没什么区别。简单考虑,假设exp1是个函数:

exp1 := (lambda (x) body)

那么

(exp1 exp2)
== ((lambda (x) body) exp2)
= body[x <- exp2]

为了让表达式无限循环,我们希望:

(exp1 exp2) == body[x <- exp2]

所以body应该是个应用:

body := (subexp1 subexp2)
<=> (exp1 exp2) == (subexp1[x <- exp2] subexp2[x <- exp2])
<=> exp1 == subexp1[x <- exp2]  and  exp2 == subexp2[x <- exp2]

从最后一个条件可以推出:

exp2 == subexp2[x <- exp2]  <=>  subexp2 == x

另外还能看出,表达式exp1中包含了一个和exp2相等的子表达式。满足这个条件的最简单的exp1就是和exp2相等的exp1。

exp1 == exp2
<=> subexp1[x <- exp2] == exp2
<=> subexp1 == x

综合几个条件:

exp1
== (lambda (x) body)
== (lambda (x) (subexp1 subexp2))
== (lambda (x) (x x)) exp2
== exp1
== (lambda (x) (x x))

于是我们要找的无限循环的表达式是:

((lambda (x) (x x)) (lambda (x) (x x)))

这个表达式就是lambda演算里的\(\Omega\)表达式。\(\Omega\)表达式循环的关键在于(f f)形式的表达式,即自己应用到自己。\(\Omega\)包含了三个(f f)形式的表达式。

寻找 Y combinator

一般来说,递归函数长这个样子:

target := (lambda args body[target])

body[target]的意思是一个包含target这个符号的表达式,args表示这是一个任意个数参数的函数。在这个表达式中,target是一个无约束的符号。一个包含无约束符号的表达式不能被求值。Lambda演算中约束一个符号的方法是用lambda关键字,为了让target变成约束符号,我们改写表达式:

p := (lambda (target)
(lambda args body[target]))
   = (lambda (f)
       (lambda args body[f]))

为了避免混淆,上面将target改名成f(\(\alpha\)归约),target仍然表示我们想要的递归函数的表达式。target和p有如下关系:

(p target)
= (lambda args body[target])
= target

顺便一提,可以看出我们在寻找的target是p的一个不动点。

接下来考虑如何找target。记得寻找\(\Omega\)的启发吗?我们猜测target是一个(g g)形式的表达式:

target == (g g)
=>
(p target) = target
<=> (p (g g)) = (g g)
<=> (g g) = (p (g g)) = ((lambda (x) (p (x x))) g)

根据最后一个条件可以看出:

g := (lambda (x) (p (x x)))
=>
target == (g g)
== ((lambda (x) (p (x x)))
(lambda (x) (p (x x))))

把p提取到参数位置:

target =
((lambda (f)
((lambda (x) (f (x x)))
(lambda (x) (f (x x))))) p) Y :=
(lambda (f)
((lambda (x) (f (x x)))
(lambda (x) (f (x x))))) target = (Y p)

这个Y就是Y combinator。

用 Y combinator 推导出 \(\Omega\)

最简单的无限循环函数是:

f := (lambda () (f))

利用Y combinator来生成这个函数的lambda表达式看看会得到什么?

(Y (lambda (f) (lambda () (f))))
= ((lambda (x) ((lambda (f) (lambda () (f))) (x x)))
(lambda (x) ((lambda (f) (lambda () (f))) (x x))))
= ((lambda (x) (lambda () ((x x))))
(lambda (x) (lambda () ((x x)))))

呼,要对齐这么多括弧真不容易。注意到(\(\eta\)归约):

(lambda () ((x x))) = (x x)

所以:

(Y (lambda (f) (lambda () (f))))
= ((lambda (x) (x x)) (lambda (x) (x x)))

如何猜出 Y combinator的更多相关文章

  1. Y Combinator

    常见的例子 阶乘函数: fact = (a) -> if a > 0 then a * fact(a - 1) else 1 问题的提出 如上,在fact函数中调用了fact本身,无法使用 ...

  2. 利用Python完成一个小游戏:随机挑选一个单词,并对其进行乱序,玩家要猜出原始单词

    一 Python的概述以及游戏的内容 Python是一种功能强大且易于使用的编程语言,更接近人类语言,以至于人们都说它是“以思考的速度编程”:Python具备现代编程语言所应具备的一切功能:Pytho ...

  3. YC(Y Combinator)斯坦福大学《如何创业》课程要点记录(粗糙)

    20节课程,每节都是干货满满,时常听说理论无用,但是好的理论,绝对能帮助你少走一些弯路. YC简介: Y Combinator成立于2005年,是美国著名创业孵化器,Y Combinator扶持初创企 ...

  4. java基础小练习,1-打印一百次(1~10)的随机数,2-固定一个随机数(1~100),然后猜出他,3-定义以指定格式打印集合(ArrayList类型作为参数),使用{}括起来,使用@代替,分隔每个元素

    推荐自己码一下,可以使用别的方法,面向对象,不需要注重过程 /* 题目:我需要打印一百次(1~10)的随机数 */ import java.util.Random; public class demo ...

  5. y combinator 做的一个调查_可以学习一下

    RoR: 在网络营运平台企业中,RoR站稳使用率第一的位置.其用户包括:ZenPayroll (人力资源).Asile50 (零售平台).BackerKit (众筹平台).Rainforest (QA ...

  6. 使用Python的yield实现流计算模式

    首先先提一下上一篇<如何猜出Y combinator>中用的方法太复杂了.其实在Lambda演算中实现递归的思想很简单,就是函数把自己作为第一个参数传入函数,然后后面就是简单的Lambda ...

  7. python 游戏(猜数字)

    1. 构造猜数字核心函数 import random def guess_core(guess_min,guess_max,guess_counrt): '''猜数字核心判断函数 :param gue ...

  8. CF 576A 猜数

    A给出一个数x,B每次猜一个y,A回答B,x是否可以被y整除,求出要猜的最小次数和需要猜的数. 枚举每个素数p,可以知道如果p^k<=n,则p^k一定需要选 Sample test(s)inpu ...

  9. LightOJ 1244 - Tiles 猜递推+矩阵快速幂

    http://www.lightoj.com/volume_showproblem.php?problem=1244 题意:给出六种积木,不能旋转,翻转,问填充2XN的格子有几种方法.\(N < ...

随机推荐

  1. 杭州富阳场口科目四考试公交路线(西溪北苑->场口)

    从西溪北苑出发,时间充裕,比较悠闲,打算坐公交前往,也打算做下科目四模拟题,顺便欣赏沿途的风景(去的时候需要看题目,回来的时候可以放松,哈哈哈),路线如下. 早上7点半出发,出去吃个早餐,步行到文一社 ...

  2. leetcode — swap-nodes-in-pairs

    /** * Source : https://oj.leetcode.com/problems/swap-nodes-in-pairs/ * * Created by lverpeng on 2017 ...

  3. 3分钟看完Java 8——史上最强Java 8新特性总结之第一篇 函数式编程基础

    目录 · 行为参数化 · Lambda表达式 · 概况 · 函数式接口 · 类型推断 · 使用外层变量 · 方法引用 · 复合Lambda表达式 行为参数化 1. 理解函数式编程要先理解行为参数化. ...

  4. Angular2入门:TypeScript的类型 - 数组解构

  5. 搭建前端监控系统(四)Js截图上报篇

    ===================================================================== 前端监控系统: DEMO地址  GIT代码仓库地址 ==== ...

  6. C# ABP源码详解 之 BackgroundJob,后台工作(一)

    本文归属作者所有,转发请注明本文链接. 1. 前言 ABP的BackgroundJob,用来处理耗时的操作.比如客户端上传文件,我们要把文件(Excel)做处理,这耗时的操作我们应该放到后台工作者去做 ...

  7. CheckBox-复选框-删除-选中行

    <!--删除选中的行,利用oTable.deleteRow(i)方法--> <!DOCTYPE html> <html xmlns="http://www.w3 ...

  8. Redis基础认识及常用命令使用(一)--转载

    Redis简介 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合)和zset(有序集 ...

  9. Java基础——Oracle(三)

    一.Oracle内部结构 1.表空间 表空间是数据库的逻辑划分,一个表空间只属于一个数据库,每个表空间由一个或多个数据文件组成,表空间中其他的逻辑结构的数据存储在这些数据文件中,一般oracle系统安 ...

  10. Python 字符编码简记

    名称 说明 ASCII 只能存英文和拉丁字符,一个字符占一个字节,8位. ASCII 码是不支持中文的,支持中文的第一张表是 GB2312 GB2312 支持中文,收录了 7445个字符 GBK1.0 ...