F#铁路编程此初始化可以改进吗?

use*_*696 3 f#

我正在学习F#,不知道这种初始化数组的前N个元素的方法实现是否可以改进。目前,它工作得很好。我的意思是,如果它尝试通过执行对工厂的第二次调用来初始化第二个元素时失败,那么它将为第一个成功的结果调用undo。唯一的小问题是如果出错,它不会清除数组中的项目,但我不必担心。我担心的是,如果它在第2或第3或更高版本上失败,则应该撤消第一个成功的结果。如果成功,则成功结果应在“撤消”列表中包含所有要撤消的函子。

问题是我想避免递归,并使用类似Linq的东西进行迭代和执行操作,但是在这种情况下,如何使用bang(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 

Run Code Online (Sandbox Code Playgroud)

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 ()

Run Code Online (Sandbox Code Playgroud)

Tom*_*cek 5

如果您向计算表达式生成器添加更多操作,您将能够for在计算内部使用该构造,这将使它变得更好:

let initializeArray (arr:_[]) factory count =
    rwu.either {
      if (arr.GetLength(0) < count) then 
        return! rwu.Failure "Count can not be greater than array length"
      for index in 0 .. count - 1 do
        let! element = factory()
        arr.[index] <- Some element 
    }
Run Code Online (Sandbox Code Playgroud)

要做到这一点,我不得不修改您的Return包裹成果转化Success(在原始版本中,你那么需要改变returnreturn!这是反正做事的正确方法),然后我不得不添加ZeroCombine并且For

type EitherBuilder () =
  member this.Return x = Success(x, [])
  member this.Bind(x, f) = bind f x

  member this.ReturnFrom x = x
  member this.Delay(f) = f()

  member this.Zero() = this.Return ()
  member this.Combine(a, b) = this.Bind(a, fun () -> b)
  member this.For(s:seq<_>, b) = 
    let en = s.GetEnumerator()
    let rec loop () = 
      if en.MoveNext() then this.Bind(b en.Current, loop)
      else this.Zero()
    loop ()
Run Code Online (Sandbox Code Playgroud)