Построение Clojure defmulti / defmethod - PullRequest
4 голосов
/ 17 декабря 2011

Я упускаю важный момент о defmulti и defmethod. Я прочитал объяснение defmulti в нескольких книгах, и я все еще в замешательстве.

Я хочу получить случайное значение в зависимости от того, является ли это транзакция или сумма, например, 100,00

Я хочу позвонить (random-val) и либо получить обратно значение в качестве значения смещения или случайное десятичное число. Я экспериментировал с размещением функций на карте, но я получаю то же значение обратно для aid-trans, a \ B.

(def^:dynamic map-val {:trans (random-trans) :amt (random-amount)})

Вот наименьшее количество кода, чтобы показать, что я делаю, что не работает. Буду признателен за любые указания или помощь.

(def^:dynamic avail-trans [\B \W \D \A])

(defn random-trans 
    [] 
    (nth avail-trans (.nextInt random (count avail-trans))))

(defn random-amount
    []
    (float (/ (.nextInt random (count (range 1 10000))) 25 )))

Следующее не правильно составлено, но я не уверен, почему или как решить проблему:

(defmulti random-val :val-type)

(defmethod random-val :trans []
    (random-trans))

(defmethod random-val :amt []
    (random-amount))

Вызов (random-val :trans) приводит к этой ошибке:

java.lang.IllegalArgumentException: в мультиметоде 'random-val' нет метода для значения отправки: null (NO_SOURCE_FILE: 0)

Ответы [ 2 ]

7 голосов
/ 17 декабря 2011

Мультиметод создается с defmulti; Вы делаете это правильно. defmulti нужны имя и функция отправки (и строка документации, а также некоторые опции, если вы хотите, но забудьте о них).

(defmulti random-val identity) 

Когда вы реализуете мультиметод с помощью defmethod, вам необходимо указать имя внедряемого мультиметода, значение отправки, которому он должен соответствовать, а затем функцию tail (arglist плюс все, что вы хотите, чтобы он делал).

(defmethod random-val :trans [t] (random-trans))
(defmethod random-val :amt [t] (random-amt))

Вы получаете java.lang.IllegalArgumentException: No method in multimethod 'random-val' for dispatch value: null (NO_SOURCE_FILE:0), потому что когда назначенная вами функция диспетчеризации random-val, :val-type применяется к любому другому ключевому слову, оно дает вам null. Когда Clojure пытается найти метод, соответствующий этому значению диспетчеризации, происходит сбой.

Но даже если это не сработало там, ваши определенные методы имеют 0 арность (не принимают значений), так что вам тоже нужно это исправить (сделано выше).

Наконец, это не похоже на хорошее использование протоколов. Просто используйте две отдельные функции, random-amount и random-trans.

Обратите внимание, что веб-сайт Clojure имеет хорошее объяснение мультиметодов.

1 голос
/ 17 декабря 2011

Вы возвращаете одно и то же значение каждый раз для vend-trans '\B', потому что вы оцениваете функцию, когда связываете ее с картой map-val, тем самым связывая значение B навсегда с ключом ':trans 'in map-val, вместо самой функции randon-trans.

Если вы удалите скобки вокруг своих назначений функций в map-val, все будет работать нормально.Тогда нет необходимости в мультиметодах, которые, вероятно, не подходят, как предполагает @ isaac-hodes.

Это работает для меня в REPL:

(def avail-trans [\B \W \D \A])
(def random (java.util.Random.))

(defn random-trans []
  (nth avail-trans (.nextInt random (count avail-trans))))

(defn random-amount []
  (float (/ (.nextInt random (count (range 1 10000))) 25 )))

; No parens around function names
(def map-val {:trans random-trans :amt random-amount})

(println ((:trans map-val)))
(println ((:amt map-val)))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...