#methods #clojure #namespaces
#методы #clojure #пространства имен
Вопрос:
У меня есть пространство имен, реализующее подобный мультиметод:
(ns a)
(defmulti funca (fn [system data] system))
(defmethod funca :add-z
[system data]
(conj data {:z 26}))
У меня есть 2-е пространство имен, реализующее другой мультиметод:
(ns b)
(defmulti funcb (fn [system data new-element] system))
(defmethod funcb :add-element
[system data new-element]
(conj data new-element))
У меня есть 3-е пространство имен, определенное как
(ns c
(:require [a :refer [funca]]
[b :refer [funcb]]))
Я хочу создать метод в пространстве имен c, который я могу вызывать как, например
(funcc (funca :add-z {:a 1 :b 2 :c 3})
(funcb :add-element {:y 25}))
Это должно принять результат (funca :add-z {:a 1 :b 2 :c 3})
(который был бы {:a 1 :b 2 :c 3 :z 26}
) и использовать этот результат в качестве входных данных для (funcb :add-y-and-count-elements {:y 25})
, который затем вернет результат {:a 1 :b 2 :c 3 :y 25 :z 26}
.
Пытаясь определить funcc
, я пытаюсь делать такие вещи, как
(ns c
(:require [a :refer [funca]]
[b :refer [funcb]]))
(defn funcc [funca funcb]
(partial funcb funca))
или
(defn funcc [funca funcb]
(funcb (funca)))
или
(defn funcc [funca funcb]
(-> funca
funcb))
но при запуске любого из них в REPL и вызове funcc я получаю
Неправильное количество аргументов (2), переданных в: b/eval19367/fn
Каков правильный синтаксис для определения funcc
, чтобы он мог принимать выходные данные первого переданного ему параметра, использовать его в качестве входных данных для второго переданного параметра, а затем возвращать результат?
Ответ №1:
Это должно принять результат
(funca :add-z {:a 1 :b 2 :c 3})
(который был бы{:a 1 :b 2 :c 3 :z 26})
и использовать этот результат в качестве входных данных(funcb :add-y-and-count-elements {:y 25})
, который затем вернул бы результат{:a 1 :b 2 :c 3 :y 25 :z 26}
Согласно этому описанию, ваш funcc
фактически принимает только один аргумент (результат вызова funca
), поэтому он должен быть определен следующим образом:
(defn funcc [data]
(funcb :add-element data {:y 25}))
и вызовите его:
(funcc (funca :add-z {:a 1 :b 2 :c 3}))
Если вы всегда хотите вызывать funcc
с результатом, funca
применяемым к add-z
, с некоторыми данными, вы могли бы изменить его на:
(defn funcc [data]
(let [result-from-funca (funca :add-z data)]
(funcb :add-element result-from-funca {:y 25})))
и вызовите его:
(funcc {:a 1 :b 2 :c 3})
Ответ №2:
Используйте as-> macro
(as-> {:a 1 :b 2 :c 3} data
(funca :add-z data)
(funcb :add-element data {:y 25}))
Если funca
и funcb
приняты данные в первой позиции аргумента, вы могли бы использовать ->
стрелку. Если оба принятых данных находятся в последней позиции, вы могли бы использовать ->>
и даже comp
, например.
(def funcc (comp (partial funcb :add-element {:y 25})
(partial funca :add-z)))
В зависимости от того, какие преобразования станут наиболее полезными для функций, которые вы пишете, создайте их подписи на основе общих инструментов композиции Clojures, которыми являются стрелки и comp.