2012-06-21 16 views
6

Para comenzar, estoy en Windows 7 (64 bits), ejecutando Java versión 6 (actualización 33) usando clooj como mi IDE. No intenté reproducir mi problema en ningún otro sistema. Tengo experiencia con Clojure, pero no con Java.Clojure macro que conservará la orden de asignación asociativa

Todo el problema que trato de resolver es largo de describir, pero se reduce a esto: digamos que me gustaría hacer una macro que tome un argumento, un mapa asociativo, y devuelva un vector de los elementos del mapa con su orden conservada.

=>(defmacro vectorize-a-map 
    [associative-map] 
    (vec associative-map)) 
=>#'ns/vectorize-a-map 
=>(vectorize-a-map {:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8} 
=>[[:a 1] [:b 2] [:c 3] [:d 4] [:e 5] [:f 6] [:g 7] [:h 8]] 

que funciona, pero añade otro elemento al mapa y el orden meta la pata ...

=>(vectorize-a-map {:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8 :i 9} 
=>[[:a 1] [:c 3] [:b 2] [:f 6] [:g 7] [:d 4] [:e 5] [:i 9] [:h 8]] 

Creo que he descubierto por qué ocurre esto. Parece que cualquier instancia con 8 o menos elementos se instancia como una PersistentArrayMap, que es exactamente lo que quiero, porque por lo que puedo decir, esta clase conserva el orden. Sin embargo, cualquier elemento con 9 o más elementos se instancia como un PersistentHashMap, que no conserva el orden.

=>(type {:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8} 
=>clojure.lang.PersistentArrayMap 
=>(type {:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8 :i 9} 
=>clojure.lang.PersistentHashMap 

Me gustaría que mi macro sea capaz de tomar mapas asociativos de cualquier tamaño, por lo que este es un problema. He intentado con el tipo de insinuación, enlace destructivo, para la comprensión de la lista, y el empalme de unificación, todo sin éxito. Para extraerlo, ninguno de los siguiente trabajo:

(defmacro vectorize-a-map 
    [^clojure.lang.PersistentArrayMap associative-map] 
    (vec associative-map)) 

(defmacro vectorize-a-map 
    [[& associative-map]] 
    (vec associative-map)) 

(defmacro vectorize-a-map 
    [associative-map] 
    (vec 
    (for [x associative-map] 
     x))) 

(defmacro vectorize-a-map 
    [associative-map] 
    `(vector [email protected])) 

Con este problema juguete presento, me di cuenta que podía simplemente escribir mi macro como tal, y evitar el problema por completo:

=>(defmacro vectorize-kvs 
    [& elements] 
    (vec (map vec (partition 2 elements)))) 
=>#'ns/vectorize-kvs 
=>(vectorize-kvs :a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8 :i 9) 
=>[[:a 1] [:b 2] [:c 3] [:d 4] [:e 5] [:f 6] [:g 7] [:h 8] [:i 9]] 

Sin embargo, para el problema actual que estoy tratando de resolver (que no he entrado), es importante (aunque no es 100% necesario) que la macro sea capaz de tomar mapas asociativos. Parece que estoy buscando cómo convertir el argumento en un PersistentArrayMap antes de que algo tenga la posibilidad de sucederle. Puede haber alguna otra vía hacia una solución que simplemente no estoy considerando o consciente.

Investigué lo mejor que he conocido y aún no he encontrado nada útil. ¿Alguien tiene pensamientos/consejos?

+0

la primera regla del macro club es "no escribir una macro" ... (¿puedes adivinar la segunda?) –

+1

Imagino que la segunda regla es: "Cállate. En serio, no escribas una macro". –

Respuesta

5

puede hacer su mapa con la matriz de mapa

user> (map vec (array-map 1 2 3 4 5 6)) 
([1 2] [3 4] [5 6]) 

o con un mapa más grande

user> (map vec (apply array-map (range 50))) 
([0 1] [2 3] [4 5] [6 7] [8 9] [10 11] [12 13] [14 15] [16 17] [18 19] [20 21] [22 23] [24 25] [26 27] [28 29] [30 31] [32 33] [34 35] [36 37] [38 39] [40 41] [42 43] [44 45] [46 47] [48 49]) 

como un bono se puede evitar el uso de una macro, que es útil porque las macros no son de primera -class y no se componen bien *


una nota en su primer comentario de la documentación en array map

 Note that an array map will only maintain sort order when un-'modified'. 
Subsequent assoc-ing will eventually cause it to 'become' a hash-map.

Si usted se encuentra en función del orden de las claves en sus mapas que puede tener en cuenta si un sorted-map le conseguirá lo que necesita. Escalará mejor que un array-map.En el ejemplo anterior, la salida es la misma:

(map vec (apply sorted-map (range 5000))) 
[0 1] [2 3] ... [4998 4999] 

* Esta es mi opinión


EDIT:

un comparason momento de sorted-map vs array-map

user> (time (dorun (map vec (apply sorted-map (range 500000))))) 
"Elapsed time: 391.520491 msecs" 
nil 
user> (time (dorun (map vec (apply array-map (range 500000))))) 
"Elapsed time: 674517.821669 msecs" 
+0

Gracias por la respuesta. Tristemente, debo desobedecer la primera regla del macro club porque es imperativo para la "función" que hago recibir código no evaluado. No tomo macros a la ligera. Ya probé la función array-map (y también el ordenado-mapa), pero no * realmente * resolvió mi problema porque entonces mi código literal se vería así (array-map: a 1: b 2 ... . etc.) en lugar de {: a 1: b 2 ... etc.}. De hecho, * podría * hacer que funcione, pero cuando vaya a usar mi macro, no podría usar {} para mapas, pero podría tener que usar (matriz-mapa, etc.), y preferiría evitar eso. –

1

El problema como se dijo no es soluble. Los mapas se definen como sin orden; cualquier orden que vea en array-map es una coincidencia. Si necesita que su macro reciba un mapa, ya ha perdido la información que desea.

+1

"a menudo es deseable tener un mapa que mantenga el orden de las teclas. Un mapa de matriz es un mapa de este tipo" http://clojure.org/data_structures#Data%20Structures-ArrayMaps –

+0

No estoy de acuerdo con la afirmación de que es deseable, y con la implicación de que es útil para las transformaciones de código. Por ejemplo, '{25 (println 25), 26 (println 26), 27 (println 27), 28 (println 28), 29 (println 29), 30 (println 30), 31 (println 31), 32 (println 32), 33 (println 33), 34 (println 34)} 'se lee como un hash-map, y por lo tanto se imprime en un orden diferente al léxico; preservar el orden de eso no es posible, por lo que no debe confiarse en ningún caso. – amalloy

Cuestiones relacionadas