Clojure - argumentos con nombre

78

¿Clojure tiene argumentos con nombre? Si es así, ¿podría darnos un pequeño ejemplo?

uno-cero-cero-uno
fuente

Respuestas:

118

En Clojure 1.2, puede desestructurar el restargumento como lo haría con un mapa. Esto significa que puede hacer argumentos de palabras clave no posicionales con nombre. Aquí hay un ejemplo:

user> (defn blah [& {:keys [key1 key2 key3]}] (str key1 key2 key3))
#'user/blah
user> (blah :key1 "Hai" :key2 " there" :key3 10)
"Hai there10"
user> (blah :key1 "Hai" :key2 " there")
"Hai there"
user> (defn blah [& {:keys [key1 key2 key3] :as everything}] everything)
#'user/blah
user> (blah :key1 "Hai" :key2 " there")
{:key2 " there", :key1 "Hai"}

Cualquier cosa que pueda hacer mientras desestructura un mapa de Clojure se puede hacer en la lista de argumentos de una función como se muestra arriba. Incluyendo el uso de: o para definir valores predeterminados para los argumentos como este:

user> (defn blah [& {:keys [key1 key2 key3] :or {key3 10}}] (str key1 key2 key3))
#'user/blah
user> (blah :key1 "Hai" :key2 " there")
"Hai there10"

Pero esto está en Clojure 1.2. Alternativamente, en versiones anteriores, puede hacer esto para simular lo mismo:

user> (defn blah [& rest] (let [{:keys [key1 key2 key3] :or {key3 10}} (apply hash-map rest)] (str key1 key2 key3)))
#'user/blah
user> (blah :key1 "Hai" :key2 " there")
"Hai there10"

y eso funciona generalmente de la misma manera.

Y también puede tener argumentos posicionales que vienen antes de los argumentos de palabras clave:

user> (defn blah [x y & {:keys [key1 key2 key3] :or {key3 10}}] (str x y key1 key2 key3))
#'user/blah
user> (blah "x" "Y" :key1 "Hai" :key2 " there")
"xYHai there10"

Estos no son opcionales y deben proporcionarse.

De hecho, puede desestructurar el restargumento como lo haría con cualquier colección de Clojure.

user> (defn blah [& [one two & more]] (str one two "and the rest: " more))
#'user/blah
user> (blah 1 2 "ressssssst")
"12and the rest: (\"ressssssst\")"

Puedes hacer este tipo de cosas incluso en Clojure 1.1. Sin embargo, la desestructuración de estilo de mapa para argumentos de palabras clave solo llegó en 1.2.

Rayne
fuente
34

Además de la excelente respuesta de Raynes, también hay una macro en clojure-contrib que hace la vida más fácil:

usuario => (use '[clojure.contrib.def: solo [defnk]])
nulo
usuario => (defnk foo [ab: c 8: d 9] 
         [a B C D])
# 'usuario / foo
usuario => (foo 1 2)
[1 2 8 9]
usuario => (foo 1 2 3)
java.lang.IllegalArgumentException: No se ha proporcionado ningún valor para la clave: 3 (NO_SOURCE_FILE: 0)
usuario => (foo 1 2: c 3)
[1 2 3 9]
Alex Taggart
fuente
6
¡Me olvidé de mencionar eso! Estaba atrapado en mostrar las 10 mil formas en que Clojure puede desestructurar cosas. : p
Rayne
1
clojure-contrib está en desuso y no pude encontrar una alternativa actual. ¿Algunas ideas?
Lstor
2
@Lstor: echa un vistazo a defnk en prismático / plomería
Ian
3

A partir de la versión 1.8 de Clojure, la compatibilidad con palabras clave todavía parece un poco normal .

Puede especificar argumentos de palabras clave como este:

(defn myfn1
  "Specifying keyword arguments without default values"
  [& {:keys [arg1 arg2]}]
  (list arg1 arg2))

Ejemplos de llamarlo:

(myfn1 :arg1 23 :arg2 45)  --> evaluates to (23 45)
(myfn1 :arg1 22)           --> evaluates to (22 nil)

Si desea especificar valores predeterminados para estos argumentos de palabras clave:

(defn myfn2
  "Another version, this time with default values specified"
  [& {:keys [arg1 arg2] :or {arg1 45 arg2 55}}]
  (list arg1 arg2))

Esto hace lo esperado en el segundo caso:

(myfn2 :arg1 22)           --> evaluates to (22 55)

Hay pros y contras en cada parte de cada idioma, pero solo para comparar, así es como harías lo mismo en Common Lisp:

(defun myfn3
    (&key arg1 arg2)
    "Look Ma, keyword args!"
    (list arg1 arg2))

(defun myfn4
    (&key (arg1 45) (arg2 55))
    "Once again, with default values"
    (list arg1 arg2))
agam
fuente
0

¿Quizás te refieres a parámetros con nombre ? Estos no están disponibles directamente, pero puede usar este enfoque de vectores si lo desea, que puede darle lo que desea.

En RosettaCode hay una explicación más profunda sobre cómo hacer esto usando la desestructuración.

Abel
fuente
3
@Abel ¿Puedes compartir los ejemplos a los que enlazas? (Tienen una forma de cambiar o desactualizarse).
David J.