Я реализовал быстрый алгоритм питания в Clojure:Clojure mutimethods для отправки по типу
(defn fast-pow [a n]
(cond (zero? n) 1
(even? n) (letfn [(square [x] (*' x x))]
(square (fast-pow a (/ n 2))))
:else (*' a (fast-pow a (dec' n)))))
А теперь я хочу играть с намеками типа и Java Interop. Я хочу сделать это из-за лучшего понимания всего этого «java-материала» в clojure. Похоже, их довольно легко, но на самом деле есть много скрытых препятствий. Итак, я написал:
(defn ^java.math.BigInteger fast-pow
([^java.lang.Long a ^java.lang.Long n]
(fast-pow (java.math.BigInteger/valueOf a) (java.math.BigInteger/valueOf n)))
([^java.math.BigInteger a ^java.math.BigInteger n]
(cond (zero? n) java.math.BigInteger/ONE
(even? n) (letfn [(square [x] (.multiply x x))]
(square (fast-pow a (.divide n (java.math.BigInteger/valueOf 2)))))
:else (.multiply a (fast-pow a (.subtract n BigInteger/ONE))))))
Конечно, он даже не скомпилирован из-за неправильной проблемы с arity. Так что я google как отправить по типу в clojure и нашел multimethods
. На самом деле в этот момент я был меняться наивный и возбужденный, я никогда раньше не пробовал multimethods
, поэтому, я написал что-то так:
(derive java.math.BigInteger ::biginteger)
(derive java.lang.Long ::long)
(defmulti fast-pow (fn [a n] [(class a) (class n)]))
(defmethod fast-pow [::biginteger ::biginteger] [a n]
(cond (zero? n) java.math.BigInteger/ONE
(even? n) (letfn [(square [x] (.multiply x x))]
(square (fast-pow a (.divide n (java.math.BigInteger/valueOf 2)))))
:else (.multiply a (fast-pow a (.subtract n BigInteger/ONE)))))
(defmethod fast-pow [::long ::long] [a n]
(fast-pow
(java.math.BigInteger/valueOf a)
(java.math.BigInteger/valueOf n)))
Дела идут немного сложнее. Это прекрасно работает, но мне интересно, есть ли более чистый путь к такой вещи, как просто перегрузка по типу. Я не понял, почему в clojure я не могу этого сделать, даже если в Java я могу.
PS: Конечно, я могу «спрятать» логику на основе java.math.BigInteger
внутри вложенной функции и вызвать ее из внешней функции fast-pow
, но на самом деле - это не интересно для меня, я уверен, эта проблема может быть решена используя multimethods
. Если я ошибаюсь или что-то не хватает, пожалуйста, объясните мне это.;)
Только некоторые подсказки стиля: извлеките функцию 'square' наружу, бесполезно определять ее локально через' letfn' на каждом этапе рекурсии. Кроме того, взгляните на 'recur' (и, вероятно,' loop') и переместите рекурсивный вызов в позицию хвостового вызова - то, как вы делаете рекурсию, скорее всего, выведет стек довольно скоро. – schaueho