Функция для применения к пользователю с различным количеством полей данных

#clojure #parameter-passing #apply

#clojure #передача параметров

Вопрос:

Вопрос возник, когда я практиковал тему наблюдателя в учебном пособии. Я пытаюсь применить функцию к пользователю, но не могу использовать поля данных пользователя, такие как имя, фамилия.

Допустим, у пользователя может быть разное количество полей данных, поэтому мы должны использовать amp; args аргумент. Мой код, который не работает:

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

На выходе:

 I am  .
My sister is  .
observer.core> 
 

Как это исправить в отношении того, что apply функция должна быть использована?

Ответ №1:

apply преобразует карту в seq, т. е. {:name "Alan" :surname "Smith" :alias "Mike"} становится ([:name "Alan"] [:surname "Smith"] [:alias "Mike"])

Вы могли бы поместить его обратно на карту, если это то, что вам нужно:

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

но для меня это выглядит немного натянуто. Я считаю, что решение могло бы быть лучше, если бы я знал, как предполагается использовать эту функцию.

Обычно существует два типа функций: (fn :text "some" :row 25) и (fn {:text "some" :row 25}) .

Комментарии:

1. Так что, возможно, пользователь должен быть сконструирован по-другому. Я пытаюсь создать функции и пользователя, которые не представлены в руководстве, на которое я поместил ссылку в вопросе.

2. Вы хотите назвать это как (f user) , где user находится объект пользователя?

3. ДА. Я думаю, что автор учебника так и предполагал.

4. Но в вашем вопросе не упоминается тот факт, что вам нужен объект User. У вас есть карта.

5. Так что, возможно, я задал неправильный вопрос. Является ли объект созданным функцией deftype в Clojure?

Ответ №2:

В духе обучения:

Ознакомьтесь с Clojure — Cheatsheet. 10 лет с Clojure, и я все еще использую его ежедневно.

(apply some-func (list x y z)) становится (some-func x y z) , потому apply что предполагает, что вторым аргументом является список (который он затем распаковывает).

И то, что вы сейчас делаете, собирает все аргументы обратно в список, вызываемый args

 (def user {:name "Alan" :surname "Smith" :alias "Mike"})
(apply
  (fn [amp; 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  .
 

И выход такой, как говорит @akond.

Вы могли бы, конечно, поместить ‘user’ в вектор (или список), но тогда не используйте ‘amp;’, чтобы собрать все обратно в список (из которого вам затем придется снова выбирать материал):

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

Это даст вам результат, который вы ожидали. Но это, возможно, немного странно, но, безусловно, жизнеспособно, если вы должны использовать apply и можете управлять частью аргумента «список».

Итак, решение @akond простое и понятное.
И дополняя его с помощью Clojure «destructing»:

 (def user {:name "Alan" :surname "Smith" :alias "Mike"})
(apply
  (fn [amp; args]
    (let [{:keys [name surname alias]} (into {} args)]
      (println (str "I am " name " " surname "." (when alias (str " But call me " alias "!"))))))
  user)
 

Ответ №3:

Я полагаю, что вы намеревались сделать что-то подобное:

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

с результатом:

 I am Alan Smith.
My sister is Jane Smith.
 

Комментарии:

1. Я пытаюсь создать функции и пользователя, которые не представлены в руководстве в заголовке Observer. Я поместил ссылку на учебное пособие в вопрос. Как можно видеть, там используется функция apply, поэтому в вопросе я сказал использовать функцию apply.

Ответ №4:

Нужно обернуть пользовательский объект или карту списком.

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

Вывод

 user
1. 150.34
2. Alan

user2
1. 629.74
2. Adam
observer.core>