我在clojure中实现了快速功率算法:
(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互操作。我想这样做是为了更好地理解所有这些" 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问题,它甚至没有编译。所以我谷歌如何按类型发送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
。如果我错了或遗失了某些东西 - 请向我解释..;)
答案 0 :(得分:3)
如果要根据多个参数的类型进行调度,那么multimethods是合适的工具。请记住,这已经包含在所有促进数学功能中,例如+'和*'。
user> (+' 2N 3)
5N
你不能在clojure中使用类型提示来完成这种类型的重载/调度(这是故意这样做的)。如果您只根据第一个参数进行调度,则应使用protocols,因为它们很多更快。