使用 Lambda 表达式编写递归五:推导装配脑袋的 Fix

本文从λ演算入手,详细推导了不动点组合子的实现过程,并将其转化为C#代码,展示了如何通过数学变换得到简洁高效的递归函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Print Gallery
《Print Gallery》      作者:埃舍尔

本系列文章目录:

 

上一篇文章 最后提到, 装配脑袋 给出的 Fix 函数精简到极致:

1
2
3
static <int, long> Fix(<<int, long>, <int, long>> f) {
    return x => f(Fix(f))(x);
}

下面我们看下是怎么推导出的,从 λ 演算入手:

λ 演算

根据 β-归约 存在以下等式:

1
(λx.x(fix x)) f = f(fix f)

根据 不动点组合子 的定义:

1
f(fix f) = fix f

由以上两个等式得出:

1
(λx.x(fix x)) f = fix f

左右互换下:

1
fix f = (λx.x(fix x)) f

根据 c a == d a 时 c == d,得出:

1
fix = λx.x(fix x)

x(fix x) η-展开为 λn.x(fix x)n

1
fix = λx.λn.x(fix x)n

再变换一步:

1
fix = λx.λn.x(fix(x))(n)

转为 c# 代码

Fix 是不动点算子,根据前文的假定和推断它的类型是:Func<Func<Func<int, long>, Func<int, long>>, Func<int, long>>。(基于输入是 int,返回值是 long 的假定)

借助 前言及基础一文中总结出的小规律,可以写出:

1
<<<int, long>, <int, long>>, <int, long>> fix = x(fix(x))(n)

当然这个是编译不通过的,让我们把它改造成一个方法:

1
2
3
static <int, long> fix(<<int, long>, <int, long>> x) {
    return n => x(fix(x))(n);
}

将 int 改为 T、long 改为 TResult,抽象为泛型通用版本:

1
2
3
static <T, TResult> Fix<T, TResult>(<<T, TResult>, <T, TResult>> x) {
    return n => x(Fix(x))(n);
}

装配脑袋 的对比下,一样吧?好像参数名不同,没关系,把参数 x 更改为 f:

1
2
3
static <T, TResult> Fix<T, TResult>(<<T, TResult>, <T, TResult>> f) {
    return n => f(Fix(f))(n);
}

再比对下吧,一样了吧!(如果还是看不出来的话,把 n 更改 x 试试)

后记

其实呢,我是反推出来的,先从 装配脑袋 的 Fix 得出:

1
fix = λx.λn.x(fix(x))(n)

然后运用 η-变换λn.x(fix(x))(n) 简化为 x(fix(x))

1
fix = λx.x(fix(x))

两侧应用 f:

1
fix f = (λx.x(fix(x))) f

运用 β-归约

1
fix f = f(fix(f))

调整下:

1
fix(f) = f(fix(f))

正是不定点算子的定义。

把这个过程序反过来,就是本文 λ 演算 部分的内容。(如果用五个字评价我的这篇文章就是:事后诸葛亮。)

至于 装配脑袋 怎么推出的这个 FIX,我就无从得知了。

转载于:https://www.cnblogs.com/ldp615/archive/2013/04/11/recursive-lambda-expressions-5.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值