2012-02-18 12 views
8

En clojure, es posible desestructurar algunas claves de un mapa como este:En Clojure, ¿cómo desestructurar todas las teclas de un mapa?

(let [{:keys [cpp js]} {:cpp 88 :js 90}] 
    (println js); 90 
    (println cpp); 88 
) 

¿Hay una manera de desestructurar todos las teclas de un mapa?

Tal vez algo como:

(let [{:all-the-keys} {:cpp 88 :js 90}] 
    (println js); 90 
    (println cpp); 88 
) 

Respuesta

22

En realidad no, y no sería una buena idea. Imagínese:

(let [{:all-the-keys} m] 
    (foo bar)) 

¿Están foo y bar globales? ¿Locales? Llaves que debes extraer de m? ¿Qué debería hacer este código si m a veces contiene una clave foo, y foo también es una función global? A veces llama a lo global, y a veces llama a la función almacenada en m?

Ignorando los problemas técnicos (la mayoría de los cuales probablemente puedan superarse), es realmente un desastre para la legibilidad y la previsibilidad. Simplemente sea explícito sobre qué claves desea extraer; si con frecuencia desea extraer las mismas diez teclas, puede escribir una macro simple como (with-person p body) que simplifica ese caso común para usted.

6

Se puede escribir una macro para hacer esto (creando así un mini-DSL), pero no creo que es una muy buena idea por las siguientes razones:

  • el fin de crear la derecha literales en tiempo de compilación js y cpp, necesitaría desestructurar el mapa en tiempo de compilación. Esto sería bastante limitante en términos de lo que podría hacer con él (tendría que especificar las claves por adelantado, y no podría usarse en funciones de orden superior, por ejemplo)
  • Las macros son generalmente una mala idea cuando método más sencillo sería hacer el trabajo (véase más adelante)

lo recomiendo simplemente usando un simple doseq en su caso para un bucle sobre el mapa:

(let [my-map {:cpp 88 :js 90}] 
    (doseq [[k v] my-map] 
    (println v))) 

Tenga en cuenta que:

  • Puede utilizar desestructuración que el anterior para extraer tanto la clave k y el valor v de cada entrada del mapa
  • Solía ​​doseq en lugar de for debido a que no es perezoso y parece que en este ejemplo que está utilizando el bucle sólo para el println efectos secundarios.
  • Si en su lugar desea una secuencia de valores vagos (88 90), entonces sería apropiado for.
7

Esta pregunta es bastante antigua, por lo que probablemente se haya olvidado, pero apareció en Google cuando intentaba hacer lo mismo, por lo que si publico mi solución podría ayudar a alguien más.

(defmacro let-map [vars & forms] 
    `(eval (list 'let (->> ~vars keys 
         (map (fn [sym#] [(-> sym# name symbol) (~vars sym#)])) 
         (apply concat) vec) 
       '~(conj forms 'do)))) 

Esto básicamente transforma el mapa {:cpp 88 :js 90} en la forma de unión [cpp 88 js 90] entonces construye un let vinculante, junto con la realización de algún eval-jitsu para asegurarse de que esto sucede en tiempo de ejecución.

(def test-map {:cpp 88 :js 90}) 
(let-map test-map 
    (println js) 
    (println cpp)) 
;=> 90 
;=> 88 
+1

Oh, cmon, esta es la única respuesta que realmente responde una pregunta en el título. Gracias. – desudesudesu

+1

Esta es una macro extremadamente inteligente. Tomemos un momento para reconocer su ingenio y luego prometernos nunca escribir algo así en el código de producción. – MichaelBlume

+0

Puede que no parezca la mejor práctica, pero incluso diría que hay casos de uso válidos para esta construcción. –

Cuestiones relacionadas