F#奇妙游(30):计算表达式与ADT

Computation Expression More

在这里插入图片描述

F#中自定义的 Computation Expression 一共有8个语法构造,其中match!let!的语法糖。

在前面的一个帖子里CE初探我们已经介绍了 computation expression 中的绑定和返回,语法是let!return。通过使用这两个语法,我们可以实现程序逻辑,而把程序逻辑和具体的实现分离开来。翻过去再看一遍,发现还是有点不太容易理解。于是嘛,书读百遍其义自见,我们就把这个例子再来一遍。

处理int option运算的简单例子

在对int option进行算术运算时,我们要对每个变量进行模式匹配,这样就会有很多的match语句,这样的代码看起来就不太美观。这里运用 computation expression 就可以把这些match语句隐藏起来,让代码看起来更加简洁。这样说来的目的就已经清楚了,我们需要实现一个 computation expression,这个 computation expression 能够处理int option的运算。

还是上次的例子,不再赘述。

type Maybe() =
    member this.Bind(opt, func) = opt |> Option.bind func
    member this.Return v = Some v
    
let maybe = Maybe()

let rateStudent name =
    match name with
    | "isaac" -> Some 90
    | "mike" -> Some 80
    | _ -> None

let answer =
    maybe {
        let! first = rateStudent "isaac"
        let! second = rateStudent "mike"
        let! third = rateStudent "isaac"
        let total = first + second + third
        return (float total) / 3.0
    }

我们再来看看这些神秘的小东西到底是什么呢?在CE初探中,我们通过对let的语义进行分析,确定let实际上是let pattern in expressionbody | expression,然后才能把let!MaybeBind联系起来。

其实,我们可以把let!maybe所构造的Computation Expression称为一种语法糖。那么从这个意义上来看,我们就能把上面代码中的计算表达式部分重构为脱糖的版本。

let answer =
    maybe.Bind( //let! first = ...
        rateStudent "isaac",
        (fun first -> 
            maybe.Bind( //let! second = ...
                rateStudent "mike",
                (fun second -> 
                    maybe.Bind( //let! third = ...
                        rateStudent "isaac",
                        (fun third -> 
                            let total = first + second + third
                            maybe.Return ((float total) / 3.0)
                        )
                    )
                )
            )
        )
    )

从这里可以看到,let!的展开就是let pattern = maybe.Bind(expression, func)。如果还是看不清楚,通常都是看不清楚的,我们就可以用F#中最好用的代码分析工具:ADT!

遇事不决ADT之

如何ADT呢?前面已经强调过很多遍,所有的值都是表达式,所有的表达式都是ADT。

我们从函数的定义开始看。

type Maybe() =
    member this.Bind(opt, func) = opt |> Option.bind func
    member this.Return v = Some v

这里this.Bind是什么类型呢?

如果用自己来替代F#的类型推导,过程大概就是:

  1. this.Bind是一个A * B -> C的类型;
  2. 其中B自己又是一个D -> F的类型;
  3. 根据Option.bind的默认类型,可以得到AoptionFoption
  4. 最终得到this.BindT option * (T -> T' option) -> T' option的类型。

写得好看点,就是option<T> * (T -> option<T'>) -> option<T'>类型。放到F# REPL中验证一下。

type Maybe =
  new: unit -> Maybe
  member Bind: opt: 'b option * func: ('b -> 'c option) -> 'c option
  member Return: v: 'a -> 'a option

再进一步我们知道,option<T>是一个和类型。

option<T> = 
    | None 
    | Some of T

这个类的组合数等于 1 + C T 1 + C_T 1+CT ,而func参数的组合数则是 C T ′ C T C_{T'} ^ {C_T} CTCTBind函数输入参数的组合数为 ( 1 + C T ) C T ′ C T (1+C_T) C_{T'} ^ {C_T} (1+CT)CTCT ,输出参数的组合数为 C ( T ′ ) C(T') C(T),整个函数的组合数为 C T ′ ( 1 + C T ) C T ′ C T C_{T'} ^ {(1+C_T) C_{T'} ^ {C_T}} CT(1+CT)CTCT

btw. 我也不知道我为什么计算组合数,貌似没什么用啊。但是通过计算组合数,我们对于这个函数的理解就比较深刻了。我每次有什么函数不太理解的时候,都会用这种方式来分析一下,来来回回算几遍,就感觉理解多了,我这边建议试一试。

回到调用函数的地方

这个语法糖这么一分析就非常清楚了,我们的builder类,Maybe其实就是为前面的语法糖提供了ADT的实现,这个ADT就定义了在计算表达式中各变量的类型和值。比如这里的三个变量firstsecondthird,它们的类型都是int,而不是int option。所以这里面的let total = first + second + third就是一个普通的算术运算,而不是int option的运算。

但是最为神奇的是什么呢?在任何一个let!语句中,我们都可能会得到None,这个时候,整个计算表达式就会停止,而不会继续往下执行。这个语法在哪里实现的呢?这个语法在let!expression中实现的。

member Bind: opt: 'b option * func: ('b -> 'c option) -> 'c option中,如果optNone,那么func就不会被调用,而是直接返回None

那么现在有一个问题,如果那个计算表达式写成这样,会怎么样呢?

let answer =
    maybe {
        let! first = rateStudent "isaac"
        let! second = rateStudent "mike"
        let! third = rateStudent "isaac"
        first + second + third
    }

大家可以试一试。会报错,因为什么?因为不满足maybe.Bind的ADT类型,maybe.Bind的ADT类型是option<T> * (T -> option<T'>) -> option<T'>,而这里的totalint类型,不是option<T'>类型。最后那句,不用return改成什么都报错,其实就是在计算表达式中的所有式子都脱糖后都要满足Maybe对应函数的ADT。

组合数不同的物种,没法谈恋爱!

在这里插入图片描述

结论

  1. ADT分析是吃透F#的关键,所有的值都是表达式,所有的表达式都是ADT;
  2. computation expression 是一种语法糖;
  3. 可以回到语法糖的脱糖版本,然后用ADT分析来理解这个语法糖;
  4. 希望能够通过这个例子,对 computation expression 有一个更深刻的理解。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
校园短期闲置资源置换平台是一种创新的在线服务系统,旨在促进校园内学生、教职工以及其他成员之间的资源共享和有效利用。通过这个平台,用户可以发布、查找并交换他们暂时不使用的资源,从而实现资源的最大化利用,减少浪费,并促进校园内的可持续生活方式。以下是该平台可能包含的一些关键特性: 1. **用户注册和认证**:用户需要通过校园身份验证进行注册,确保平台的使用者都是校园社区的成员。 2. **资源发布**:用户可以发布他们愿意短期出借或交换的资源,如书籍、运动器材、电子产品、家具等。 3. **资源搜索和筛选**:用户可以根据资源类型、状态、可用时间等条件搜索和筛选所需的资源。 4. **在线预订系统**:用户可以通过平台预订所需的资源,并约定取用和归还的时间和地点。 5. **评价和信誉系统**:用户可以对交易的对方进行评价,建立信誉体系,增加用户间的信任。 6. **即时通讯功能**:平台内置即时通讯工具,方便用户就资源交换的细节进行沟通。 7. **安全和隐私保护**:确保用户的个人信息和交易记录的安全,保护用户的隐私。 8. **移动应用支持**:开发移动应用程序,使用户能够随时随地访问平台,进行资源的发布和搜索。 9. **教育资源置换**:特别为教师和研究人员提供教育资源的置换服务,如实验材料、研究工具等。 10. **活动和研讨会空间**:平台还可以用于发布和预订校园内临时活动或研讨会所需的空间。 11. **环保意识提升**:通过平台的推广和使用,增强校园社区成员的环保意识和资源节约意识。 12. **数据分析和报告**:平台可以收集和分析资源使用数据,为校园管理层提供资源利用和需求的洞察,以优化资源配置。 校园短期闲置资源置换平台通过提供一个便捷、高效和安全的在线环境,鼓励校园成员参与资源共享,不仅能够促进资源的合理分配和循环利用,还能增强社区的凝聚力和互助精神。随着共享经济理念的普及,这样的平台在校园中的推广和应用将越来越广泛。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大福是小强

除非你钱多烧得慌……

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值