我正在尝试掌握延续传球风格(CPS),因此我很久以前就改编了Gary Short给我看的一个例子。我没有他的示例源代码,所以我试图从内存中重写他的例子。请考虑以下代码:
let checkedDiv m n =
match n with
| 0.0 -> None
| _ -> Some(m/n)
let reciprocal r = checkedDiv 1.0 r
let resistance c1 c2 c3 =
(fun c1 -> if (reciprocal c1).IsSome then
(fun c2 -> if (reciprocal c2).IsSome then
(fun c3 -> if (reciprocal c3).IsSome then
Some((reciprocal c1).Value + (reciprocal c2).Value + (reciprocal c3).Value))));;
我无法弄清楚如何构建阻力函数。我早先想出了这个:
let resistance r1 r2 r3 =
if (reciprocal r1).IsSome then
if (reciprocal r2).IsSome then
if (reciprocal r3).IsSome then
Some((reciprocal r1).Value + (reciprocal r2).Value + (reciprocal r3).Value)
else
None
else
None
else
None
但是,当然,这不是使用CPS - 更不用说它看起来真的很hacky并且有相当多的重复代码,这看起来像代码味道。
有人能告诉我如何用CPS方式重写阻力函数吗?
答案 0 :(得分:3)
直截了当的方式:
let resistance_cps c1 c2 c3 =
let reciprocal_cps r k = k (checkedDiv 1.0 r)
reciprocal_cps c1 <|
function
| Some rc1 ->
reciprocal_cps c2 <|
function
| Some rc2 ->
reciprocal_cps c3 <|
function
| Some rc3 -> Some (rc1 + rc2 + rc3)
| _ -> None
| _ -> None
| _ -> None
或使用Option.bind
缩短一点let resistance_cps2 c1 c2 c3 =
let reciprocal_cps r k = k (checkedDiv 1.0 r)
reciprocal_cps c1 <|
Option.bind(fun rc1 ->
reciprocal_cps c2 <|
Option.bind(fun rc2 ->
reciprocal_cps c3 <|
Option.bind(fun rc3 -> Some (rc1 + rc2 + rc3))
)
)
答案 1 :(得分:2)
这是Chris Smith撰写的“Programming F#”一书中的一项已知任务; CPS风格的解决方案代码在第244页给出:
let let_with_check result restOfComputation =
match result with
| DivByZero -> DivByZero
| Success(x) -> restOfComputation x
let totalResistance r1 r2 r3 =
let_with_check (divide 1.0 r1) (fun x ->
let_with_check (divide 1.0 r2) (fun y ->
let_with_check (divide 1.0 r3) (fun z ->
divide 1.0 (x + y + z) ) ) )
答案 2 :(得分:2)
使用Maybe monad定义here
let resistance r1 r2 r3 =
maybe {
let! r1 = reciprocal r1
let! r2 = reciprocal r2
let! r3 = reciprocal r3
return r1 + r2 + r3
}