大到可以小说的Y组合子(二)
问:上一回,你在最后曾提到“抽象性不足”,这话怎么说?
答:试想,如果现在需要实现一个其它的递归(比如:Fibonacci),就必须把之前的模式从头套一遍,然后通过fib_maker(fib_maker)来返回一个fib函数。可见,这个产生递归过程的“接口”让用户相当不舒服。
问:嗯,fib_maker(fib_maker)这种形式看起来的确不怎么舒服,那又如何对其进行抽象,以得到更好的接口呢?
答:这里,有两条路可以走。其一,就是对fact_maker(fact_maker)进一步抽象,便可得到传说中的Y组合子;其二,则是否定之前所为,走一条能反应Y组合子本质的路。二选一,你走那条?
问:???...
答:那就走第二条路吧,至于如何抽象出Y组合子,放到第(四)章谈。 在开始今天的讨论之前,我们先来定义一些委托类型,以方便后文的表达:
//C#
delegate T OuroborosFunc<T>(OuroborosFunc<T> self);
class Y<T, TResult>
{
public delegate TResult Func1(T p);
public delegate Func1 Func2(Func1 f1);
public delegate Func1 Func3(Func2 f2);
... ...
}
这样就可以得到(==表示类型等价,并且在下文使用时省略Y<int,int>限定):
//C#伪码
Func1 == Func<int,int>
Func2 == Func<Func1,Func1> == Func<Func<int,int>,Func<int,int>>
Func3 == Func<Func2,Func1> == Func<Func<Func<int,int>,Func<int,int>>,Func<int,int>>
OK,让我们回忆一下上一章的讨论:
//C#
OuroborosFunc<Func<int, int>> fact_maker =
self => x => x == 0 ? 1 : x * self(self)(x - 1);
为了传递fact_maker给self,我们在Lambda的主体内做了让步——以self(self)的形式来表达fact递归。试想,如果我们不打算传入fact_maker,而是直接传入一个能表达递归的Lambda(类型为Func<int,int>,即Func1),且同时能返回这个Lambda,那么,将得到如下结果:
//C#
Func2 fact_seed = fact => x => x == 0 ? 1 : x * fact(x - 1);
对此,我们来做一个形式化的描述,令F为fact_seed,f为那个表达递归的Lambda,则:F(f) = f, 也就是说f是F的一个不动点。所谓函数的不动点,即函数作用于之得到的仍是其本身的那个点,如:若F(x)=x*x, 则1则是F的一个不动点,因为F(1)=1. 到这里,大家应该可以看出来了,我们期望得到的那个匿名递归fact,其实正是fact_seed的不动点。再试想,如果我们有这么一个函数Fix,它的作用就是求某个函数的不动点,那么,所要求的fact即为Fix(fact_seed). 终于,理想的接口产生了,那就是只要把诸如fact_seed这样的Lambda交给Fix,得到的不动点就是问题的解。
//C#
Func1 fact = Fix(fact_seed);
//或者
Func1 fact = Fix(fact => x => x == 0 ? 1 : x * fact(x - 1));
问:那这个Fix到底是什么呢?
答:根据以上伪码,Fix的类型已然昭昭:Func3,再根据其功能,似乎应该是这样一个高阶函数:
//C#伪码
Func3 Fix = f=>(f的不动点);
而f的不动点怎么表示呢?Fix(f),这样代入上式其实什么也没有做。既然Fix(f)是f的不动点,那Fix(f)=f(Fix(f))=f(f(Fix(f)))...都应该是f的不动点。于是得到(当然也可以取f(f(Fix(f))),我没测试过,应该没问题):
//C#
Func3 Fix = f=>f(Fix(f));
这里需要稍加甄别,对于lazy求值的函数语言来说,上式的定义的确没问题,但是C#是eager求值的,所以Fix(some_f)表答的意思是——将f应用于Fix(some_f)的求值,而Fix(some_f)的求值势必导致新一轮的求值,直至堆栈溢出,可以这样修正这个问题:
//C#
Func3 Fix = f=>f(x=>Fix(f)(x));
现在可以来测试一下结果了:
//C#
int result = Fix(fact => x => x == 0 ? 1 : x * fact(x - 1))(5);
Console.WriteLine(result); //120
依然非常棒,得到了正确的结果。
问:慢着...这里似乎有问题啊,我们的目的是完成一个匿名的递归,那个fact_seed的确是可以做到匿名,但是Fix却是一个普通的递归啊!
答:Good Question! 那你想想应该怎么解决这个问题呢?
问:嗯,让我好好想想,再听你下回分解吧。
答:待续...
大到可以小说的Y组合子(二)的更多相关文章
- 大到可以小说的Y组合子(一)
问:上回乱扯淡了一通,这回该讲正题了吧. 答:OK. 先来列举一些我参考过,并从中受到启发的文章. (1.)老赵的一篇文章:使用Lambda表达式编写递归函数 (2.)装配脑袋的两篇文章:VS2008 ...
- 大到可以小说的Y组合子(三)
答:关于Fix的问题你fix了吗? 问:慢着,让我想想,上次留下个什么问题来着?是说我们有了一个求不动点的函数Fix,但Fix却是显式递归的,是吧? 答:有劳你还记的这个问题. 问:Fix的参与背离了 ...
- 大到可以小说的Y组合子(零)
问:啊!我想要一个匿名的递归… 答:Y(音同Why)… … … 问:作为一位命令式语言的使用者,为什么会突然折腾起Y组合子呢? 答:的确,这事儿要从很久以前的几次搁浅开始说起…上学的时候,从来没有接触 ...
- Y组合子
Y组合子 Y组合子的用处 作者:王霄池链接:https://www.zhihu.com/question/21099081/answer/18830200来源:知乎著作权归作者所有.商业转载请联系作者 ...
- Lambda演算 - 简述Y组合子的作用
Y组合子:\f.(\x.f(xx))(\x.f(xx)),接受一个函数,返回一个高阶函数 Y组合子用于生成匿名递归函数. 什么叫匿名递归函数,考虑以下C语言递归函数 int sum(int n) { ...
- Racket中使用Y组合子
关于Y组合子,网上已经介绍很多了,其作用主要是解决匿名lambda的递归调用自己. 首先我们来看直观的递归lambda定义, 假设要定义阶乘的lambda表达,C#中需要这么定义 Func<in ...
- 简单易懂的程序语言入门小册子(4):基于文本替换的解释器,递归,如何构造递归函数,Y组合子
递归.哦,递归. 递归在计算机科学中的重要性不言而喻. 递归就像女人,即令人烦恼,又无法抛弃. 先上个例子,这个例子里的函数double输入一个非负整数$n$,输出$2n$. \[ {double} ...
- [学习] 从 函数式编程 到 lambda演算 到 函数的本质 到 组合子逻辑
函数式编程 阮一峰 <函数式编程初探>,阮一峰是<黑客与画家>的译者. wiki <函数编程语言> 一本好书,<计算机程序的构造与解释>有讲到schem ...
- 面向组合子设计Coder
面向组合子 面向组合子(Combanitor-Oriented),是最近帮我打开新世界大门的一种pattern.缘起haskell,又见monad与ParseC,终于ajoo前辈的几篇文章. 自去年9 ...
随机推荐
- ASP.NET 根据现有动态页面生成静态Html
现有动态页面的格式都是类似 pageName.aspx?ID=1的格式,后面由于发布服务器的原因,要求将动态页面转为静态html后上传. 首先根据页面生成的格式,枚举获取页面html: foreach ...
- sql 合并列
1.合并一列用“ ,”号隔开. 如下图: 这样的一列我想直接在sql里面合并最后变成:586,444,444,444,444这样的效果,平常的做法是直接把这列数据取出来,在前端循环加上逗号,但其实是可 ...
- 运行一个Hadoop Job所需要指定的属性
1.设置job的基础属性 Job job = new Job(); job.setJarByClass(***.class); job.setJobName("job name") ...
- 最大公约数(gcd):Euclid算法证明
1个常识: 如果 a≥b 并且 b≤a,那么 a=b. 2个前提: 1)只在非负整数范围内讨论两个数 m 和 n 的最大公约数,即 m, n ∈ N. 2)0可以被任何数整除,但是0不能整除任何数,即 ...
- 【转】myget编译过程中make出错的解决办法
源链接:http://www.tangqizhong.info/?p=741 myget(至今不明白为什么它对应的命令是mytget…)是我从用linux之后就开一直在用的命令行下载工具(其次也会用到 ...
- php平台移植windows和linux
2015/1/14 今天项目中遇到一个问题,在本地运行没有问题,挂到服务器上,就运行错误.过程中比较粗心,知道导致这样的原因,居然小时漏掉了一些细节. 比如,在php中通过声明__autoload() ...
- CActiveForm提示中文化
一般情况下,yii框架的CActiveForm组建的提示是英文的,把它改成中文提示 首先在main.php配置文件中,设置 'language'=>'zh_cn'; 这样将会使得cannot b ...
- ubuntu vim YCM
http://blog.sina.com.cn/s/blog_499386b00100rxm1.html http://www.cnblogs.com/junnyfeng/p/3633697.html
- 这样就算会了PHP么?-11
PHP中关于类的基本内容练习: <?php class SportObject{ public $name; public $height; public $avirdupois; public ...
- CPU的物理限制
Quote from Solidot 密歇根大学的Igor Markov在上周的<自然>期刊上发表了一篇文章,谈论了CPU面临的基本物理限制.有学者曾估计预言CPU晶体管数目和性能增长的摩 ...