按类型分派的 Clojure 多种方法
Clojure mutimethods for dispatch by type
我在 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 stuff"。看似很容易,其实隐藏着很多障碍。所以,我写道:
(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))))))
当然连编译都没有,因为偶数错误的问题。所以我 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:当然我可以在嵌套函数中"hide"基于java.math.BigInteger
的逻辑,并从外部函数fast-pow
调用它,但实际上-它是对我来说不感兴趣,我很确定,这个问题可以通过使用 multimethods
来解决。如果我错了或遗漏了什么 - 请向我解释.. ;)
如果您想根据多个参数的类型进行分派,那么多方法是合适的工具。请记住,这已经内置于所有促销数学函数中,例如 +' 和 *'。
user> (+' 2N 3)
5N
在 clojure 中,您不能使用类型提示来完成这种类型的 overloading/dispatch(这是故意的)。如果你只是根据第一个参数进行调度,你应该使用 protocols 因为它们 快得多 。
我在 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 stuff"。看似很容易,其实隐藏着很多障碍。所以,我写道:
(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))))))
当然连编译都没有,因为偶数错误的问题。所以我 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:当然我可以在嵌套函数中"hide"基于java.math.BigInteger
的逻辑,并从外部函数fast-pow
调用它,但实际上-它是对我来说不感兴趣,我很确定,这个问题可以通过使用 multimethods
来解决。如果我错了或遗漏了什么 - 请向我解释.. ;)
如果您想根据多个参数的类型进行分派,那么多方法是合适的工具。请记住,这已经内置于所有促销数学函数中,例如 +' 和 *'。
user> (+' 2N 3)
5N
在 clojure 中,您不能使用类型提示来完成这种类型的 overloading/dispatch(这是故意的)。如果你只是根据第一个参数进行调度,你应该使用 protocols 因为它们 快得多 。