如何返回具有固定键和条件值的clojure映射?
我有一个返回映射的函数。键是静态的,但值是有条件的。像这样:如何返回具有固定键和条件值的clojure映射?,clojure,idioms,Clojure,Idioms,我有一个返回映射的函数。键是静态的,但值是有条件的。像这样: (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 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)}))
这对我来说似乎没有什么好处,因为它重复了条件的每个分支的键名,并且在
不同的测试中重复了函数调用。如果
你的第一个代码示例对我来说似乎是最可读的,那我就不需要一个条件,而只需要一个。可读性通常优于效率,除非代码中有性能关键的部分。但是这里有一种方法只对条件句求值一次。我非常怀疑它的性能与您的代码有多大区别。就优雅而言,我仍然更喜欢您的第一个示例,因为直接查看键值对更为清晰
(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))))
这个怎么样:
(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)})
人们可以想到的另一种变体是使用合并
。一种情况作为默认情况,根据函数参数进行修改。这样,您可以轻松地对测试进行分组,并在必要时添加一些注释
(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})))
看看你要问的问题,我想说一种方法是构造一个如下所示的函数:
(pred-map
{:a [some-test-fn "one-value" "other-value"]
:b [some-test-fn 15 25]
:c [different-test :one :two]}
x
y)
(pred-map
{:a [(some-test-fn x) "one value" "other-value"]
:b [(some-test-fn x) 15 25]
:c [(different-test y) :one :two]})
其中x是第一个指定函数的所有引用的参数,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 "one-value" "other-value"]
:b [some-test-fn 15 25]
:c [different-test :one :two]}
x
y)
(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)]))))
还有另一种方法:-)
无耻地窃取别人的想法
(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))))
您当然可以编写一些聪明的代码来消除if。。。。然而,仅仅为了避免键入三个或四个字符,就真的值得额外的间接性和/或复杂性吗??在这种情况下,你可能需要运用接吻原则。@mikera:我同意你的观点,但我明白,为了让这个例子更清楚,我把它简化了一点,作为问题的一部分。我有点希望有人能拿出一些疯狂的macro'n skilz,告诉我它是如何完成的。@站,假设你定义了很多这些条件映射,我建议写一个宏,在这个答案中生成后一个代码。这将提供最高的效率和可读性。宏将接受一个四元组的列表,一个键,测试fn,参数,值如果为真,值如果为假。我没有考虑过使用conj
,所以我必须尝试一下(或者可能是merge
)。我怀疑可能需要一个宏,所以我很高兴听到@bmillare表示同意。思考一个广义的宏观解决方案,这实际上取决于范围。我可以想象,在某些情况下,这甚至可能导致逻辑编程和规则引擎的出现。:-)宏是朝这个方向迈出的一步。很好!我曾想过类似的方法,但很高兴看到它实现了。我认为如果有一个自然的默认映射,这是有意义的。