因此,通过尝试避免可变变量,我提出了以下重试逻辑代码,这看起来很难看:
let result = TryConnect()
match result with
| ErrorConnecting ->
SetupConnectionParameters()
let resultAgain = TryConnect()
match resultAgain with
| ErrorConnecting ->
Console.Error.WriteLine("Setup failed!")
Environment.Exit(7)
| Success(value) -> PerformOperations(value)
| Success(value) -> PerformOperations(value)
有没有办法减少一些重复? (请记住,没有mutable
vars。)
谢谢!
答案 0 :(得分:12)
由于这里显示了很多替代品,这里有另一个:
let private tryConnectNth n =
if n <> 0 then SetupConnectionParameters()
TryConnect()
let isSuccess = function
|Success _ -> true
|ErrorConnecting -> false
let tryConnect n =
Seq.init n tryConnectNth // generate a sequence of n connection attempts
|> Seq.tryFind isSuccess // try to find the first success - returns Option
|> Option.fold (fun _ -> id) ErrorConnecting // return ErrorConnecting if None, or result otherwise
仅在非零连接尝试时调用SetupConnectionParameters()
并重复最多n次。
答案 1 :(得分:8)
使用重试参数递归函数:
let rec private tryToConnectAux tryAgain =
match TryConnect() with
| Success(value) -> PerformOperations(value)
| ErrorConnecting when tryAgain ->
SetupConnectionParameters ()
tryToConnectAux false
| ErrorConnecting ->
Console.Error.WriteLine("Setup failed!")
Environment.Exit(7)
通过tryToConnectAux true
致电。
这个答案已被编辑。原始代码:
let rec tryConnecting nRetries =
match TryConnect() with
| ErrorConnecting ->
if nRetries > 0 then tryConnecting (nRetries - 1)
else
Console.Error.WriteLine("Setup failed!")
Environment.Exit(7)
| Success(value) -> PerformOperations(value)
(此版本不包含SetupConnectionParameters()
,您必须在适当的位置添加它)
答案 2 :(得分:8)
您可以将重试逻辑分离为单独的函数。这是一个有很多打印到控制台的例子来说明发生了什么。
let rec retry f tries =
printfn "Trying..."
match f () with
| Some successValue ->
printfn "Success"
Some successValue
| None ->
match tries with
| [] ->
printfn "Failed"
None
| delayMs :: rest ->
printfn "Waiting %i ms..." delayMs
System.Threading.Thread.Sleep(delayMs:int)
retry f rest
let random = System.Random()
let connect () =
if random.Next(100) < 30 then Some "connection"
else None
match retry connect [100; 100] with
| Some connection -> printfn "Do something with connection."
| None -> printfn "Could not connect."
尝试几次运行最后一个表达式。
这为您提供了灵活的尝试次数,每次尝试都有可选的延迟(提供的延迟次数是重试次数)。
应该可以调整代码以使用retry
函数。您需要创建一个尝试连接一次的函数,如果成功则返回Some
中包含的连接,如果失败则返回None
。然后将该函数作为f
参数传递。
答案 3 :(得分:2)
虽然我很欣赏@Vandroiy的尝试,但他的版块并不像我原来的代码那样(因为我故意不想第一次调用SetupConnectionParameters()
)。
这是我的结果,灵感来自他的回答和Jon的初步暗示:
let rec TryConnectAndMaybeSetup(retries) =
if (retries > 1) then
Console.Error.WriteLine("Setup failed")
Environment.Exit(7)
let result = TryConnect()
match result with
| ErrorConnecting ->
SetupConnectionParameters()
TryConnectAndMaybeSetup(retries + 1)
| Success(value) -> PerformOperations(value)
TryConnectAndMaybeSetup(0)
这个替代方案也比@ TheQuickBrownFox更简单。
答案 4 :(得分:2)
这是另一种基于Vandroiy解决方案的解决方案,该解决方案仅在第一次失败时调用设置功能。</ p>
let tryConnecting =
let rec connect nRetries setupFunction =
match TryConnect() with
| ErrorConnecting ->
if nRetries > 0 then
setupFunction()
connect (nRetries - 1) setupFunction
else
Console.Error.WriteLine("Setup failed!")
Environment.Exit(7)
| Success(value) -> PerformOperations(value)
connect 1 SetupConnectionParameters
答案 5 :(得分:2)
这是一个基于Seq.unfold
函数的迭代解决方案。我们使用此函数生成一系列成功/失败事件的延迟。然后,我们可以对此序列执行操作以获得成功结果,或者在重试之后停止。
首先让我们定义可能失败的函数的签名:
type ActionResult<'a> =
| Success of 'a
| ErrorConnecting
type getValue<'a> = unit -> ActionResult<'a>
然后定义一个有区别的联合,它可以模拟我们在重试方面可能遇到的所有不同状态:
type Retry<'a> =
| Success of 'a * int
| Failure of int
| Untried
现在,给定上次重试的结果,我们生成序列中的下一个项目:
let unfolder (functionInvoke : getValue<_>) (retryParameters : Retry<_>) : ((Retry<_>* Retry<_>) option) =
let nextRetryResult () =
match functionInvoke() with
| ActionResult.ErrorConnecting ->
match retryParameters with
| Untried -> Failure 1
| Failure pastRetries -> Failure (pastRetries + 1)
| ActionResult.Success value ->
match retryParameters with
| Untried -> Success (value, 0 )
| Failure pastRetries -> Success (value, pastRetries )
match retryParameters with
| Untried
| Failure _ -> Some(retryParameters, nextRetryResult() )
| success -> Some(retryParameters, success)
我们现在可以使用此函数创建getResultWithRetries
函数:
let isNotSuccessAndLimitNotReached limit (retry : Retry<'a>) =
match retry with
| Untried -> true
| Failure retryCount when retryCount < limit -> true
| _ -> false
let getResultWithRetries limit getValue =
Seq.unfold (unfolder getValue) Retry.Untried
|> Seq.skipWhile(isNotSuccessAndLimitNotReached limit)
|> Seq.head
我们终于可以测试一下:
let successValue = getResultWithRetries 3 (fun () -> ActionResult.Success "ABC")
let ``fail after 3 attempts`` : Retry<string> = getResultWithRetries 3 (fun () -> ActionResult.ErrorConnecting)
let ``fail after 5 attempts`` : Retry<string> = getResultWithRetries 5 (fun () -> ActionResult.ErrorConnecting)
使用以下函数,我们可以测试不纯函数会发生什么:
let succeedOn count =
let mutable callCount = 0
let f () =
match callCount < count with
| true ->
callCount <- callCount + 1
ErrorConnecting
| false -> ActionResult.Success "ABC"
f
let ``result after 3 attempts when succeeds on 2nd`` : Retry<string> = getResultWithRetries 3 (succeedOn 2)
let ``result after 3 attempts when succeeds on 5th`` : Retry<string> = getResultWithRetries 3 (succeedOn 5)