f# - F#铁路编程是这个初始化可以改进吗?
问题描述
我正在学习 F#,我想知道这种初始化数组的前 N 个元素的方法实现是否可以改进。目前它工作得很好。我的意思是,如果它在尝试通过对工厂执行第二次调用来初始化第二个元素时失败,那么如果会为第一个成功结果调用 undo。唯一的小问题是,如果出错,它不会清理数组中的项目,但我不担心。我所担心的是,如果它在第 2 次或第 3 次或更晚时失败,它应该为第一个成功的结果撤消。如果它成功,那么成功结果应该在 Undo 列表中列出所有要撤消的函子。
问题是我想避免递归并使用 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!
正确的做事方式),然后我必须添加Zero
,Combine
和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 ()
推荐阅读
- angular - 带有真实示例的角材质树复选框
- asp.net-core - MassTransit.Analyzers 没有发现潜在问题
- typescript - 我可以在泛型中使用与 JS 中的命名参数相同的命名 TS 类型吗?
- python - 如何避免python中的for循环?
- html - 如何在别人的 onclick 上更改反应组件
- inheritance - Java中是否可以“间接覆盖”静态方法?
- python - 使用 webbrowser 打开 Opera 时出现“未检测到可运行的浏览器”
- html - 我怎么能复制像亚马逊这样的产品列表
- php - 有没有办法只从 url 中删除换行符同时保留其他
- python - 如何用python找出笔记本电脑的用户名?