【问题标题】:clojure like cond in F#clojure 像 F# 中的 cond
【发布时间】:2016-05-29 09:32:40
【问题描述】:

我最近从 F# 中绕道使用 clojure,遇到了一个名为 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 中那样干净。

有什么想法可以改进吗?

【问题讨论】:

  • 对于我们这些不了解 Clojure 的人来说,您的问题非常不透明。
  • 抱歉,我添加了一些伪代码以明确 clojure 代码的行为方式。
  • 能否发下完整代码,包括targetleftright

标签: clojure f#


【解决方案1】:

这是一个使用match 的草图,我认为它非常接近clojure。

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

let (|Cond|_|) f  arg = 
    if f arg then Some () else None;;

使用起来很简单

match 1 with
|Cond ( (=) 5) _ -> printfn "unlikely"
| _ -> printfn "likely";;

【讨论】:

    【解决方案2】:

    您需要一种使条件体延迟评估的方法。这是一种方法,通过使主体成为在您遍历条件序列时调用的函数:

    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"
    

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

    【讨论】:

      【解决方案3】:

      在 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# 是编译型和静态类型的。

      【讨论】:

      • match 表达式和 match 的主要区别是在一个条件下求值,而 cond 你也可以测试不同的条件。
      • @PeterV 是的,这就是我在这里提供if/elif/else 示例的原因,而不是模式匹配。
      • 没错,这解决了问题。我真正想要的是在 clojure 中创建一个与 cond 宏类似的构造,主要是因为我喜欢它的紧凑性。你可以争论这是否有意义。它主要是代码乒乓球。我有点喜欢这些,因为我认为我可以学到很多关于语言的知识。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-21
      • 1970-01-01
      • 2011-05-01
      相关资源
      最近更新 更多