如何在Clojure中对向量进行条件转换

5
有没有更简洁的方式在Clojure中实现以下操作?
(defn this [x] (* 2 x))
(defn that [x] (inc x))
(defn the-other [x] (-> x this that))

(defn make-vector [thing]
  (let [base (vector (this (:a thing))
                     (that (:b thing)))]
    (if-let [optional (:c thing)]
      (conj base (the-other optional))
      base)))

(make-vector {:a 1, :b 2}) ;=> [2 3]
(make-vector {:a 1, :b 2, :c 3}) ;=> [2 3 7]

By "cleaner" I mean something closer to this:

(defn non-working-make-vector [thing]
  (vector (this (:a thing))
          (that (:b thing))
          (if (:c thing) (the-other (:c thing)))))

(non-working-make-vector {:a 1, :b 2} ;=> [2 3 nil]  no nil, please!
(non-working-make-vector {:a 1, :b 2, :c 3} ;=> [2 3 7]

请注意,我可能想在thing中的任何键上调用一些任意函数(例如thisthatthe-other),并将结果放入返回的向量中。重要的是,如果该键不存在于映射中,则不应在向量中放置nil
这类似于此问题,但输出是向量而不是映射,因此我不能使用merge

1
你难道不想要 (vec (vals thing)) 吗? - Diego Basch
@DiegoBasch:是的,那就是他想要的。 - Ankur
不好意思,@DiegoBasch,我想能够在给定的键上调用任意函数。我已经编辑了示例以使其更清晰。 - stand
3个回答

2
(defn this [x] (* 2 x))
(defn that [x] (inc x))
(defn the-other [x] (-> x this that))

(def k-f-map {:a this
              :b that
              :c the-other})

(def m1 {:a 1 :b 2})
(def m2 {:a 1 :b 2 :c 3})

(defn make-vector [k-f-map m]
  (reduce (fn [res [fk fv]]
            (if (fk m)
              (conj res (fv (fk m)))
              res))
          [] k-f-map))

(make-vector k-f-map m1)
-> [2 3]

(make-vector k-f-map m2)
-> [2 3 7]

1
这可能是stand正在寻找的答案。作为一个小调整,在Clojure 1.4中,reduce-kv更适合这项工作。 - Michał Marczyk

1
;;; replace [:a :b :c] with a vector of arbitrary functions
;;; of your choice, or perhaps accept a seqable of functions
;;; as an extra argument
(defn make-vector [thing]
  (into [] (keep #(% thing) [:a :b :c])))

;;; from the REPL:
(make-vector {:a 1 :b 2})
; => [1 2]
(make-vector {:a 1 :b 2 :c 3})
; => [1 2 3]

请注意,keep 只会排除 nilfalse 将包含在输出中。

如果需要将 false 一并丢弃,可以使用 (filter identity (map #(% thing) ...)) 替换 keep 调用。 - Michał Marczyk
请看我的编辑。是否可以使用 keep 调用一些任意函数?我想我可以有一些总函数,根据键名进行分派,但这开始变得丑陋了。 - stand
如答案所述,您可以将任意函数放入传递给keep的可序列中,在make-vector内部。特别地,您可以用[(comp this :a) (comp that :b) (comp the-other :c)]替换[:a :b :c],以获得一个版本的non-working-make-vector,它实际上从输出中省略了任何nil(就像当前包含在问题文本中的那样)。 (当然,您也可以将(comp this :a)拼写为#(this (:a %))。)另请参见(doc keep)(基本上它的工作方式类似于map,但会从输出中丢弃nil)。 - Michał Marczyk
嗯,这并没有达到我所寻找的内容。我的例子不好。是完全重新编辑问题还是删除它并重新提问更好? - stand
好的,如果我理解正确的话,您想在调用值的函数之前进行过滤(以检查是否存在关键字),这样您就不需要在nil上调用thisthatthe-other。在这种情况下,mobyte的答案应该适合您。 - Michał Marczyk

0

或者使用cond->

您在cond->版本中的make-vector函数:

(defn make-vector [thing]
  (cond-> [(this (:a thing))
           (that (:b thing))]
    (:c thing) (conj (the-other (:c thing)))))

你可以有更多的条件或将:a和:b也改为可选。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接