像F#中的cond一样的clojure

Pet*_*r V 7 f# clojure

我最近在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)
Run Code Online (Sandbox Code Playgroud)

这意味着以下伪代码:

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
Run Code Online (Sandbox Code Playgroud)

这只是二进制搜索的一个例子,以防你想知道左边和右边是什么,但不是很重要.

我试图在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 )
      |]
Run Code Online (Sandbox Code Playgroud)

这里的问题是我想在递归函数中使用它,并且因为recSrch left(mid-1)正在被立即评估,所以我最终处于无限循环中.我希望它只在条件成立时进行评估.此外,表格仍然不像Clojure那样干净.

任何想法我怎么能改善这个?

Joh*_*mer 7

这是一个草图match,我认为它非常接近clojure.

它定义Cond为将测试函数作为参数的部分活动模式

let (|Cond|_|) f  arg = 
    if f arg then Some () else None;;
Run Code Online (Sandbox Code Playgroud)

使用它很容易

match 1 with
|Cond ( (=) 5) _ -> printfn "unlikely"
| _ -> printfn "likely";;
Run Code Online (Sandbox Code Playgroud)


scr*_*wtp 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 )
        |]
Run Code Online (Sandbox Code Playgroud)

请注意,只有当您的条件列表应该是动态的时,才有意义使用这样的东西。

对于静态条件,您可以使用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"
Run Code Online (Sandbox Code Playgroud)

如果将其包装为活动模式,效果会更好。