2011-12-29 8 views
15
user=> (Integer/rotateRight 0 0) 
0 

user=> (apply Integer/rotateRight [0 0]) 
CompilerException java.lang.RuntimeException: Unable to find static field: 
    rotateRight in class java.lang.Integer, compiling:(NO_SOURCE_PATH:172) 

¿Hay alguna manera de aplicar para funciones Java en Clojure? Si no, ¿cómo podría escribir una macro o función que respaldaría esto?¿Hay alguna función de aplicación en las funciones de Clojure para Java?

+0

Interesante: planea incorporar la generación de envoltura de método automático con un futuro compilador de Clojure reescribir http://dev.clojure.org/display/design/Better+method+inference – Ambrose

Respuesta

17

La cosa más simple que puedo pensar es envolverlo en una función, pero no estoy del todo seguro de si este es el mejor/manera más idiomática:

user> (apply (fn [a b] (Integer/rotateRight a b)) [0 0]) 
0 

O, un poco más corto, pero equivalentes:

user> (apply #(Integer/rotateRight %1 %2) [0 0]) 
0 

Como alternativa, puede crear una función de contenedor correcto para su llamada al método java:

(defn rotate-right [a b] 
    (Integer/rotateRight a b)) 

debería usar este modo:

user> (apply rotate-right [0 0]) 
0 

edición: sólo por diversión, inspirado por el comentario de iradik sobre efficieny, comparaciones en el tiempo entre tres maneras diferentes de llamar a este método:

;; direct method call (x 1 million) 
user> (time (dorun (repeatedly 1E6 #(Integer/rotateRight 2 3)))) 
"Elapsed time: 441.326 msecs" 
nil 

;; method call inside function (x 1 million) 
user> (time (dorun (repeatedly 1E6 #((fn [a b] (Integer/rotateRight a b)) 2 3)))) 
"Elapsed time: 451.749 msecs" 
nil 

;; method call in function using apply (x 1 million) 
user> (time (dorun (repeatedly 1E6 #(apply (fn [a b] (Integer/rotateRight a b)) [2 3])))) 
"Elapsed time: 609.556 msecs" 
nil 
+1

eso es bastante inteligente, me pregunto si podría escribir un macro que escribió eso para mí. –

+1

probablemente podría, pero personalmente creo que los beneficios de eso son escasas: la forma resultante difícilmente sería más corta, y el código anterior deja en claro lo que está sucediendo. Alternativamente, puede definir la función y llamar a eso en lugar del método java. Editaré mi respuesta para ilustrar. – Gert

+0

No estoy de acuerdo. ¿Qué pasaría si pudieras hacer (j-apply 'Integer/rotateRight [0 0])? ¿No sería eso más conciso? –

0

I escribió algunas macros para hacer esto después de haber sido inspirado por la respuesta de gertalot. Parece compilar al código normal equivalente. El punto de referencia fue idéntico. Curioso lo que piensas

(defmacro java-new-apply 
    ([klass] `(new ~klass)) 
    ([klass args] `(new ~klass [email protected](map eval args)))) 

(defmacro java-static-apply 
    ([f] f) 
    ([f args] `(~f [email protected](map eval args)))) 

(defmacro java-method-apply 
    ([method obj] method obj) 
    ([method obj args] `(~method ~obj [email protected](map eval args)))) 

;; get date for Jan 1 1969 
(import java.util.Date) 
(java-new-apply Date [69 1 1]) 
(macroexpand '(java-new-apply Date [69 1 1])) 
(new Date 69 1 1) 

(java-static-apply Integer/rotateRight [2 3]) 
(macroexpand '(java-static-apply Integer/rotateRight [2 3])) 
(. Integer rotateRight 2 3) 

(java-method-apply .substring "hello world!" [6 11]) 
(macroexpand '(java-method-apply .substring "hello world!" [6 11])) 
(. "hello world!" substring 6 11) 
+3

Esto funciona solo si pasa literales a sus macros. Intente esto:' (def my-date-data [69 1 1]) (macroexpand '(java-new-apply Fecha my-date-data) 'para ver que no funciona. Las macros se expanden en tiempo de compilación, no tiempo de ejecución, lo que significa que no tiene acceso a los valores reales que se pasan. –

+0

I [intenté con la misma cosa] (http://stackoverflow.com/a/4514494/506721) y eventualmente descubrí que simplemente no vale la pena en comparación con la solución idiomática con funciones de envoltura. –

+0

De acuerdo con Goran: no importa cómo lo segmente, siempre terminará con una llamada a método incluida dentro de una llamada a función. – Gert

4

Un par de puntos que, si bien no son una respuesta directa, son relevantes aquí.

En primer lugar, Java no tiene funciones. Solo tiene métodos de instancia o métodos estáticos. Esto puede parecer una distinción pedante, pero hace una diferencia (como se muestra en algunos de los otros ejemplos donde se necesitan formularios diferentes para la invocación estática y de instancia).

En segundo lugar, entra en juego la falta de correspondencia de la impedancia entre los sistemas tipo. Para que Java tenga un soporte FP totalmente desarrollado en Java, tendría que estar tipado estáticamente. Esto resulta bastante difícil de hacer de una manera realmente satisfactoria (consulte la discusión en la lista de correo de lambda-dev para conocer detalles del enfoque que se está utilizando y que llegará a Java 8).

De estos dos puntos, podemos ver que desde Clojure lo mejor que podemos hacer es apoyar un enfoque de "todas las apuestas están fuera de servicio" para llamar a los métodos Java a través de #() o similar. Clojure solo elegirá entre formas para llamar en función de la ariadidad del argumento, por lo que es posible que se necesite algún tipo de sugerencia de tipo o conversión para garantizar que se invoque el método Java correctamente sobrecargado.

Más importante, por supuesto, si un usuario pasa un argumento de un tipo que Java no espera, o no puede manejar, esto puede no ser detectable hasta el tiempo de ejecución.

+0

derecha. Me gustaría saber cómo llamar a un método de instancia en una secuencia de instancias. – Timo

Cuestiones relacionadas