利用F#编写、理解Y组合子函数

赵劼的博客《使用Lambda表达式编写递归函数》中用C#实现了为函数求出 Y 组合子的函数。C#代码生涩,难以阅读,原代码如下:

        static Func<T, TResult> Fix<T,TResult>(Func<Func<T, TResult>, Func<T, TResult>> f)

        {

            return x => f(Fix(f))(x);

        }

        static Func<T1, T2, TResult> Fix<T1,T2, TResult>(Func<Func<T1, T2, TResult>, Func<T1, T2, TResult>> f)

        {

            return (x, y) => f(Fix(f))(x, y);

        }

代码调用如下:

var fac = Fix<int, int>(f => x => x <= 1 ? 1 :x * f(x - 1)); 

让代码变得容易阅读原是F#的强项,我们尝试用F#重写上面的代码,把 Fix 函数改名为 Y,得出以下代码:

let rec Y f x = f (Y f) x

代码调用则如下:

    let fac =

        Y (fun yf x ->

                if x <= 1 then 1

                else x * yf(x - 1))

现在,Y 组合子函数一下子就明撩了:它就是定义了一个高阶函数 Y f ,实现为 f (Y f)

我们把 Y f 代入下面的 fac 函数,这里,参数 f 其实被赋值为 fun yf x -> ...

关键: yf 被赋值为Y f,也就是 Y (fun yf  x),因此产生了递归。

另外,需要注意的是: let rec Y f x = f (Y f) x 不能改写为 let rec Y f = f (Y f)

理论上,两者的 Y 等义,但是,后者会在 Y 被调用时,先计算出 Y f 的值,最后因为无法结束递归而导致堆栈溢出。

而前者不计算 Y f,直接计算 Y f x,在 x <= 1 时候停止递归。

================================================================================

什么?Y 组合子有什么用?就是在你需要使用匿名函数,但是同时需要递归的时候,有了 Y 组合子你才可以同时拥有两者。

譬如,可以这样:

        a
        |> (+) 4
        |> Y (fun yf x ->

                    if x <= 1 then 1

                    else x * yf(x - 1))    
时间: 2024-10-14 02:51:34

利用F#编写、理解Y组合子函数的相关文章

大到可以小说的Y组合子(一)

问:上回乱扯淡了一通,这回该讲正题了吧. 答:OK. 先来列举一些我参考过,并从中受到启发的文章. (1.)老赵的一篇文章:使用Lambda表达式编写递归函数 (2.)装配脑袋的两篇文章:VS2008亮点:用Lambda表达式进行函数式编程和用Lambda表达式进行函数式编程(续):用C#实现Y组合子 (3.)Y组合子的推导过程(用Scheme推导),这里的"推导"并不是数学意义上上的推导证明,而是说如何一步步引导构想出Y来的,值得一看.也许从某种程度反应出了当年Y是如何被发明的. [

大到可以小说的Y组合子(三)

答:关于Fix的问题你fix了吗? 问:慢着,让我想想,上次留下个什么问题来着?是说我们有了一个求不动点的函数Fix,但Fix却是显式递归的,是吧? 答:有劳你还记的这个问题. 问:Fix的参与背离了匿名递归的定义,所以-所以-我们被Fix给坑了? 答:当然不是.你还记的第(一)章我们讨论过什么吗? 问:记的,我们把一个显式递归的Fact变成了一个匿名递归的结构. 答:很好,让我们再造一次轮子. 问:哦!我明白了,是用与上次类似的方法,把Fix写成一个匿名递归的Lambda. 答:就是这个意思,

Lambda演算 - 简述Y组合子的作用

Y组合子:\f.(\x.f(xx))(\x.f(xx)),接受一个函数,返回一个高阶函数 Y组合子用于生成匿名递归函数. 什么叫匿名递归函数,考虑以下C语言递归函数 int sum(int n) { return n == 0 ? 0 : n + sum(n-1); } 这个函数在内部递归调用了自身,调用自身需要函数本体的名字,这个函数叫sum,sum内部用名字sum,递归调用了自己 在lambda演算中,可以写成类似的表达式sum = \x. x == 0 ? 0 : sum x 但是对于一个

大到可以小说的Y组合子(二)

问:上一回,你在最后曾提到"抽象性不足",这话怎么说?  答:试想,如果现在需要实现一个其它的递归(比如:Fibonacci),就必须把之前的模式从头套一遍,然后通过fib_maker(fib_maker)来返回一个fib函数.可见,这个产生递归过程的"接口"让用户相当不舒服.  问:嗯,fib_maker(fib_maker)这种形式看起来的确不怎么舒服,那又如何对其进行抽象,以得到更好的接口呢?  答:这里,有两条路可以走.其一,就是对fact_maker(fa

Racket中使用Y组合子

关于Y组合子,网上已经介绍很多了,其作用主要是解决匿名lambda的递归调用自己. 首先我们来看直观的递归lambda定义, 假设要定义阶乘的lambda表达,C#中需要这么定义 Func<int, int> fact = null; fact = x => x <= 1 ? 1 : x * fact(x - 1); 这种方法非常简单直接,当然问题也存在,因为这里fact其实是一个委托对象,当这个对象改变后,可能就得不到阶乘的效果了. 在scala中则是这样, def F: Int

Y组合子

// Y组合子 fact = n => n == 1 ? 1 : n * fact(n - 1) // 无法匿名调用 (n => n == 1 ? 1 : n * fact(n - 1))(5) // 参数可以命名, 消灭掉里面掉fact fact = (f, n) => n == 1 ? 1 : n * f(f, n - 1); // 只能有一个参数 fact = f => n => n == 1 ? 1 : n * f(f)(n - 1); // 将fact带入 amaz

重新发明 Y 组合子:不用 λ演算那一套

重新发明 Y 组合子:不用 λ演算那一套 重新发明 Y 组合子:不用 λ演算那一套 目录 1. 一类奇妙的函数 1.1. 三个例子 1.2. 构造方法 1.3. 程序分解 2. X 组合子 3. Y 组合子 4. 结语 1 一类奇妙的函数 1.1 三个例子 函数 (lambda ($) (lambda (n) (if (< n 2) 1 (* n (($ $) (- n 1)))))) 以一个函数为参数,当这个参数是函数本身时,返回计算阶乘的函数 函数 (lambda ($) (lambda (

scheme实现匿名递归阶乘(Y组合子)

(((lambda ()         ((lambda (f)        (f f))      (lambda (x)        ((lambda (y)           (lambda (n)             (if (zero? n)                 1                 (* n (y (- n 1)))))) (lambda z                                        (apply (x x) 

SA:T1法利用Matlab编写主函数实现对一元函数优化求解——Jason niu

%SA:T1法利用Matlab编写主函数实现对一元函数优化求解--Jason niu x = 1:0.01:2; y = sin(10*pi*x) ./ x; figure plot(x,y,'linewidth',1.5) ylim([-1.5, 1.5]) xlabel('x') ylabel('y') title('SA:T1法利用Matlab编写主函数实现对一元函数y = sin(10*pi*x) / x优化求解-Jason niu') hold on [maxVal,maxIndex]