我有一个返回地图的函数。键是静态的,但值是有条件的。像这样:
(defn map-returning-function [x y]
{:a (if (some-test-fn x) "one value" "other value"
:b (if (some-test-fn x) 15 25
:c (if (different-test y) :one :two})
是否有更优雅的方法来实现这一点,而无需为每个值编写if
测试?我能想到的另一种方式是
(defn another-map-returning-function [x y]
(if (some-test-fn x)
{:a "one value", :b 15, :c (if (different-test y) :one :two)}
{:a "other value" :b 25, :c (if (different-test y) :one :two)}))
这对我来说似乎没有那么好,因为它重复了条件的每个分支的键名,并重复了different-test
上的函数调用。天堂禁止我需要cond
而不是if
。
答案 0 :(得分:5)
可以想到的另一个变体是使用merge
。一个案例用作默认值,根据函数参数进行修改。通过这种方式,您可以轻松地对测试进行分组,并在必要时添加一些注释。
(defn map-returning-function
[x y]
(merge {:a "other value"
:b 25
:c :two}
(when (some-test-fn x)
{:a "one value"
:b 15})
(when (different-test y)
{:c :one})))
另一种方式,取决于您认为是默认值。
(defn map-returning-function
[x y]
(merge {:a "one value"
:b 15
:c :one}
(when-not (some-test-fn x)
{:a "other value"
:b 25})
(when-not (different-test y)
{:c :two})))
答案 1 :(得分:4)
这个怎么样:
(let [test-x (some-test x) test-y (some-test y)]
(conj
(if test-x {:a "one value" :b 15} {:a "other value" :b 25})
(if test-y {:c :one} {:c :two})))
条件在一个地方执行一次,然后在另一个地方使用。取决于背景和个人偏好。更像你的例子可能更干净:
(let [test-x (some-test x) test-y (some-test y)]
{:a (if test-x "one value" "other value")
:b (if test-x 15 25)
:c (if test-y :one :two)})
答案 2 :(得分:2)
看看你问的问题我会说一种方法就是构建一个看起来像这样的函数:
(pred-map
{:a [some-test-fn "one-value" "other-value"]
:b [some-test-fn 15 25]
:c [different-test :one :two]}
x
y)
其中x是第一个menti0oned函数的所有引用的参数,y是第二个函数的参数
实现这一目标的方法可能如下:
(defn get-value [p tv fv a]
(if (p a) tv fv))
(defn get-predicate-set [m]
(set (map first (vals m))))
(defn get-arg-map [m args]
(zipmap (get-predicate-set m) args))
(defn get-arg [p m args]
((get-arg-map m args) p))
(defn get-key-value-pair-creator [m args]
(fn [[k [p tv fv]]]
[k
(get-value
p
tv
fv
(get-arg p m args))]))
(defn pred-map [m & args]
(into {}
(map
(get-key-value-pair-creator m args)
m)))
然而,这些函数依赖于通过相等映射到函数的参数(它似乎与引用一起使用),因此它不会将两个相同的匿名函数理解为相同的函数。
如果你不介意重复参数,你可以创建一个更简单的函数,如下所示:
(pred-map
{:a [(some-test-fn x) "one value" "other-value"]
:b [(some-test-fn x) 15 25]
:c [(different-test y) :one :two]})
遵循简单的功能:
(defn pred-map [m] (into {} (for [[k [p tv fv]] m] [k (if p tv fv)])))
或以无点样式:
(def pred-map (comp (partial into {}) (partial map (fn [[k [p tv fv]]] [k (if p tv fv)]))))
答案 3 :(得分:1)
另一种方法: - )
(defmulti map-returning-function
(fn [x y] [ (some-test-fn x) (different-test y) ]))
(let [x-values {true {:a "one value" :b 15}
false {:a "other value" :b 25}}
y-values {true {:c :one} false {:c :two}}]
(defmethod map-returning-function [false false] [x y]
(merge (x-values false) (y-values false)))
(defmethod map-returning-function [true true] [x y]
(merge (x-values true) (y-values true)))
...)
答案 4 :(得分:0)
您的第一个代码示例对我来说似乎最具可读性。可读性通常优于效率,除非您的代码具有性能关键部分。但这是一种只评估你的条件一次的方法。我非常怀疑它的性能与你的代码有很大不同。在优雅方面,我仍然更喜欢你的第一个例子,因为直接看到键值对是更清楚的。
(defn another-map-returning-function [x y]
(let [first-map (if (some test-fn x) {:a "one value" :b 15}
{:a "other value" :b 25})]
(assoc first-map :c
(if (different-test y) :one :two))))
答案 5 :(得分:0)
通过无耻地窃取他人的想法。
(defn map-returning-function [x y]
(let [x-values {true {:a "one value" :b 15}
false {:a "other value" :b 25}}
y-values {true {:c :one} false {:c :two}}
x-key (some-test-fn x)
y-key (different-test y) ]
(merge (x-values x-key) (y-values y-key))))