代码改变世界

使用 Lambda 表达式编写递归四:实现 Θ 组合子

  鹤冲天  阅读(3405)  评论(4编辑  收藏  举报
Fish and Scales
《Fish and Scales》      作者:埃舍尔

本系列文章目录:

 

上一篇文章 我们实现 Y 组合子,这篇文章讨论 Θ 组合子的实现。(Θ 读 Theta,希腊语第八个字母,小写为 θ。)

继续使用前文中的类型假定,假定递归函数:

  • 参数为 int;
  • 返回值为 long。

Θ 组合子

Θ 组合子也是一个常见不动点组合子,由 阿兰·图灵 发现,也称为图灵不动点组合子:

1
Θ = (λx.λy.(y(x x y))) (λx.λy.(y(x x y)))

传值调用形式为((x x y) η-展开为 λz. x x y z):

1
Θv = (λx.λy.(y(λz. x x y z))) (λx.λy.(y(λz. x x y z)))

不过我还是喜欢把 y (x x y) η-展开为: λn.(y (x x y) n),得出:

1
Θ = (λx.λy.λn.(y(x x y) n)) (λx.λy.λn.(y(x x y) n))

定义一个中间变量 h,令:

1
h = λx.λy.λn.(y(x x y)n)

则:

1
Θ = h h 
Θ = h(h)

实现 h

确定 h 的类型

根据前文的推断,Θ 的类型为 Func<Func<Func<int, long>, Func<int, long>>, Func<int, long>> , 这也是 h 返回值的类型。

h 可调用自身 h(h),需要用到 上一篇文章 用的 SelfApplicable<TResult>委托:

1
delgate TResult SelfApplicable<TResult>(SelfApplicable<TResult> self);

h 的类型为:SelfApplicable<Func<Func<Func<int, long>, Func<int, long>>, Func<int, long>>>。

实现 h

对 h 作一步简单变换:

1
2
h = λx.λy.λn.(y(x x y)n)
h = λx.λy.λn.(y(x(x)(y))(n)

使用本系列第一篇文章总结出的规律,可写出:

1
SelfApplicable<Func<Func<Func<int, long>, Func<int, long>>, Func<int, long>>> h = x => y => n => y(x(x)(y))(n);

实现 Θ

根据 Θ 的简单式:

1
Θ = h(h)

可写出:

1
Func<Func<Func<int, long>, Func<int, long>>, Func<int, long>> Θ = h(h);

与 h 的代码放在一块:

1
2
SelfApplicable<Func<Func<Func<int, long>, Func<int, long>>, Func<int, long>>> h = x => y => n => y(x(x)(y))(n);
Func<Func<Func<int, long>, Func<int, long>>, Func<int, long>> Θ = h(h);

还记得 上一篇文章 最后给出的代吗?(出处:http://blogs.msdn.com/b/madst/archive/2007/05/11/recursive-lambda-expressions.aspx

比较下,看看是不是一回事的。

调用下试试:

1
2
var factorial = Θ(f => x => x == 0 ? 1 : x * f(x - 1));
var result = factorial(5);  // 120

封装 Θ

1
2
3
4
5
6
7
8
9
10
public class ThetaCombinator {
    public static Func<TInput, TResult> Fix<TInput, TResult>(Func<Func<TInput, TResult>, Func<TInput, TResult>> f) {
        return Theta<TInput, TResult>.Fix(f);
    }
    static class Theta<TInput, TResult> {
        delegate T SelfApplicable<T>(SelfApplicable<T> self);
        static readonly SelfApplicable<Func<Func<Func<TInput, TResult>, Func<TInput, TResult>>, Func<TInput, TResult>>> h = x => y => n => y(x(x)(y))(n);
        public static readonly Func<Func<Func<TInput, TResult>, Func<TInput, TResult>>, Func<TInput, TResult>> Fix = h(h);
    }
}

调用示例代码:

1
2
3
4
5
var factorial = ThetaCombinator.Fix<int, int>(f => n => n == 0 ? 1 : n * f(n - 1));
var result1 = factorial(5);   // 120

var fibonacci = ThetaCombinator.Fix<int, int>(f => n => n < 2 ? n : f(n - 1) + f(n - 2));
var result2 = fibonacci(5);   // 5

后记

http://blogs.msdn.com/b/madst/archive/2007/05/11/recursive-lambda-expressions.aspx 文中也给出了下面一种简单到极致的写法:

1
2
3
Func<T, T> Fix<T>(Func<Func<T,T>, Func<T,T>> F) {
  return t => F(Fix(F))(t);
}

增加一个泛型参数并修改下参数的名称,得出:

1
Func<T, TResult> Fix<T, TResult>(Func<Func<T,TResult>, Func<T,TResult>> f) {
  return t => f(Fix(f))(t);
}

便是 装配脑袋 给出的写法。下一篇文章 中将说明这个是如何编导出的。

编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
点击右上角即可分享
微信分享提示