2011-03-16 19 views
36

Clojure es increíble, todos lo sabemos, pero ese no es el punto. Me pregunto cuál es la forma idiomática de crear y administrar funciones de orden superior de una manera similar a Haskell. En Clojure que pueda hacer lo siguiente:Funciones de orden superior en Clojure

(defn sum [a b] (+ a b)) 

Pero (sum 1) no devuelve una función: se produce un error. Por supuesto, se puede hacer algo como esto:

(defn sum 
    ([a] (partial + a)) 
    ([a b] (+ a b))) 

En este caso:

user=> (sum 1) 
#<core$partial$fn__3678 [email protected]> 
user=> ((sum 1) 2) 
3 

Pero no parece que la forma correcta de proceder. ¿Algunas ideas? No estoy hablando de implementar la función sum, estoy hablando a un nivel más alto de abstracción ¿Hay algún patrón idiomático a seguir? Algunos macro? ¿Es la mejor manera de definir una macro o hay soluciones alternativas?

Respuesta

32

Alguien tiene already implememented this en el grupo Clojure. Puede especificar cuántos argumentos tiene una función, y se curry para usted hasta que obtenga tantos.

La razón por la que esto no ocurre por defecto en Clojure es que preferimos las funciones variadas a las funciones auto-curried, supongo.

+1

Gracias por la respuesta rápida. Ya he consultado esa publicación, y me pregunto si las soluciones propuestas se adoptan en algunos subpaquetes clojure.contrib o son solo un brunch de ideas posibles :) –

+0

¿Puedo inferir de su respuesta que cree que las funciones varían? y las funciones auto-curried no son compatibles entre sí? Si es así, me gustaría saber más sobre el motivo. Gracias. – day

+0

Dado '(defn f ([x] 1) ([x y] 2))', ¿qué devuelve '(f true)'? Debe ser 1, lo que significa que no se puede curvar automáticamente como una aplicación parcial de la versión de dos aries. – amalloy

8

He jugado un poco con las funciones sugeridas por amalloy. No me gusta la especificación explícita del número de argumentos para curry. Así que he creado mi macro personalizada. Esta es la vieja manera de concreto de una alta función de orden:

(defn-decorated old-sum 
    [(curry* 3)] 
    [a b c] 
    (+ a b c)) 

Ésta es mi nueva macro:

(defmacro defn-ho 
    [fn-name & defn-stuff] 
    (let [number-of-args (count (first defn-stuff))] 
    `(defn-decorated ~fn-name [(curry* ~number-of-args)] [email protected]))) 

Y esta es la nueva forma implícita:

(defn-ho new-sum [a b c] (+ a b c)) 

Como se puede ver que no hay rastros de (curry) y otras cosas, solo defina su función currificada como antes.

Chicos, ¿qué opinas? Ideas? Sugerencias? ¡Adiós!

Alfedo

Edición: He modificado la macro según el tema sobre amalloy cadena de documentación. Esta es la versión actualizada:

(defmacro defhigh 
    "Like the original defn-decorated, but the number of argument to curry on 
    is implicit." 
    [fn-name & defn-stuff] 
    (let [[fst snd] (take 2 defn-stuff) 
     num-of-args (if (string? fst) (count snd) (count fst))] 
    `(defn-decorated ~fn-name [(curry* ~num-of-args)] [email protected]))) 

No me gusta la instrucción if dentro del segundo enlace. ¿Alguna idea de hacerlo más succint?

+3

Me gusta. Aunque sugeriría un nombre diferente para tu macro. Quizás "defcurry" o quizás "defcurried". –

+0

Acepto sugerencia para este tema también ... defn-ho es horrible, lo sé: D –

+0

'(defn-ho myfn" hace cosas increíbles "[a b c] ...)'. Ahora tu recuento de args no funcionará tan bien. Ciertamente se puede hacer, pero manejar formularios que acepten definitivamente no es trivial, y sospecho que es la razón por la que no se implementó de esa manera para empezar. – amalloy

0

Esto le permitirá hacer lo que quiere:

(defn curry 
    ([f len] (curry f len [])) 
    ([f len applied] 
    (fn [& more] 
     (let [args (concat applied (if (= 0 (count more)) [nil] more))] 
     (if (< (count args) len) 
      (curry f len args) 
      (apply f args)))))) 

Así es como se usa:

(def add (curry + 2)) ; read: curry plus to 2 positions 
((add 10) 1) ; => 11 

El condicional con la [nil] tiene por objeto garantizar que todas las aplicaciones se asegura un cierto progreso hacia adelante al estado de curry. Hay una larga explicación detrás de esto, pero lo he encontrado útil.Si no te gusta este bit, podría configurar args como:

[args (concat applied more)] 

A diferencia de JavaScript no tenemos manera de saber la aridad de la función pasada y lo que debe especificar la longitud esperada. Esto tiene mucho sentido en Clojure [Script] donde una función puede tener aries múltiples.

Cuestiones relacionadas