Я изучаю F #, и мне интересно, можно ли улучшить реализацию этого метода, которая инициализирует первые N элементов массива. На данный момент работает отлично. Я имею в виду, что если он потерпит неудачу, когда попытается инициализировать 2-й элемент, выполнив 2-й вызов фабрики, то if вызовет отмену для первого успешного результата. Единственная незначительная проблема - это не чистить элементы в массиве в случае ошибки, но я не беспокоюсь об этом. Все, что я волнуюсь, это то, что он должен отменить первые успешные результаты, если он потерпит неудачу 2-го или 3-го или позже. Если это удастся, то результат успеха должен иметь в списке отмен все функторы, которые нужно отменить.
Дело в том, что я хотел бы избежать рекурсии и использовать что-то вроде Linq для итерации и выполнения чего-либо, но не совсем ясно, как в этом случае делать let с ударом (let!)
// val private initializeArrayInternal:
// arr : 'a option [] ->
// factory: unit -> RopWithUndo.Result<'a> ->
// count : int ->
// index : int
// -> RopWithUndo.Result<'a option []>
let rec private initializeArrayInternal (arr: _ []) factory count index =
if (arr.GetLength(0) < count) then
rwu.Failure "Count can not be greater than array length"
else if (count = index ) then
rwu.successNoUndo arr
else
rwu.either {
let! element = factory()
arr.[index] <- Some element
return (initializeArrayInternal arr factory count (index+1))
}
// val initializeArray:
// arr : 'a option [] ->
// factory: unit -> RopWithUndo.Result<'a> ->
// count : int
// -> RopWithUndo.Result<'a option []>
let rec initializeArray arr factory count =
initializeArrayInternal arr factory count 0
RopWinUndo
module RopWithUndo
type Undo = unit -> unit
type Result<'success> =
| Success of 'success * Undo list
| Failure of string
/// success with empty Undo list. It only applies to the curretn operation. The final list is concatenated of all list and no problem if some lists are empty.
let successNoUndo result =
Success (result,[])
let doUndo undoList =
undoList |> List.rev |> List.iter (fun undo -> undo())
let bind f x =
match x with
| Failure e -> Failure e
| Success (s1,undoList1) ->
try
match f s1 with
| Failure e ->
// undo everything in reverse order
doUndo undoList1
// return the error
Failure e
| Success (s2,undoList2) ->
// concatenate the undo lists
Success (s2, undoList1 @ undoList2)
with
| _ ->
doUndo undoList1
reraise()
type EitherBuilder () =
member this.Bind(x, f) = bind f x
member this.ReturnFrom x = x
member this.Return x = x
member this.Delay(f) = f()
let either = EitherBuilder ()