Макрос Clojure, сохраняющий порядок ассоциативных карт.

В предисловии скажу, что я работаю в Windows 7 (64-разрядная версия), использую Java версии 6 (обновление 33) и использую cloojв качестве своей IDE. Я не пытался воспроизвести свою проблему в любой другой системе. У меня есть опыт работы с Clojure, но совсем не с Java.

Всю проблему, которую я пытаюсь решить, долго описывать, но она сводится к следующему: скажем, я хочу создать макрос, который принимает один аргумент, ассоциативную карту, и возвращает вектор элементы карты с сохранением их порядка.

=>(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]]

Это работает, но добавьте еще один элемент на карту, и порядок испортится...

=>(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]]

Кажется, я обнаружил, почему это происходит. Кажется, что все, что содержит 8 или меньше элементов, создается как экземпляр PersistentArrayMap, и это именно то, что мне нужно, потому что, насколько я могу судить, этот класс сохраняет порядок. Однако все, что содержит 9 или более элементов, создается как экземпляр PersistentHashMap, который не сохраняет порядок.

=>(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

Я бы хотел, чтобы мой макрос мог брать ассоциативные карты любого размера, так что это проблема. Я пробовал подсказку типа, деструктурирующую привязку, для понимания списка и объединение без кавычек, но все безуспешно.Чтобы нарисовать это, ничего из следующего не сработает:

(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 ~@associative-map))

С этой игрушечной проблемой, которую я представляю, я понимаю, что мог бы просто написать свой макрос таким образом и вообще избежать проблемы:

=>(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]]

Однако для фактического проблема, которую я пытаюсь решить (в которую я не вникал), важно (хотя и не на 100% обязательно), чтобы макрос умел брать ассоциативные карты. Похоже, я ищу, как передать аргумент в PersistentArrayMap, прежде чем с ним что-то случится. Может быть какой-то другой путь к решению, который я просто не рассматриваю или не знаю.

Я исследовал все, что знал, и пока не нашел ничего полезного. У кого-нибудь есть мысли/советы?

6
задан Omri Bernstein 21 June 2012 в 01:35
поделиться