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

标签 f#

我正在学习 F#,我想知道这个初始化数组前 N 个元素的方法实现是否可以改进。目前它工作得很好。我的意思是,如果当它尝试通过对工厂执行第二次调用来初始化第二个元素时失败,那么如果它会调用撤消以获得第一个成功的结果。唯一的小问题是,如果出现错误,它不会清除数组中的项目,但我不担心它。我所担心的是,如果第二次、第三次或更晚失败,它应该撤消第一个成功的结果。如果成功,那么成功结果应该在撤消中列出所有要撤消的仿函数。

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

RopWin撤消

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

最佳答案

如果您向计算表达式生成器添加更多操作,您将能够在计算中使用 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 
    }

为此,我必须修改您的 Return 以将结果包装到 Success 中(在您的原始版本中,您需要更改 return > 到 return! 无论如何,这是正确的做事方式),然后我必须添加 ZeroCombineFor:

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

关于F# 铁路编程 这个初始化可以改进吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55446497/

相关文章:

lambda - F# 不带参数的 lambda 表达式

string - 如何在 F# 中迭代​​一个字符串?

f# - 为委托(delegate)类型提供参数名称

asynchronous - 为什么 Async.StartChild 返回 `Async<Async<' T>>`?

f# - 无法构建 F# 类型提供程序 SDK

.net - F# SqlDataConnection 类型提供程序 - 运行一次,而不是每次编译时运行

f# - 如何在函数式编程(F#)中将行号添加到文本文件?

f# - 我应该使用 new Type() 还是只使用 Type() 来调用构造函数

f# - 覆盖 ToString 和 `%A` 说明符

f# - 匹配列表的前两个元素