Вопрос по casting, hashmap, clojure, macros, associative-array – Макрос Clojure, который сохранит ассоциативный порядок отображения

6

В качестве предисловия я нахожусь на 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

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

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

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

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

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

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

Полагаю, второе правило таково: «Заткнись. Серьезно, не пишите макрос. Omri Bernstein
первое правило макроклуба - «не пишите макрос»; ... (можете угадать второе) Arthur Ulfeldt

Ваш Ответ

2   ответа
5

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

или с большей картой

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])

в качестве бонуса вы можете избежать использования макроса, который полезен, потому что макросы не являются первоклассными и плохо сочетаются *

записка на ваш первый комментарий из документации по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.

Если вы попадаете в зависимости от порядка ключей на ваших картах, вы можете рассмотреть возможностьsorted-map даст вам то, что вам нужно. Это будет масштабироваться лучше, чемarray-map, В приведенном выше примере результат такой же:

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

*это мое мнение

РЕДАКТИРОВАТЬ:

временное сравнениеsorted-map против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"
Спасибо за ответ. К сожалению, я должен не повиноваться первому правилу макроклуба, потому что оно обязательно для «функции» Я делаю, чтобы получить неоцененный код. Я не воспринимаю макросы легкомысленно. Я уже опробовал функцию array-map (а также sorted-map), но это не такreally решить мою проблему, потому что тогда мой буквальный код будет выглядеть так: (массив-карта: a 1: b 2 ... и т. д.) вместо {: a 1: b 2 ... и т. д.}. На самом деле, яcould заставить это работать, но потом, когда я буду использовать свой макрос, я не обязательно смогу использовать {} для карт, но, возможно, придется использовать (массив-карта и т. д.), и я предпочел бы избежать этого. Omri Bernstein
1

как указано, не решаема. Карты определены как не имеющие порядка; любой заказ, который вы видите вarray-map это случайно. Если вам требуется, чтобы ваш макрос получил карту, вы уже потеряли нужную информацию.

& Quot; часто желательно иметь карту, которая поддерживает ключевой порядок. Карта массива является такой картой & quot;clojure.org/data_structures#Data%20Structures-ArrayMaps
Я не согласен как с утверждением, что это желательно, так и с тем, что это полезно для преобразований кода. Например,{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)} читается как хэш-карта и, следовательно, печатает в другом порядке, нежели лексический; сохранение порядка этого невозможно, поэтому на него нельзя полагаться в любом случае.

Похожие вопросы