Function for applying to a user with a various num

2020-05-06 12:16发布

The question was born when I was practicing an Observer topic in a tutorial I am trying to apply the function to the user but cannot use user's data fields like name, surname.

Let's say that the user may have various number of data fields so we must use & args argument. My code that does not work:

(ns observer.core)
(def user {:name "Alan" :surname "Smith" :alias "Mike"})
(def user2 {:name "Jane" :surname "Smith"})
(apply
 (fn [& args] (println (str "I am " (:name args) " " (:surname args) ".")))
   user)
(apply
 (fn [& args] (println (str "My sister is " (:name args) " " (:surname args) ".")))
   user2)

The output:

I am  .
My sister is  .
observer.core> 

How to fix it regarding that the apply function must be used?

4条回答
Anthone
2楼-- · 2020-05-06 12:46

One has to wrap a user object or the map by a list.

(ns observer.core)
(defrecord Person [name balance])
(def user (Person. "Alan" 150.34))
(def user2 {:name "Adam" :balance 629.74})
(def observers (atom #{}))
(swap! observers conj (fn [l] (println (str "2. " (:name l)))))
(swap! observers conj (fn [l] (println (str "1. " (:balance l)))))
(println "user")
(vec (map #(apply % (list user)) @observers))
(println "\nuser2")
(vec (map #(apply % (list user2)) @observers))

Output

user
1. 150.34
2. Alan

user2
1. 629.74
2. Adam
observer.core>
查看更多
小情绪 Triste *
3楼-- · 2020-05-06 12:47

apply converts a map to a seq, i.e. {:name "Alan" :surname "Smith" :alias "Mike"} becomes ([:name "Alan"] [:surname "Smith"] [:alias "Mike"])

You could put it back into a map, if that is what you need:

(let [user {:name "Alan" :surname "Smith" :alias "Mike"}]
    (apply
        (fn [& args]
            (let [args (into {} args)]
                (println (str "I am " (:name args) " " (:surname args) "."))))
        user))

but this looks a bit of a stretch to me. I believe the solution could have been better if I knew how this function is supposed to be used.

Usually there are two types of functions: (fn :text "some" :row 25) and (fn {:text "some" :row 25}).

查看更多
放我归山
4楼-- · 2020-05-06 12:59

In the spirit of learning:

Check out Clojure - Cheatsheet. 10 years with Clojure, and I still use it daily.

(apply some-func (list x y z)) becomes (some-func x y z), because apply assumes that the second argument is a list (which it then unpacks).

And what you are currently doing is collecting all the arguments back into a list called args

(def user {:name "Alan" :surname "Smith" :alias "Mike"})
(apply
  (fn [& args]
    (prn 'ARGS args)  ;; lets see what is actually in args
    (println (str "I am " (:name args) " " (:surname args) ".")))
  user)
;; -> ARGS ([:name "Alan"] [:surname "Smith"] [:alias "Mike"])
;; -> I am  .

And the outut is as @akond says.

You could, of course, put 'user' in a vector (or list), but then don't use '&' to collect everything back into a list, (which you would then have to pick stuff out of again):

(def user {:name "Alan" :surname "Smith" :alias "Mike"})
(apply
  (fn [args]
    (prn 'ARGS args) 
    (println (str "I am " (:name args) " " (:surname args) ".")))
  [user])

That would give you the output you expected. But this is a bit strange, perhaps, but certainly viable if you must use apply and you can control the "list" part of the argument.

So, @akond's solution is simple and clean.
And augmenting it with Clojure "destructing":

(def user {:name "Alan" :surname "Smith" :alias "Mike"})
(apply
  (fn [& args]
    (let [{:keys [name surname alias]} (into {} args)]
      (println (str "I am " name " " surname "." (when alias (str " But call me " alias "!"))))))
  user)
查看更多
\"骚年 ilove
5楼-- · 2020-05-06 13:03

I believe you intended to do something like this:

(def user  {:name "Alan" :surname "Smith" :alias "Mike"})
(def user2 {:name "Jane" :surname "Smith"})

(defn fn-1
  [item]
   (println (str "I am " (:name item) " " (:surname item) ".")) )
(defn fn-2
  [item]
  (println (str "My sister is " (:name item) " " (:surname item) ".")))

  (fn-1 user)
  (fn-2 user2)

with result:

I am Alan Smith.
My sister is Jane Smith.
查看更多
登录 后发表回答