2010-03-23 29 views
15

Mi piacerebbe essere in grado di definire lambda usando la sintassi comune del Lisp, in Clojure. Per esempio:Come implementare lambda come una funzione chiamata "lambda" in Clojure?

(lambda (myarg) 
    (some-functions-that-refer-to myarg)) 

Questo deve comportare lo stesso:

#(some-functions-that-refer-to %) 

Nel mio caso, io so avrò sempre esattamente un arg, così forse che semplifica le cose. (Ma può essere chiamato qualsiasi cosa - "myarg" o qualsiasi altra cosa.)

Sospetto che una soluzione praticabile sia "(defmacro lambda ...". In tal caso, non sono sicuro del modo migliore di procedere . Come tradurre in modo pulito il nome arg al%? E come finire con il corretto funzionamento?

Oppure, c'è una soluzione più semplice che scrivere il mio macro che in realtà ri-implementa di Clojure ... lambda?

risposta

25

#(foo %) è solo una scorciatoia per il lettore per (fn [arg] (foo arg)). Non c'è motivo di scrivere una macro che si espanda in #(...). Tutti i in un costrutto #(...) vengono espansi in ge nsyms subito comunque.

user> `#(foo % %1 %2) 
(fn* [user/p1__1877 user/p2__1878] 
    (user/foo user/p1__1877 user/p1__1877 user/p2__1878)) 

Se siete mai a scrivere una macro che si espande per costruire funzioni anonime, si potrebbe anche semplicemente ampliare loro di fn forma da soli. Nel tuo caso, probabilmente dovresti semplicemente usare fn direttamente e saltare i macro. fn è lambda di Clojure.

La differenza tra (fn [] ...) e (lambda() ...) in questo caso è che "fn" è più corto da digitare rispetto "lambda", e fn prende un vettore per i binding che lambda prende una lista. Se si sta utilizzando Clojure, ci si deve abituare alla fine, perché i vettori vengono sempre utilizzati per le raccolte di associazioni, in tutti i moduli do e in for e binding ecc. La logica alla base di questo, come ho capito, è che gli elenchi vengono utilizzati per chiamate di funzione o macro e i vettori vengono utilizzati per cose che non sono chiamate (elenchi di simboli da associare, ad esempio). Probabilmente, rende più facile la scansione del codice visivamente rispetto agli elenchi tutto-giù. Clojure non è Common Lisp e proverai dolore se cerchi di forzarlo.

Se davvero, davvero voluto fare questo, solo per dire che avete fatto:

user> (defmacro lambda [args & body] 
     `(fn ~(vec args) [email protected])) 
user> ((lambda (x) (println x)) "foo") 
foo 
nil 

Questo non consente di mettere un docstring o metadati sulla vostra funzione, tra le altre cose. Non penso che vorrai usarlo in un vero programma Clojure.

+2

Grazie Brian, questo è esattamente ciò di cui avevo bisogno. Sono imbarazzato nel dire che ho avuto un punto cieco qui ... Sono così innamorato dello zucchero #() che ho dimenticato che c'è il fn formale. Ora, in realtà, voglio davvero usare l'approccio macro, perché sono in una situazione in cui sono costretto a eseguire un semplice codice Lisp che utilizza "lambda". E la macro che hai scritto è esattamente ciò di cui avevo bisogno. Grazie mille! – dirtyvagabond

+0

Sembra divertente, buona fortuna. :) –

Problemi correlati