F# ——Computation 表达式
或许你对这个词有点陌生,但是如果说到F#中的Sequence表达式,Async结构时,你或许会有点印象。其实这两种表达式均可以理解为Computation Expression.
如果你对Sequence表达式这个名词感觉很陌生的话,那先弄些代码来看看:
let seqExpr = seq{ for i in [1..10] do yield i }
相信看到上面的代码你应该知道神马叫Sequence表达式了。。。
下面是Async的代码片断:
let asyncExpr path = async{ let fileStr = new System.IO.FileStream(path,System.IO.FileMode.Open) use strRdr = new System.IO.StreamReader(fileStr) while not strRdr.EndOfStream do let line = strRdr.ReadLine() printfn "%s" line } |> Async.RunSynchronously
Async 和 Seq 中的那关键字都是有它自己的功能的,那么这些关键字是怎么定义的呢,下面就来自定义一个这样的表达式玩玩:
[<Measure>] type 米 [<Measure>] type 秒 [<Measure>] type 米每秒 = 米 / 秒 type Velocity<[<Measure>] 'T> = | MovingOrStop of float<'T> | Illegal let cal m s = match s with | 0.<秒> -> Illegal | _ -> MovingOrStop(m/s) type CalVelocityBuilder() = let mutable speed = 0.0<米每秒> member this.Yield(()) = speed member this.Bind(v : Velocity<米每秒>,func : float<米每秒> -> float<米每秒>) = match v with | Illegal -> speed <- -1.<米每秒>; speed | MovingOrStop(value) -> func value member this.Return(v : float<米每秒>) = v let V = new CalVelocityBuilder() let 速度 = V { let! k = cal 1.0<米> 5.0<秒> return k }
代码自定义了let!关键字,这是通过member this.Bind(v : Velocity<米每秒>,func : float<米每秒> -> float<米每秒>)函数实现的,它的MSDN 描述 如下:
Method |
Typical signature(s) |
Description |
Bind |
M<'T> * ('T -> M<'U>) -> M<'U> |
Called for let! and do! in computation expressions. |
代码中的 米 ,秒 ,和 米每秒都是 自定义的测量单位。
上面代码在FSI中的运行结果:
val 速度 : float<米每秒> = 0.2
个人的理解就是: 定义的bind函数 以及 表现为关键字let!,其作用都是为了在执行这一步操作的同时检查是不是有不合法的结果出现,如果有,即跳出这个流程(workflow),这样做是为了弥补F#中没有return,break这样的异常跳出操作。
Computation 表达式可以再一定程度上减少代码量—— 当你需要经常使用某些特定的代码处理流程的时候。Async就是个例子。
下面是MSDN关于computation Expression 的描述:
http://msdn.microsoft.com/en-us/library/dd233182.aspx
Don Syme(F#创始人)关于这个的解释:
要完全实现表中的那些函数,需要对Computation更深入的了解。