简体   繁体   English

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

[英]Idiomatic way to destructure commands in Clojure

Here's some code I wrote, using clojure.core.match , which performs a pretty common programmng task. 这是我编写的一些代码,使用clojure.core.match ,它执行一个非常常见的programmng任务。 A function takes some "commands" (or "objects", "records", or whatever you prefer to call them), has to do something different with each type, and has to destructure them to figure out exactly what to do, and different command types might have to be destructured differently: 一个函数需要一些“命令”(或“对象”,“记录”,或者你喜欢称之为的任何东西),必须对每种类型做一些不同的事情,并且必须对它们进行解构以弄清楚要做什么,以及不同的命令类型可能必须以不同方式进行解构:

(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]))

I'm mostly imitating the way people commonly use the pmatch macro in Scheme. 我主要模仿人们在Scheme中使用pmatch宏的方式。 I'd like to know what's the idiomatic way to do this in Clojure. 我想知道在Clojure中这样做的惯用方法是什么。

Here's what I like about the above code: 以下是我喜欢的上述代码:

  • It's very readable. 非常易读。

  • It was effortless to write. 写作毫不费力。

Here's what I don't like: 这是我不喜欢的:

  • Accessing the from and to fields from anywhere but inside a match macro is extremely unreadable and error-prone. from match宏内部的任何地方访问fromto字段是非常难以理解的,并且容易出错。 For example, to extract the from element from most of the action vectors, you write (action 1) . 例如,要从大多数动作向量中提取from元素,请编写(action 1) That code will break if I ever add a new action, and it breaks right now on :recip-normalize . 如果我添加一个新动作,该代码将会中断,并且它现在会中断:recip-normalize

  • The code generated by match is inefficient: it searches by repeatedly throwing and catching exceptions. match生成的代码效率低下:它通过反复抛出和捕获异常进行搜索。 It doesn't just generate a big nested if . 它不只是生成一个大的嵌套if

I experimented a little with representing the commands as maps, but it seemed to get verbose, and the name of the command doesn't stand out well, greatly reducing readability: 我尝试了一些将命令表示为地图,但它似乎变得冗长,并且命令的名称不能很好地显示,大大降低了可读性:

  (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}]
    . . .)

Probably future versions of match will generate better code, but the poor code generated now (and lack of support for records) suggests that in Clojure, people have been handling this kind of thing happily for years without match . 可能未来的match版本会生成更好的代码,但现在生成的代码很糟糕(并且缺乏对记录的支持)表明,在Clojure中,人们已经多年来一直在愉快地处理这类事情而没有match So how do you do this kind of thing in Clojure? 那么你如何做这种事情在Clojure的?

I would utilize clojure's build-in destructuring facilities , since I do not see a requirement for core.match here - but I might be missing something. 我会利用clojure的内置解构工具 ,因为我在这里看不到core.match的要求 - 但我可能会遗漏一些东西。

For example: 例如:

(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"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM