[Erlang 0056] 用fun在Erlang Shell中编写尾递归 Ⅱ
来源:互联网 发布:买的域名怎么使用 编辑:程序博客网 时间:2024/04/29 08:47
之前研究了一个问题"[Erlang 0050]用fun在Erlang Shell中编写尾递归",一直对这个问题保持着关注;最近在搜索引擎里找到同一个问题,题目足够清晰calling fun() from fun() 它提供了另外一种解决解决方案:Y-combinator!
%%That's easy, you need the Y-combinator!y(M) -> G = fun (F) -> M(fun(A) -> (F(F))(A) end) end, G(G). and then you define you fun with a fun wrapper like so:Fac -> fun (F) -> fun (0) -> 1; (N) -> N * F(N-1) end end.and call it like:(y(Fac))(5)120
不错吧,不过我们的目标是在shell里面直接实现尾递归,所以要动手简单改造一下
Eshell V5.9 (abort with ^G)1> Y=fun(F) ->1> G = fun(T) ->1> F(fun(X) -> (T(T))(X) end)1> end,1> G(G) end.#Fun<erl_eval.6.111823515>2> 2> Fac = fun (F) ->2> fun (0) -> 1;2> (N) -> N * F(N-1)2> end2> end.#Fun<erl_eval.6.111823515>3> 3> (Y(Fac))(5).1204> Fib = fun(F) ->4> fun(0) -> 0;4> (1) -> 1;4> (N) -> F(N-1) + F(N-2) 4> end4> end.#Fun<erl_eval.6.111823515>5> 5> (Y(Fib))(8).216>
问题还没有结束,对于两个参数的怎么办呢?Y函数比较直观,可以修改为:
6> Y2=fun(F) ->6> G = fun(T) ->6> F(fun(Y, Z) -> (T(T))(Y, Z) end)6> end,6> G(G) end.#Fun<erl_eval.6.111823515>7> Func2=fun(F)->7> fun(X,Y) when Y<1000 ->io:format("~p,",[X+Y]), F(Y,X+Y); 7> (X,Y) -> done 7> end7> end.#Fun<erl_eval.6.111823515>8> 8> (Y2(Func2))(0,1).1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,done
当然多个参数我们可以使用apply mfa的方式来处理多个参数的情况.同时Y方法也可以放在user_default里面方便后续使用.实践之后说一点理论吧,这种解决方案是使用了Y combinator可能最近你已经频繁接触到这个名字,比如在IT新闻,比如在<<黑客与画家>>,它本身是一个数学概念,那么我们从维基百科开始:
英文维基
Fixed-point combinator http://en.wikipedia.org/wiki/Fixed-point_combinator
Y combinator: http://rosettacode.org/wiki/Y_combinator
中文维基
不动点组合子 http://zh.wikipedia.org/wiki/%E4%B8%8D%E5%8A%A8%E7%82%B9%E7%BB%84%E5%90%88%E5%AD%90
不动点 http://zh.wikipedia.org/wiki/%E4%B8%8D%E5%8A%A8%E7%82%B9
λ演算 http://zh.wikipedia.org/wiki/%E6%97%A0%E7%B1%BB%E5%9E%8B_lambda_%E6%BC%94%E7%AE%97
我们理顺一下里面的逻辑关系,从不动点开始:
在数学中,函数的不动点或定点是指被这个函数映射到其自身一个点.例如,定义在实数上的函数f,f(x)=x2-3x+4则2是函数f的一个不动点,因为f(2)=2.也不是每一个函数都具有不动点。例如定义在实数上的函数,f(x)=x+1就没有不动点。因为对于任意的实数,x永远不会等于x+1。用画图的话来说,不动点意味着点(x,f(x))在直线y=x上,或者换句话说,函数f的图像与那根直线有共点。上例f(x)=x+1的情况是,这个函数的图像与那根直线是一对平行线.
我用wolframalpha.com绘制了f(x)=x2-3x+4的函数图:
接下来我们看不动点组合子(Fixed-point combinator,或不动点算子)是计算其他函数的一个不动点的高阶函数.函数 f 的不动点是一个值 x 使得 f(x) = x.例如,0 和 1 是函数 f(x) = x2 的不动点,因为 02 = 0 而 12 = 1.鉴于一阶函数(在简单值比如整数上的函数)的不动点是个一阶值,高阶函数 f 的不动点是另一个函数 g 使得 f(g) = g.那么,不动点算子是任何函数 fix 使得对于任何函数 f 都有f(fix(f)) = fix(f).不动点组合子允许定义匿名的递归函数.
然后接下来是Y combinator(Y组合子)在无类型 lambda 演算中众所周知的(可能是最简单的)不动点组合子叫做 Y 组合子.它是 Haskell B. Curry 发现的,定义为
- Y = λf.(λx.f (x x)) (λx.f (x x)) %%用一个例子函数g来展开它,我们可以看到上面这个函数是怎么成为一个不动点组合子的
- Y g = (λf.(λx . f (x x)) (λx . f (x x))) g
- Y g = (λx. g (x x)) (λx . g (x x)) %%λf 的 β-归约 - 应用主函数于 g
- Y g = (λy. g (y y)) (λx . g (x x)) %%α-转换 - 重命名约束变量
- Y g = g ((λx. g (x x)) (λx . g (x x))) %%λy 的 β-归约 - 应用左侧函数于右侧函数
- Y g = g (Y g) %%Y 的定义
这个!?在编程语言里面怎么实现呢?看一下http://rosettacode.org/wiki/Y_combinator 这里罗列了大多数语言对Y combinator的实现,比如C#版本:
delegate Func<int , int > Recursive(Recursive recursive);
void Main()
{
Func<Func<Func<int, int>, Func< int, int>>, Func< int, int>> Y =
f => ((Recursive)(g => (f(x => g(g)(x)))))((Recursive)(g => f(x => g(g)(x))));var fac = Y(f => x => x < 2 ? 1 : x * f(x - 1));
var fib = Y(f => x => x < 2 ? x : f(x - 1) + f(x - 2));
Console.WriteLine(fac(6));
Console.WriteLine(fib(6));
}
不过注意一下Erlang版本的实现,上面提供了另外一种实现方式(只不过在下面这种实现中,怎么使用多个参数呢?):
Eshell V5.9 (abort with ^G)1> Y = fun(M) -> (fun(X) -> X(X) end)(fun (F) -> M(fun(A) -> (F(F))(A) end) end) end.#Fun<erl_eval.6.111823515>2> Fac = fun (F) ->2> fun (0) -> 1;2> (N) -> N * F(N-1)2> end2> end.#Fun<erl_eval.6.111823515>3> (Y(Fac))(5). 1204> 4> Fib = fun(F) ->4> fun(0) -> 0;4> (1) -> 1;4> (N) -> F(N-1) + F(N-2) 4> end4> end.#Fun<erl_eval.6.111823515>5> (Y(Fib))(8).
沿着Y Combinator这个线索,可以找到更多的资料,
[PDF] The Why of Y - Dreamsongs
[1] Y Combinator in Erlang
[2] Deriving the Y Combinator in Erlang (原文已经被墙,这是我拷贝出来的副本)
- [Erlang 0056] 用fun在Erlang Shell中编写尾递归 Ⅱ
- [Erlang 0050]用fun在Erlang Shell中编写尾递归
- 在erlang shell中使用ETS
- erlang 尾递归
- Erlang尾递归
- Erlang 尾递归
- erlang递归和尾递归
- [Erlang]如何在Erlang中使用SSL
- Erlang-fun函数笔记
- erlang fun 函数匹配
- erlang的shell里写一个尾递归
- erlang递归
- erlang:使用 fun 来编写一个成绩排序
- [Erlang 0044] Erlang Shell History
- erlang fun函数使用详解
- erlang的递归与尾递归
- Erlang学习:递归和尾递归
- 关于erlang中makefile的编写
- [Erlang 0054] Erlang Web 监控工具
- [Erlang 0055] Erlang Shared Data using mochiglobal
- [Think] 解决问题 Ⅱ
- 纯java操作SVN,使用svnkit做commit,update 提交,更新操作
- Disk is Tape, Flash is Disk
- [Erlang 0056] 用fun在Erlang Shell中编写尾递归 Ⅱ
- [Erlang 0057] Erlang 排错利器: Erlang Crash Dump Viewer
- [Erlang 0058] Erlang Function调用效率
- Linux/Unix设计思想 读书笔记
- [Erlang 0059] Erlang日期与时间处理
- [Erlang 0060] Joe Armstrong 论文《面向软件错误构建可靠的分布式系统》笔记
- [Erlang 0061] Erlang Code Snippet Ⅱ
- The Matrix Revolutions
- [Erlang 0062] Erlang Unicode 两三事