clojure like cond in F#
我最近从F#绕行Clojure,遇到一个名为cond的宏。
这是用法示例:
1 2 3 4 5 | (cond (= target (nth arr mid)) mid (< target (nth arr mid)) (search left (- mid 1)) (> target (nth arr mid)) (search (+ mid 1) right) (= left right) -1) |
这意味着伪代码中的以下内容:
1 2 3 4 | if target == arr.[mid] then return mid if target < arr.[mid] then return (call search(left, mid-1)) if target > arr.[mid] then return (call search(mid+1, right)) if left == right then return -1 |
这只是二进制搜索中的一个示例,以防您想知道左边和右边是什么,但并不是很重要。
我曾尝试在F#中找到类似的内容,但找不到,所以我决定尝试自己编写。
我最终得到了这样的东西:
1 2 3 4 5 6 7 8 9 10 | type condition = bool * int let cond (conds: condition seq) = conds |> Seq.pick(fun c -> if fst c then Some (snd c) else None) cond [| ( (=) target arr.[mid], mid ) ( (=) left right, -1 ) ( (<) target arr.[mid], recSrch left (mid-1) ) ( (>) target arr.[mid], recSrch (mid+1) right ) |] |
这里的问题是,我想在递归函数中使用它,因为马上就对recSrch left(mid-1)进行求值,所以我陷入了无限循环。 我希望仅在条件成立时对其进行评估。 另外,表格仍然不像Clojure中的表格干净。
有什么想法可以改善吗?
这是使用
它定义
1 2 | let (|Cond|_|) f arg = if f arg then Some () else None;; |
使用它很容易
1 2 3 | match 1 with |Cond ( (=) 5) _ -> printfn"unlikely" | _ -> printfn"likely";; |
您需要一种使条件体延迟求值的方法。这是一种实现方法,通过使主体成为在遍历条件序列时要调用的函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 | type condition = bool * (unit -> int) let cond (conds: condition seq) = conds |> Seq.pick(fun c -> let pred, func = c if pred then Some (func()) else None) cond [| ( (=) target arr.[mid], fun () -> mid ) ( (=) left right, fun () -> -1 ) ( (<) target arr.[mid], fun () -> recSrch left (mid-1) ) ( (>) target arr.[mid], fun () -> recSrch (mid+1) right ) |] |
请注意,只有在条件列表应该是动态的情况下,才使用这样的方法。
对于静态条件,您可以使用
1 2 3 4 5 6 7 | let result = match target with | _ when target = arr.[mid] -> mid | _ when left = right -> -1 | _ when target < arr.[mid] -> recSrch left (mid-1) | _ when target > arr.[mid] -> recSrch (mid+1) right | _ -> failwith"you need this case if the compiler can't figure if your matches are exhaustive" |
如果将其包装为活动模式,它将变得更好。
在F#中,有一种用于这种表达的语言构造:
1 2 3 4 | if target = arr.[mid] then mid elif target < arr.[mid] then call (search(left, mid-1)) elif target > arr.[mid] then call (search(mid+1, right)) else -1 |
...或者,总的来说:我将Clojure的