Clojure中解构命令的惯用方法

Clojure中解构命令的惯用方法,clojure,pattern-matching,record,idioms,destructuring,Clojure,Pattern Matching,Record,Idioms,Destructuring,下面是我使用编写的一些代码,它执行一个非常常见的编程任务。函数接受一些“命令”(或“对象”、“记录”或您喜欢调用它们的任何内容),必须对每种类型执行不同的操作,并且必须对它们进行分解以确定要执行的操作,不同的命令类型可能必须以不同的方式进行分解: (defn action->edits [g action] "Returns vector of edits needed to perform action in graph g." (match action [:boost

下面是我使用编写的一些代码,它执行一个非常常见的编程任务。函数接受一些“命令”(或“对象”、“记录”或您喜欢调用它们的任何内容),必须对每种类型执行不同的操作,并且必须对它们进行分解以确定要执行的操作,不同的命令类型可能必须以不同的方式进行分解:

(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中使用宏的方式。我想知道Clojure的惯用方法是什么

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

  • 它可读性很强

  • 写起来很容易

以下是我不喜欢的:

  • match
    宏内部以外的任何位置访问
    from
    to
    字段都是非常不可读且容易出错的。例如,要从大多数动作向量的元素中提取
    ,可以编写
    (动作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中实现这种功能呢?

我会利用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"

为什么不
(defn action->edits[g[action f t]]
(condp=action:boost“boosting”
,或者我遗漏了什么?-换句话说,为什么不首先分解fn参数?@birdspider谢谢,
condp
可能正是我所需要的!我只使用了Clojure几天,因此我写了这篇文章“Clojure方案“。发布一个答案!顺便说一句,在我的示例中,所有记录都有相同的两个参数。通常当我做这种事情时,它们会有所不同。我现在将修改示例,因为我对如何分解变体记录的习惯用法感兴趣,但在我的实际代码中,我将按照您的建议利用通用模式。:)谢谢,这可能正是Clojure noob需要的。三个问题:1)什么是“ksp风格”?(?)2)如果不同的操作具有不同的参数,是否有一种简单的方法来分解它们?3) 您通常如何访问代码中其他位置操作的
from
to
字段<代码>(动作1)
(动作2)
,总是进行解构,最好使用地图而不是向量,还有别的吗?@BenKovitz 1)是的!我刚刚测试了ksp 1.1prerelease,您的boosting问题出现了-无法抗拒。2) 你可以在(map)键上进行解构(参见我提供的链接-向下滚动),3)在可读性方面为什么不在
(让[[a f t]动作]
-但我想没有硬性规则(除了性能,那么你需要
defrecord
s)