Oto iteracyjny rozwiązanie, oparte na funkcji Seq.unfold
. Używamy tej funkcji do generowania leniwych sekwencji zdarzeń Sukces/Nieudane. Następnie możemy wykonać manipulacje na tej sekwencji, aby uzyskać pomyślny wynik lub zatrzymać się po kilku próbach.
Najpierw niech nam określić podpis funkcji, która może zawodzić:
type ActionResult<'a> =
| Success of 'a
| ErrorConnecting
type getValue<'a> = unit -> ActionResult<'a>
następnie zdefiniować discrimianted unii które modele wszystkie różne stany moglibyśmy być w odniesieniu do ponawiania:
type Retry<'a> =
| Success of 'a * int
| Failure of int
| Untried
Teraz, biorąc pod uwagę wynik ostatniego ponowienia, generujemy następny element w sekwencji:
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)
możemy teraz korzystać z tej funkcji, aby utworzyć getResultWithRetries
funkcję:
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
Możemy wreszcie przetestować to:
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)
Korzystanie następującą funkcję, możemy sprawdzić, co się dzieje z funkcji nieczystych:
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)
użyj rekursji - posiada parametr, który jest prawdziwy przy pierwszym podejściu, a fałsz na drugim. –
6 odpowiedzi. Jaką puszkę z robakami otworzyłeś ... :-) – s952163