Несколько пространств имен; попытка вызвать метод в одном, передача выходных данных другому методу в рамках 3-го метода

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