从零开始的函数式编程(2) —— Church Boolean 编码

[!quote] 关于λ表达式……
详见λ表达式

本文导出自Obsidian,可能存在格式偏差(例如链接、Callout等)
本文地址:https://www.cnblogs.com/oberon-zjt0806/p/18710283

目录
  • λ演算与λ代数
    • Church 编码
      • Church-Boolean 逻辑编码
        • 条件选择函数
        • 真与假 | True | False
        • 逻辑运算 | AND | OR | NOT
          • 合取 | 且 | AND
          • 析取 | 或 | OR
          • 反转 | 非 | NOT

λ演算与λ代数

上一整节我们利用λ符号体系构建了一套表达式系统,从这里开始,我们将正式开始利用这套系统进行代数应用,在进行演算之前,需要先利用符号体系构建一个代数运算系统

[!note] 命名终究只是命名
虽然我们之前使用了很多诸如(+ x 1)等等这样的形式,但它们只是我们定义的命名,所以无论是x还是+1,都只是一个记号而已,尽管我们根据以往的经验为这些符号赋予了某些我们所熟知的含义,但在当前的λ演算语境下,这些东西都还没定义过。

Church 编码

为了使λ演算能够具体应用到计算机和程序上,那么就意味着λ代数系统必须能够表示如下两种东西——

  • 数值(逻辑值、整数……)
  • 运算(算符、函数、操作……)

也就是说,这些东西要在λ演算中映射为λ表达式(使用表达式来表示)。

[!tip]
粗暴地说,Church编码就是一种把数值和运算编码为λ表达式的过程。

  • 但注意!Church编码并非唯一的编码方式,还有其他的编码方式,如Scott编码等。
  • Church编码的特点在于以数值表示为起点进行编码,并在基础上构建其他编码。

Church-Boolean 逻辑编码

[!abstract] Church-Boolean 编码汇总
为了方便查阅,这里将本节所有的编码定义列出来,正文是比较冗长的推导过程

DEF T = λx.λy.x
DEF F = λx.λy.y
DEF AND = λP.λQ.(P Q P)
DEF OR = λP.λQ.(P P Q)
DEF NOT = λP.λQ.(P F T)

首先我们需要通过Church编码构建出布尔运算系统。之所以先选择布尔代数,是因为布尔代数的结构简单,性质清晰,比较容易构建。

布尔代数(Boolean Algebra)包含的内容非常简单——

  • 布尔域\(\mathbb B\)中只包含两个元素\(\mathrm T\)\(\mathrm F\)
  • 支持三种基本运算\(\wedge\)\(\vee\)\(\neg\)
  • 运算对域封闭,且对于\(\wedge\)\(\vee\)都在\(\mathbb B\)上分别存在上界和下界

条件选择函数

在介绍Church-Boolean中的真假值前,我们先来考察条件选择函数,所谓条件选择函数就是下面这样的一个三元函数——

\[\operatorname{COND} (c,x,y) = \begin{cases} x, &c=\mathrm{T} \\ y, &c=\mathrm{F} \end{cases} \]

其中\(c\)是条件值,条件选择函数根据\(c\)的值就在\(x\)\(y\)中做出选择。可以发现,实际上,这个条件选择函数就对应了大多数编程语言中的三元运算符c ? x : y

我们将这个运算表示为IF-THEN-ELSE形式,可以表示为——

IF c THEN x ELSE y

可以发现这里分为3个子部——

  • IF c :判断c的条件;
  • THEN x:当c == true被满足时,选择x
  • ELSE y:上述条件不成立时,选择y

至此,我们可以把这三个部分抽象三个λ表达式

DEF cond = λc.λx.λy.(c x y)

由于真假值承载于c中,因此我们就利用c来对真假值进行编码。

真与假 | True | False

基于上面的想法,我们就能够通过Church编码定义出逻辑的真值T和假值F。讨论c的情况,根据定义,cond函数应当满足——

cond T x y => λc.λx.λy.(c x y) T x y => λx.λy.(T x y) x y => T x y => x
COND F x y => λc.λx.λy.(c x y) T x y => λx.λy.(F x y) x y => F x y => y

观察倒数两步归约,我们发现

  • 欲使T x y => x,那么就要求(T x) y必须发生η归约
    • 也就是说(T x)约束不生效,可以构建自由表达式(T x) == λb.x
      • 再脱去对x的运用,解开约束对,意味着我们需要引入一个新的约束变量
      • 于是我们就可以得到T == λa.λb.a
  • 欲使F x y => y,那么就要求(F x) y必须发生β归约
    • 而且更进一步地,(F x) == identity == λb.b
      • 类似地,再脱去对x的运用,解开约束对,引入另外的约束变量 λa
      • 于是F == λa.λb.b

[!tip] 反归约技巧
我们知道对于运用(f x)进行归约时,会将f中受约束的变量替换为参数x,例如(λa.a x) => x
那么,反过来对于已知的某一表达式x如果想要引入约束,或者把x作为参数提出来,那么就需要引入新的不冲突的约束命名,x => (λy.y x)
利用这种性质在已知(f x)的情况下可以展开f == λa.(f a)

^9b9507

经过α转换,将a更名为xb更名为y,我们就可以得出TF的定义

DEF T = λx.λy.x
DEF F = λx.λy.y

这种定义下的TF被映射为λ函数,因此可以作为一种条件选择函数来运用。

可以将上述定义代入表达式(c x y)通过[[λ表达式#归约 消解|归约]]来证明这个编码的正确性——

[!warning] 注意
归约化简时,不要忘记变量约束的右结合律函数运用的左结合律

(T T F) => (λx.λy.x λx.λy.x λx.λy.y)β|=> (λx.λy.(λx.λy.x) λx.λy.y) α|=> (λy.(λa.λb.a) λx.λy.y)η|=> (λa.λb.a)α|=> (λx.λy.x) => T(F T F) => (λx.λy.y λx.λy.x λx.λy.y)η|=> (λy.y λx.λy.y)β|=> (λx.λy.y) => F // alternatively, ==> identity F => F

逻辑运算 | AND | OR | NOT

接下来要对逻辑运算进行Church编码,这里先给出三种基本逻辑运算真值表——

A B A AND B A OR B NOT A
F F F F T
F T F T T
T F F T F
T T T T F

XOR、NAND之类的都可以在这三种基本运算的基础上组合出来。所以我们姑且只定义上面三个基本运算即可。

在正式开始之前,我们先考察一个东西——既然TF都被映射为函数,那么意味着他们可以相互作为函数和参数构成约束对进行运用,那么约束对能否归约,以及归约后的结果是什么,这里给出两个基本函数相互运用的归约结果——

T T => (λx.λy.x) (λx.λy.x) β|=> λy.(λx.λy.x) α|=> λy.(λa.λb.a) => λy.T
T F => (λx.λy.x) (λx.λy.y) β|=> λy.(λx.λy.y) α|=> λy.(λa.λb.b) => λy.F
[i.e.]  T P => λy.PT P Q => P
F T => (λx.λy.y) (λx.λy.x) η|=> λy.y => identity
F F => (λx.λy.y) (λx.λy.y) η|=> λy.y => identity
[i.e.]  F P => λy.y => identityF P Q => Q

^6686a8

需要注意的是,归约结果中的TF中的xy和外层约束的y没有任何关系,而是出现了命名冲突(如果要展开那么需要进行一次α转换),所以实际上这里的T自由表达式。在上面的归约过程中,我们可以归纳出如下性质——

  • 如果以T作为函数运用(T P)(其中P,Q in {T, F}),那么会通过β归约x替换为P从而得到一个新的函子λy.P,且Q不受y的约束。
    • 把这个结果λy.P再作为函数并传入参数Q构成约束对,那么下一步将发生η归约,消去λy约束,最终只会剩下PT P Q => λy.Q Q => Q
  • 如果以F作为函数,那么由于Fλx没有进行约束,所以先进行η归约,消去λx约束,最终总会留下λy.y,好巧不巧地,这正好是恒等函数identity
    • 那接下来就很清晰了,如果再传入参数Q,由于identity的性质,或者直接通过β归约替换,则会只留下后面的这个参数PF P Q => identity Q => λy.y Q => Q

![Church T F.svg]]

完成上面的工作有助于我们通过Church编码来定义逻辑运算

合取 | 且 | AND

首先来看一下合取运算,合取的要求是只有当两个输入均为T,才可以被归约为T,其他情形全部为F——

AND T Q => QAND T => (F P)
AND F Q => FAND F => (T P)

观察上面的形式,对于AND P Q ,我们可以做出如下归纳

  • P==T时,AND P Q => AND T Q => Q
    • 这种情况对应[[#^6686a8|上面]]的(F P),于是AND T Q => F P Q
  • P==F时,AND P Q => AND F Q => F
    • 这种情况对应[[#^6686a8|上面]]的(T P),于是AND F Q => T P Q

[!question] 麻烦了
目前我们归纳出的结论是AND P == (NOT P) P,然而问题在于我们还没有定义过NOT, 这怎么办呢?

[!tip] 还好
AND满足交换律,也就是说应当有AND P Q == AND Q P

通过交换律将AND P Q换成AND Q P,不影响先前的结论,除了讨论对象此时从AND P变成了AND Q

AND Q P[P:=T] => QAND Q => (T Q)
AND Q P[P:=F] => AND Q F => FAND Q => (F Q)
AND Q => (P Q)

终于我们可以得出AND的Church编码——

DEF AND = λP.λQ.(P Q P)
析取 | 或 | OR

与合取类似,析取也具有交换律,并且我们也可以效仿刚才的过程完成OR的定义,首先考察

OR T Q => TOR T => (T P)
OR F Q => QOR F => (F P)

这次无需交换律了,直接替换就能够得到OR的定义——

DEF OR = λP.λQ.(P P Q)
反转 | 非 | NOT

NOT比较特别,因为NOT是一个一元运算,需要单独讨论。

NOT T == NOT λx.λy.x => F == λx.λy.y
NOT F == NOT λx.λy.y => T == λx.λy.x

简单来说,输入的参数是选择其中一个,那么NOT的输出总是选择另外一个。考虑到真假值TF均是通过cond定义的,那么,如果直接反转cond定义是不是就能够得到相反的输出?

cond == λc.λx.λy.(c x y)
ncond == λc.λx.λy.(c y x)

于是我们得到了一种NOT的定义形式

DEF NOT1 = λP.λx.λy.(P y x)

这个形式看起来比较底层,我们能不能利用已有的逻辑值来定义呢?

再次考察 cond P ——

cond P => λP.λx.λy.(P x y) P => λx.λy.(P x y)

如果考虑将x替换为Fy替换为T,也能达成同样的效果,于是我们进一步提供参数——

cond P F T => λx.λy.(P x y) F T => P F T

于是我们得到了另一种NOT的定义——

DEF NOT2 = λP.(P F T)

通过归约可证明,NOT1 <=> NOT2

至此,两个逻辑值和三个基本逻辑运算被定义完毕,Church-Boolean编码完成,可以使用λ表达式进行逻辑演算了。

[!question] 思考
不妨试试用类似的方式定义出更多的逻辑运算,例如异或XOR、与非NAND等……

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

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

相关文章

踩坑记录-二分搜索的不同情况

二分搜索的不同情况 二分搜索可以用来查找满足条件的值,但是满足条件的值可能只有1个,也可能有多个。比如查找1的索引,对于【1,1,2,2】来说,就有2个。一般要求的就是:满足条件最大值/满足条件最小值。 二分搜索详细介绍可以参考:https://programmercarl.com/0704.二分…

《ESP32-S3使用指南—IDF版 V1.6》第五章 搭建开发环境

第五章 搭建开发环境 1)实验平台:正点原子DNESP32S3开发板 2)章节摘自【正点原子】ESP32-S3使用指南—IDF版 V1.6 3)购买链接:https://detail.tmall.com/item.htm?&id=768499342659 4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/esp32/AT…

内测之家介绍

内测之家:助力应用开发与迭代的专业平台内测之家是一款功能强大且全面的应用内测与管理平台,专为 iOS 和 Android 开发者打造,旨在为他们提供便捷高效、安全可靠的一站式服务。无论是从资源安全到传输安全,还是从数据保护到应用管理、统计分析,内测之家都展现出卓越的能力…

如何用好 AI 编码工具,让通义灵码帮你做更多工作

通义灵码,是阿里云与通义实验室联合打造的智能编码辅助工具,提供行级/函数级实时续写、自然语言生成代码、单元测试生成、代码优化、注释生成、代码解释、研发智能问答、异常报错排查等能力,提供代码智能生成、研发智能问答能力。通义灵码,是阿里云与通义实验室联合打造的智…

用EXPLAIN检查SQL是否有慢查询

先看下面两张图: 图一 图二 同样都是查询语句,图一的条件采用的是模糊匹配,产生了全表扫面(type:a…

【触想智能】工控一体机在机械臂上应用的四大优势

随着工业自动化的发展,机械臂已成为现代制造业中不可或缺的一部分。为了使机械臂能够高效、精确地执行各种任务,工控一体机的应用变得越来越广泛。触想工控一体机在机械臂上的应用工控一体机是一种集计算、控制和通信于一体的高性能工业计算机,它不仅具备强大的计算能力,还…

CTFShow-Web167:

CTFShow-Web167:.htaccess利用 <button type="button" class="layui-btn" id="upload" lay-data="{url: upload.php, accept: images,exts:jpg}"> 限制上传类型为jpg文件 题目提示httpd,并且404页面返回Apache/2.4.25 (Debian…

对极几何(Epipolar Geometry)总结

为什么stereo很有用? 当我们需要从单一视角恢复结构时,我们的信息来源有以下几种: \(\bullet\) 从标定架可以获取标定架的位置 / 姿态以及相机内参 K。 \(\bullet\) 从无穷远点和线,加上正交的线和平面等信息,可以获取场景的结构和相机内参 K 。 但是由于内在歧义性,从单…

P10451 做题随笔

Solution 题意 原题链接 对每组数据,给定两颗用 01 序列描述的树,描述规则如下:按照 \(\text{DFS}\) 序进行遍历; 若序列中某位为 0,表示除根节点外的节点进栈;为 1 则表示出栈。要求判断一树是否可以通过交换子树的方式变换成另一子树(对于本题,即两树同构)。 分析 1…

堆排序--代码实现

本文主要说明代码编写思路和具体代码,下面的博文讲的比较全面 参考文章:https://www.cnblogs.com/jingmoxukong/p/4303826.html代码思路(以大根堆为例) 堆排一共分2个阶段:1. 创建一个大根堆 2.交换堆顶和堆尾元素,获取到堆顶元素,并重新维护大根堆 第一个阶段的思路: 从…

贪心tricks总结

贪心题一般没有什么技巧,多做题积累经验。 对于结论或策略,大胆猜想,小心求证,注意使用数据结构优化/结合其他算法。 一般类贪心 主要是证明贪心的正确性。 H. Fight Against Monsters 先用二分求出每个怪需要打的次数。 问题转化为一个排列的答案是 \[\sum_{i=1}^{n} \sum…