2010-04-29 11 views
11

parece que hay un límite en el número de parámetros que puede tomar una función clojure.¿Por qué hay un límite de 20 parámetros máx. Para una función de clojure

Al definir una función con más de 20 parámetros que recibe lo siguiente:

#<CompilerException java.lang.RuntimeException: java.lang.RuntimeException: java.lang.Exception: Can't specify more than 20 params (NO_SOURCE_FILE:0) (NO_SOURCE_FILE:0)>

Obviamente, esto se puede evitar, pero yo estaba golpeando este límite portar el modelo de ejecución de un DSL existente para clojure, y tengo construcciones en mi DSL como el siguiente, que por la expansión de macros se pueden asignar a funciones con bastante facilidad a excepción de este límite:

(defAlias nn1 ((element ?e1) (element ?e2)) number 
"@doc features of the elements are calculated for entry into 
     the first neural network, the result is the score computed by the latter" 
(nn1-recall (nn1-feature00 ?e1 ?e2) (nn1-feature01 ?e1 ?e2) ... (nn1-feature89 ?e1 ?e2))) 

que es una declaración DSL para llamar a una red neuronal con 90 nodos de entrada. Puede solucionarlo, por supuesto, pero me preguntaba de dónde viene el límite. Gracias.

+0

Al igual que una nota de interés sobre el estilo: "Code Complete" de Steve McConnell aboga, en parte basado en estudios de eficiencia de programación, que un método nunca debe tener más de 7 argumentos y, más recientemente, el "Código limpio" de Robert C. Martin establece el límite tan bajo como 3. He encontrado 7 una buena regla para decidir cuándo rediseñar una interfaz, mientras que 3 parece un poco extrema. Sí, soy consciente de tus circunstancias especiales con el macro y todo; solo señalando que para el caso "general", 20 es bastante generoso. –

+0

Construye tus neuronas con matrices o algo así y distribúyelas a la función como seqs. Y mapa: ¿reducir los seqs? ¿Algo como esto? http://www.fatvat.co.uk/2009/06/back-propagation-algorithm-in-clojure.html – claj

Respuesta

17

En primer lugar, el límite solo se aplica a argumentos posicionales necesarios; siempre se puede utilizar el caso aridad variable (& more-args en la firma de la función) para manejar tantos argumentos como se desea:

(defn foo [& args] 
    (count args)) 

(foo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25) 
;; returns 25 

De hecho, a primera vista, & args es probable que sea la solución adecuada a su problema. (Por ejemplo, podrá mapear una función sobre sus nodos de entrada recogidos en una secuencia, loop/recur sobre dicha secuencia, etc. - tiende a tener más sentido con una gran cantidad de elementos similares que asignar nombres separados a cada uno de ellos.

(Tenga en cuenta que no pretendo conocer la naturaleza de la DSL específica que está transcribiendo en Clojure o el tipo de problemas que está tratando, simplemente sugiriendo puntos que pueden ser de su interés. Si tiene una situación realmente funky en la que parece que esto no se aplica, tal vez pueda proporcionar más detalles y veremos si alguien aquí puede ofrecer algunos consejos útiles para tratarlo en Clojure.)

Para para completar, puede agregar el & args bits a una función que toma sus primeros 19 argumentos como argumentos posicionales requeridos:

(defn bar [a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 a16 a17 a18 a19 & as] 
    (+ 19 (count as))) 

(bar 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25) 
;; returns 25 

Tenga en cuenta que si especifica 20 argumentos posicionales y el & arg encima de eso, al parecer, se producirán rareza.

En cuanto a la razón de ser, creo que esto tiene que ver con el hecho de que la JVM puede enviar a un método por el aridad de manera muy eficiente, por lo que la clase clojure.lang.Fn tiene el método invoke sobrecargado para arities hasta 20. No estoy Estoy completamente seguro de si podría ir más allá de eso, pero supongo que esto no es algo que la gente requiera a menudo ... Es decir, ciertamente encuentro cualquier API que especifique más de 20 argumentos de posición para una función un poco sospechosa.

+0

Gracias, y args de hecho lo aborda perfectamente. – GuyC

+2

Solo una nota re: tener 20 args posicionales y args de reposo al mismo tiempo: hubo un error que provocó que se devolviera 'nil' sin que se evaluara el cuerpo de la función para este arity, que Rich ha corregido hace unos minutos. Por lo tanto, 1.2 no tendrá este problema. –

11

La respuesta de Michal Marczyk te dice muy bien cómo superar el límite. Si está interesado en el motivo de esta limitación, le recomendamos que eche un vistazo a esta fuente de Clojure: IFn

invoque que es un método Java sobrecargado implementado por la interfaz Ifn, Rich lo sobrecargó para admitir hasta 20 argumentos. Cuando llama a una función en clojure, las llamadas de implementación subyacentes invocan al objeto de función, un método que solo admite hasta 20 argumentos.

Puede sobrecargarlo para soportar más, pero dudo de la utilidad de tal cosa. Si tiene varias fuentes de entrada, probablemente puedan tratarse como una matriz de todos modos.

+0

La respuesta de Michal me lleva al límite, pero de hecho también es interesante echar un vistazo a IFn. Gracias. – GuyC

Cuestiones relacionadas