F# 重试F中的计算表达式或其他构造#
我希望能够在F#中编写一个计算表达式,在抛出异常时能够重试操作。现在,我的代码如下所示:F# 重试F中的计算表达式或其他构造#,f#,computation-expression,F#,Computation Expression,我希望能够在F#中编写一个计算表达式,在抛出异常时能够重试操作。现在,我的代码如下所示: let x = retry (fun() -> GetResourceX()) let y = retry (fun() -> GetResourceY()) let z = retry (fun() -> DoThis(x, y)) etc. (this is obviously an astract representation of the actual code) 我需要能够多次
let x = retry (fun() -> GetResourceX())
let y = retry (fun() -> GetResourceY())
let z = retry (fun() -> DoThis(x, y))
etc. (this is obviously an astract representation of the actual code)
我需要能够多次重试每个函数,这是我定义的
我认为计算表达式可以帮助我,但我不知道它如何帮助我删除将每个右手边显式包装为可重复的内容,但对我来说,这只是使重复/包装更加简洁;它还在那里。我可以将每个函数包装成一个可重试的计算表达式,它有一些扩展(除了标准的一元特性之外),这为您提供了一个很好的方法
正如你所说,monad本质上是包装器(创建一个简单的函数就可以了)
let rec retry times fn =
if times > 1 then
try
fn()
with
| _ -> retry (times - 1) fn
else
fn()
测试代码
let rnd = System.Random()
let GetResourceX() =
if rnd.Next 40 > 1 then
"x greater than 1"
else
failwith "x never greater than 1"
let GetResourceY() =
if rnd.Next 40 > 1 then
"y greater than 1"
else
failwith "y never greater than 1"
let DoThis(x, y) =
if rnd.Next 40 > 1 then
x + y
else
failwith "DoThis fails"
let x = retry 3 (fun() -> GetResourceX())
let y = retry 4 (fun() -> GetResourceY())
let z = retry 1 (fun() -> DoThis(x, y))
这是在单个计算表达式中进行此操作的第一次尝试。但请注意,这只是第一次尝试;我还没有对其进行彻底测试。。此外,在计算表达式中重新设置尝试次数时,这有点难看。我认为在这个基本框架中,语法可以得到很好的清理
let rand = System.Random()
let tryIt tag =
printfn "Trying: %s" tag
match rand.Next(2)>rand.Next(2) with
| true -> failwith tag
| _ -> printfn "Success: %s" tag
type Tries = Tries of int
type Retry (tries) =
let rec tryLoop n f =
match n<=0 with
| true ->
printfn "Epic fail."
false
| _ ->
try f()
with | _ -> tryLoop (n-1) f
member this.Bind (_:unit,f) = tryLoop tries f
member this.Bind (Tries(t):Tries,f) = tryLoop t f
member this.Return (_) = true
let result = Retry(1) {
do! Tries 8
do! tryIt "A"
do! Tries 5
do! tryIt "B"
do! tryIt "C" // Implied: do! Tries 1
do! Tries 2
do! tryIt "D"
do! Tries 2
do! tryIt "E"
}
printfn "Your breakpoint here."
让rand=System.Random()
让tryIt标记=
printfn“尝试:%s”标记
将rand.Next(2)>rand.Next(2)与
|true->failwith标记
|->printfn“成功:%s”标记
类型Tries=int的Tries
键入重试(重试)=
让我们来看看=
匹配
printfn“史诗般的失败。”
假的
| _ ->
试试f()
带| u->tryLoop(n-1)f
成员this.Bind(u:unit,f)=tryLoop尝试f
成员this.Bind(Tries(t):Tries,f)=tryLoop t f
此成员。返回(41;)=true
让结果=重试(1){
做!试8次
做!尝试“A”
做!试5次
做!尝试“B”
do!tryIt“C”//暗示:do!尝试1
做!尝试2
做!尝试“D”
做!尝试2
做!尝试“E”
}
printfn“此处是您的断点。”
p、 但我更喜欢Tomas和gradbot的版本。我只是想看看这种类型的解决方案可能会是什么样子。对我来说,这似乎是一个很好的单子使用案例。如果
抛出的异常次数超过您允许的次数,会发生什么情况?它会让异常通过吗?这是一个巧妙的技巧。如果我愿意,我假设为了将这种行为封装在一个计算表达式中,我需要定义每个函数将返回的一元类型(或者定义包装函数,让他们100%返回MI agree,即一个简单的重试函数正是所需的。为什么要使用盒式手套来计算牙签数?F#没有Haskel那么严格。无需在任何地方使用monads。。。
let rec retry times fn =
if times > 1 then
try
fn()
with
| _ -> retry (times - 1) fn
else
fn()
let rnd = System.Random()
let GetResourceX() =
if rnd.Next 40 > 1 then
"x greater than 1"
else
failwith "x never greater than 1"
let GetResourceY() =
if rnd.Next 40 > 1 then
"y greater than 1"
else
failwith "y never greater than 1"
let DoThis(x, y) =
if rnd.Next 40 > 1 then
x + y
else
failwith "DoThis fails"
let x = retry 3 (fun() -> GetResourceX())
let y = retry 4 (fun() -> GetResourceY())
let z = retry 1 (fun() -> DoThis(x, y))
let rand = System.Random()
let tryIt tag =
printfn "Trying: %s" tag
match rand.Next(2)>rand.Next(2) with
| true -> failwith tag
| _ -> printfn "Success: %s" tag
type Tries = Tries of int
type Retry (tries) =
let rec tryLoop n f =
match n<=0 with
| true ->
printfn "Epic fail."
false
| _ ->
try f()
with | _ -> tryLoop (n-1) f
member this.Bind (_:unit,f) = tryLoop tries f
member this.Bind (Tries(t):Tries,f) = tryLoop t f
member this.Return (_) = true
let result = Retry(1) {
do! Tries 8
do! tryIt "A"
do! Tries 5
do! tryIt "B"
do! tryIt "C" // Implied: do! Tries 1
do! Tries 2
do! tryIt "D"
do! Tries 2
do! tryIt "E"
}
printfn "Your breakpoint here."