Java Clojure用于按类型分派的多个方法

Java Clojure用于按类型分派的多个方法,java,clojure,clojure-java-interop,multimethod,Java,Clojure,Clojure Java Interop,Multimethod,我在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互操作。我之所以想这样做,是因为在clojure中更好地理解了所有这些“java

我在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互操作。我之所以想这样做,是因为在clojure中更好地理解了所有这些“java东西”。看起来他们很容易,但事实上有很多隐藏的障碍。因此,我写道:

(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))))))
当然,由于算术错误,它甚至没有被编译。因此,我在clojure中搜索了如何按类型发送,并找到了
多方法
。事实上,在这一点上,我很天真和兴奋,我以前从未尝试过多种方法,所以,我写了一些类似的东西:

(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.biginger
的逻辑,并从外部函数
fast-pow
调用它,但事实上-这对我来说并不有趣,我很确定,这个问题可以通过使用
多方法来解决。如果我错了或遗漏了什么,请向我解释……)

如果要基于多个参数的类型进行分派,则multimemethods是合适的工具。请记住,这已经内置到所有数学函数中,例如+'和*'

user> (+' 2N 3)
5N

在clojure中,不能使用类型提示来完成这种类型的重载/分派(这是故意的)。如果只基于第一个参数进行调度,则应该使用,因为它们要快得多

只是一些风格提示:将
square
函数提取到外部,在每个递归步骤上通过
letfn
本地定义它是没有用的。另外,看看
recur
(可能还有
loop
)并将递归调用移动到尾部调用位置——您执行递归的方式可能很快就会破坏堆栈。好的,谢谢,但是为什么它这么复杂呢?我的意思是,JVM中不是已经实现了按类型分派吗?这是因为clojure中的evey函数看起来像是JVM的类吗?虽然语言是这样设计的,但我真的无法解释为什么会这样。Multimethods提供了一种完全通用的分派方法,它涵盖了这种分派以及在调用时可以表示的所有其他分派类型。协议在大多数情况下通过第一次辩论提供调度。所以我只能假设,按第二个参数的类型进行的调度并没有被视为重要到足以保证第三个调度类型(以及后来添加的协议,最初Clojure只有多个方法)。如果需要,可以使用宏构建此功能