#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>