zoukankan      html  css  js  c++  java
  • 利用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,其部分实现为:  Y f = f (Y f) 

    我们检视 fac 函数中的 Y f ,这里,参数 f 其实就是匿名函数 fun yf x -> ...

     关键: 这里参数 yf 被代入值 Y f,而 Y f 等于 fac,因此上,实质上变成: 

    let fac =
        fun x ->
            if x <= 1 then 1
            else x * fac(x - 1)

    OK,现在如果在 let 后面加上  rec  ,那就成为一个标准的递归函数了。

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

    理论上,两者的 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))    

     

     

     

     

     

     

     

  • 相关阅读:
    pl/sql 编程!
    oracle中的常用函数、字符串函数、数值类型函数、日期函数,聚合函数。
    oracle 相关查询和非相关查询,oracle 去除重复数据,以及oracle的分页查询!
    初识 oracle!
    分页查询。
    利用ajax技术 实现用户注册。
    quartz CronExpression
    SQL 面试题
    什么是HTTP协议?常用的状态码有哪些?
    聚集索引与非聚集索引
  • 原文地址:https://www.cnblogs.com/greatim/p/4474454.html
Copyright © 2011-2022 走看看