2010-11-26 20 views
11

Quiero crear una función que adopte un argumento obligatorio x, y un argumento opcional opt1 O un argumento de palabra clave opt2.Palabra clave de Clojure y argumento opcional Problema

Ahora mismo tengo

(defn foo x & [opt1 {:keys [opt2]}] 
    ... 

Pero la firma anterior sólo me permite pasar en opt2 argumento de palabra clave cuando tanto x como opt1 está presente como

(foo 'x 'opt1 {:opt2 'opt2}) 
no

gusta esta

(foo 'x {:opt2 'opt2}) 

Ayúdenme a crear una función que adopte un argumento obligatorio X y opt1 u opt2, cuando re opt2 es un argumento de palabra clave.

Gracias.

EDITAR: Quiero hacer lo mismo para otras macros también. Así que todavía necesito usar el defmacro.

+0

considerar el uso de 'defnk' de [clojure.contrib.def] (http://richhickey.github.com/clojure-contrib/def-api.html) en lugar de desestructuración explícita. – ffriend

+0

'defnk' está en desuso en favor de una funcionalidad incorporada más consistente a partir de 1.2. – kotarak

Respuesta

15

El problema es la ambigüedad. Considere una función (fn foo [x y & args]) que toma dos argumentos opcionales y luego cualquier número de argumentos de palabra clave. Si luego lo llama como (foo :bar :baz), ¿cómo lo maneja su programa? x =>:bar, y =>:baz? O x y y no proporcionados, con un solo argumento de palabra clave :bar =>:baz?

Incluso en Common Lisp, que tiene sin duda aún más flexibilidad que Clojure en analizar los parámetros de función, mezclando argumentos opcionales y palabras clave no se recomienda, de acuerdo con al menos one popular book.

Su mejor opción es cambiar todos sus argumentos a los argumentos posicionales, o todos los parámetros de palabras clave argumentos. Si usa argumentos de palabra clave, puede usar la desestructuración de hash-map para proporcionar valores predeterminados para los parámetros de palabras clave "opcionales".

user> (defn foo [& {:keys [x y bar] 
        :or {x 1 y 2 bar 3}}] 
     (prn [x y bar])) 
#'user/foo 
user> (foo) 
[1 2 3] 
nil 
user> (foo :bar :baz) 
[1 2 :baz] 
nil 
2

usted tiene que comprobar si los argumentos Adicionales son argumentos de palabra clave o no de todos modos (que supongo que usted o es un exclusivo o) para que pueda hacerlo de esta manera:

(defn foo [& args] 
    (if (= (count args) 1) 
     (let [[opt1] args] (println opt1)) 
     (let [{:keys [opt2]} args] (println opt2)))) 

verificación de los argumentos si son palabras clave argumentos o no. Como solo tiene un parámetro opcional, es fácil: verifique si solo hay uno, ya que los argumentos de palabras clave requieren dos.

Cuestiones relacionadas