如何返回带有固定键和条件值的clojure映射?

时间:2021-10-22 21:42:01

I have a function that returns a map. The keys are static, but the values are conditional. Like this:

我有一个返回地图的函数。键是静态的,但值是有条件的。是这样的:

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

Is there some more elegant way to achieve this without needing to write the if test for each value? The only other way I can think of is

是否有更优雅的方法来实现这一点,而不需要为每个值编写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)}))

which doesn't seem much better to me since it repeats the key names for each branch of the conditional and it repeats of the function call on different-test. And heaven forbid that I need a cond instead of just an if.

这对我来说并没有什么好处,因为它重复条件的每个分支的键名,并且在不同的测试上重复函数调用。但愿我需要的不是一个,而是一个。

6 个解决方案

#1


4  

How about this:

这个怎么样:

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

Conditions executed once and in one place, then used in another. Depends on context and personal preference though. Something more like your example may be cleaner:

条件在一个地方执行一次,然后在另一个地方使用。但这取决于上下文和个人喜好。更像你的例子可能更简洁:

(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


4  

Another variation one could think of is to use merge. One case serves as default, which is modified depending on the function arguments. This way you can easily group the tests and spice things up with some comments if necessary.

另一个可以想到的变体是使用merge。一个case作为默认值,根据函数参数进行修改。通过这种方式,您可以轻松地将测试分组,并在必要时添加一些注释。

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

Alternatively the other way around, depending on what you consider to be the default.

或者反过来,这取决于您认为的默认值。

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

#3


2  

Looking at what you're asking i'd say one way to do this would be constructing a function that looks something like this:

看看你的问题,我想说的一种方法是构造一个像这样的函数

(pred-map 
  {:a [some-test-fn "one-value" "other-value"] 
   :b [some-test-fn 15 25] 
   :c [different-test :one :two]} 
  x 
  y)

where x is the argument to all references of first menti0oned function and y to the second one

x是第一个函数的参数,y是第二个函数的参数

a way to achieve this could be following:

实现这一目标的办法可以如下:

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

These functions however rely on arguments being mapped to functions by equality (which seems to go with references) so it will not understand two equal anonymous functions as the same one.

然而,这些函数依赖于被映射为函数的参数(这似乎与引用有关),因此它不会理解两个相同的匿名函数。

if you don't mind repeating the arguments you create a simpler function looking like this:

如果你不介意重复参数,你可以创建一个更简单的函数,如下所示:

(pred-map
  {:a [(some-test-fn x) "one value" "other-value"]
   :b [(some-test-fn x) 15 25]
   :c [(different-test y) :one :two]})

by following simple function:

通过简单的函数:

(defn pred-map [m] (into {} (for [[k [p tv fv]] m] [k (if p tv fv)])))

or in pointfree style:

或者在pointfree风格:

(def pred-map (comp (partial into {}) (partial map (fn [[k [p tv fv]]] [k (if p tv fv)]))))

#4


1  

Yet another way of doing it :-)

还有另一种方法:

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

#5


0  

Your first code example seems most readable to me. Readability is often preferable over efficiency, unless you have a performance critical part of your code. But here's a way to only evaluate your conditionals once. I highly doubt that its performance makes much difference with your code. In terms of elegance I still prefer your first example, since it is clearer to see directly what the key-value pairs are.

您的第一个代码示例对我来说是最有可读性的。可读性常常比效率更可取,除非您的代码中有性能关键部分。但是这里有一种方法可以只计算一次条件。我非常怀疑它的性能对您的代码有多大的影响。就优雅而言,我仍然更喜欢您的第一个示例,因为直接查看键-值对是什么更清楚。

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

#6


0  

By shamelessly stealing others ideas.

无耻地窃取别人的想法。

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

#1


4  

How about this:

这个怎么样:

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

Conditions executed once and in one place, then used in another. Depends on context and personal preference though. Something more like your example may be cleaner:

条件在一个地方执行一次,然后在另一个地方使用。但这取决于上下文和个人喜好。更像你的例子可能更简洁:

(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


4  

Another variation one could think of is to use merge. One case serves as default, which is modified depending on the function arguments. This way you can easily group the tests and spice things up with some comments if necessary.

另一个可以想到的变体是使用merge。一个case作为默认值,根据函数参数进行修改。通过这种方式,您可以轻松地将测试分组,并在必要时添加一些注释。

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

Alternatively the other way around, depending on what you consider to be the default.

或者反过来,这取决于您认为的默认值。

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

#3


2  

Looking at what you're asking i'd say one way to do this would be constructing a function that looks something like this:

看看你的问题,我想说的一种方法是构造一个像这样的函数

(pred-map 
  {:a [some-test-fn "one-value" "other-value"] 
   :b [some-test-fn 15 25] 
   :c [different-test :one :two]} 
  x 
  y)

where x is the argument to all references of first menti0oned function and y to the second one

x是第一个函数的参数,y是第二个函数的参数

a way to achieve this could be following:

实现这一目标的办法可以如下:

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

These functions however rely on arguments being mapped to functions by equality (which seems to go with references) so it will not understand two equal anonymous functions as the same one.

然而,这些函数依赖于被映射为函数的参数(这似乎与引用有关),因此它不会理解两个相同的匿名函数。

if you don't mind repeating the arguments you create a simpler function looking like this:

如果你不介意重复参数,你可以创建一个更简单的函数,如下所示:

(pred-map
  {:a [(some-test-fn x) "one value" "other-value"]
   :b [(some-test-fn x) 15 25]
   :c [(different-test y) :one :two]})

by following simple function:

通过简单的函数:

(defn pred-map [m] (into {} (for [[k [p tv fv]] m] [k (if p tv fv)])))

or in pointfree style:

或者在pointfree风格:

(def pred-map (comp (partial into {}) (partial map (fn [[k [p tv fv]]] [k (if p tv fv)]))))

#4


1  

Yet another way of doing it :-)

还有另一种方法:

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

#5


0  

Your first code example seems most readable to me. Readability is often preferable over efficiency, unless you have a performance critical part of your code. But here's a way to only evaluate your conditionals once. I highly doubt that its performance makes much difference with your code. In terms of elegance I still prefer your first example, since it is clearer to see directly what the key-value pairs are.

您的第一个代码示例对我来说是最有可读性的。可读性常常比效率更可取,除非您的代码中有性能关键部分。但是这里有一种方法可以只计算一次条件。我非常怀疑它的性能对您的代码有多大的影响。就优雅而言,我仍然更喜欢您的第一个示例,因为直接查看键-值对是什么更清楚。

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

#6


0  

By shamelessly stealing others ideas.

无耻地窃取别人的想法。

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