简体   繁体   中英

Clojure: Function to determine map's value

The more I think about this problem, the more wrong it seems...

I have defined in my program something like a 'map constructor'. The idea behind this is that I have a generic map structure to handle some 'items' but I want to enforce some defaults for specific kind of items.

The problem that I have is that this 'map constructor' has a kv pair, and that pair's value should be determined by the function that consumes this map (it might get clearer in the following example).

My first idea was to quote an expression in the value and then do an eval on it in the said function. The second idea was to replace the value with a fn , but this seems to return something similar to the quoted expression.

Let me try to depict the problem:

  • The model resulting map should be something like {:a 1 :b 2 :c 3}
  • The constructor is something like

     (defn cons-field [b] {:a (fn [name] (str name "!")) :bb :c "default"}) 
  • The item is created (def a-field (cons-field 5))

  • The calling function that consumes the map is something like

      (defn the-function [name field] (str (get-in field [:a]))) 

Now what I need is this :a's value to be a function of the parameter name in 'the-function'. Of course the last function is not working and I'm not sure if it's the correct approach anyway. The ':a' key is not always a fn ; sometimes it's just a string literal. Any ideas?

Cheers!

So this is how I solved this problem after the comments of A. Webb and Jeremy Heiler.

The initial constructor was changed to this:

(defn cons-field [b]
  {:a nil ; either delete completely or comment that
          ; the case will be handled by the caller
   :flag xx ; true or :case-qualifier
   :b b
   :c "default"})

The calling func was changed to this:

(defn the-function [name field]
  (let [case-q (:flag field)]
    (cond
      (= case-q :case-qualifier) (get-name name) ; you can have many different cases
                                              ; conciser using constants for these qualifiers
      (...) ()))) ; else as normal

Then the logic initially put in the map goes in a different func:

(defn get-name [name] (str name "!"))

Hope this helps someone else :)

It is not really possible to understand your problem based on what you have posted. All I can do for you is tell you what your provided code does and guess what you would want it to do.

(def r (cons-field 5)) creates a hash-map r with (r :b) = 5, (r :c) = "default" and (r :a) = (fn [name] (str name "!")) . As you can see, neither the result of (r :a) nor the result of (r :c) is related to r or 5.

If the results described above is what you want, it would be only logical to do some refactoring:

(def default-field {:a (fn [name] (str name "!"))
                    :c "default"})

(def cons-field (partial assoc default-field :b))

Regarding the-function : A call to (get-in field [:a]) is the same as (field :a) and will return the function (fn [name] ...) . the-function will stringify this fn using str and most likely return something like "user.fn$23092...."

If you want the-function to return the result of calling (fn [name] ...) with name as passed to the function, you need to change your the-function as follows:

(defn the-function 
  [name field]
  (str ((field :a) name)))

If you want the function to return something else based on the actual value of :a, you could check if it is a function and invoke it with name, otherwise return the value.

(defn the-function
  [name field]
  (when-let [v (field :a)]
    (or (when (fn? v)
          (v name))
        v)))

Reading from your own answer now I am even more confused what actual problem you are trying to solve, but I hope that this answer could provide help.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM