在 Clojure 中解构命令的惯用方法

问题描述 投票:0回答:1

这是我使用 clojure.core.match 编写的一些代码,它执行非常常见的编程任务。函数需要一些“命令”(或“对象”、“记录”,或者任何你喜欢称呼它们的东西),必须对每种类型做不同的事情,并且必须解构它们以弄清楚到底要做什么,以及不同的命令类型可能必须以不同的方式解构:

(defn action->edits [g action]
  "Returns vector of edits needed to perform action in graph g."
  (match action
    [:boost from to]
      [[:add-edge from to 1.0]]
    [:retract from to]
      [[:remove-edge from to]]
    [:normalize from to]       ; a change has just been made to from->to 
      (map (fn [that] [:remove-edge from that])
           (successors-except g from to))
    [:recip-normalize to from] ; a change has just been made to from->to
      []
    [:reduce-to-unofficial from to competitor]
      [[:remove-edge from to] (make-competitive-edge from competitor]))

我主要模仿人们在Scheme中常用的pmatch宏的方式。我想知道在 Clojure 中执行此操作的惯用方法是什么。

以上代码是我喜欢的地方:

  • 非常可读。

  • 写起来毫不费力。

这是我不喜欢的:

  • from
    宏内部以外的任何地方访问
    to
    match
    字段是极其不可读且容易出错的。例如,要从大多数动作向量中提取
    from
    元素,您可以编写
    (action 1)
    。如果我添加新操作,该代码就会中断,并且它现在会在
    :recip-normalize
    上中断。

  • match
    生成的代码效率低下:它通过重复抛出和捕获异常来进行搜索。它不只是生成一个大嵌套
    if

我尝试了一些将命令表示为地图的方法,但它似乎变得冗长,并且命令的名称不太突出,大大降低了可读性:

  (match action
    {:action :boost :from from :to to}
      [{:edit :add-edge :from from :to to :weight 1.0}]
    {:action :retract :from from :to to}
      [{:edit :remove-edge :from from :to to}]
    . . .)

未来版本的

match
可能会生成更好的代码,但是现在生成的糟糕代码(并且缺乏对记录的支持)表明,在Clojure中,人们在没有
match
的情况下已经愉快地处理这种事情很多年了。那么如何在 Clojure 中do做这种事情呢?

clojure pattern-matching record idioms destructuring
1个回答
0
投票

我会利用 clojure 的内置 解构工具,因为我在这里没有看到 core.match 的要求 - 但我可能会遗漏一些东西。

例如:

(defn action->edits [g [action from to]]
  (condp = action
    :boost "boosting"
    :retract "retracting"
    :normalize-ksp-style (recur g [:boost from to])
    nil))

(action->edits 2 [:normalize-ksp-style 1 2]) 
;=> "boosting"
© www.soinside.com 2019 - 2024. All rights reserved.