我最近在F#中绕道而行,并遇到了一个叫做cond的宏。 以下是有关用法的示例:
(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)
这意味着以下伪代码:
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#中找到类似的东西,但我不能,所以我决定尝试自己编写。 我最终得到了类似的东西:
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那样干净。
任何想法我怎样才能改善这一点?
答案 0 :(得分:7)
这是使用match
的草图,我认为它非常接近clojure。
它将Cond
定义为将测试函数作为参数
let (|Cond|_|) f arg =
if f arg then Some () else None;;
使用它很容易
match 1 with
|Cond ( (=) 5) _ -> printfn "unlikely"
| _ -> printfn "likely";;
答案 1 :(得分:4)
在F#中,有这种表达的语言结构:
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的cond
宏等同于模式匹配或if/elif/else
块。它们显然不完全相同,因为Clojure被解释并动态输入,而F#被编译并静态输入。
答案 2 :(得分:4)
你需要一种让条件体懒惰评估的方法。这是一种方法,通过使主体成为在迭代条件序列时调用的函数:
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 )
|]
请注意,如果您的条件列表应该是动态的,那么使用类似的东西才有意义。
对于静态条件,您具有与when
子句匹配的模式。这给了你很好的惯用语法,并且通常在编译时检查你的匹配穷举,所以这是值得的。
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"
如果将其包装为活动模式,则获得更好的效果。