一个cond-> threading宏替代方案,它引用谓词中的最后一个线程状态?

时间:2018-08-23 19:45:02

标签: clojure

说我有这个fn

(let [{:keys [a b c d] :as params} {:a 1 :b 1 :c nil :d nil}] 
  (cond-> params
    a       (update :b inc)
    (= b 2) (assoc :c "here")
    c       (assoc :d "here")))

我会得到什么

;;=> {:a 1, :b 2, :c nil, :d nil}

我想要什么:

;;=> {:a 1, :b 2, :c "here", :d "here"}

可能的语法:

(let [params {:a 1 :b 1 :c nil :d nil}] 
  (cond$-> params
    (:a $)       (update :b inc)
    (= (:b $) 2) (assoc :c "here")
    (:c $)       (assoc :d "here")))

此解决方案或类似解决方案是否已在某处实施?

2 个答案:

答案 0 :(得分:3)

这里有一个简单的实现方式:

(defmacro cond$->
  ([value] value)
  ([value cond body & clauses]
   (assert (even? (count clauses)))
   `(cond$-> (let [~'$ ~value]
               (if ~cond (-> ~'$ ~body) ~'$))
             ~@clauses)))

基本上,它只是在第一个条件和主体上创建一个表单。如果条件匹配,则除非使用原始值,否则下一个cond$->调用的值将为body。它使用递归来处理所有子句。

通常,最好让用户选择将用于绑定值的符号:

(defmacro cond-as->
  ([value sym] value)
  ([value sym cond body & clauses]
   (assert (even? (count clauses)))
   `(cond-as-> (let [~sym ~value]
                 (if ~cond ~body ~sym))
               ~sym
               ~@clauses)))


(let [params {:a 1 :b 1 :c nil :d nil}]
  (cond-as-> params $
             (:a $)       (update $ :b inc)
             (= (:b $) 2) (assoc $ :c "here")
             (:c $)       (assoc $ :d "here")))

答案 1 :(得分:1)

我可以看到这可能有用。您可以找到my take on it here

(let [params {:a 1 :b 1 :c nil :d nil}]
  (cond-it-> params
    (:a it)        (update it :b inc)
    (= (:b it) 2)  (assoc it :c "here")
    (:c it)        (assoc it :d "again")))

;=> {:a 1, :b 2, :c "here", :d "again"}

实施:

(defmacro cond-it->
  [expr & forms]
  (let [num-forms (count forms)]
    (when-not (even? num-forms)
      (throw (IllegalArgumentException. (str "num-forms must be even; value=" num-forms)))))
  (let [cond-action-pairs (partition 2 forms)
        cond-action-forms (for [[cond-form action-form] cond-action-pairs]
                            `(or (when ~cond-form) ~action-form)) ]
    `(it-> ~expr ~@cond-action-forms)))