Изобретаю ли я (квадратное) колесо?

#macros #map #clojure

#макросы #словарь #clojure

Вопрос:

Как мы должны знать, Clojure map может быть применен к последовательности:

 (map #(* %1 %1) [1 2 3])           ; (1)
  

..или для нескольких, таким образом:

 (map vector [0 1] [2 1])           ; (2)
;=> ([0 2] [1 1])
  

Теперь я хочу получить тот же результат, что и (2), но у меня есть аргументы, хранящиеся внутри последовательности. Другими словами, следующее не дает желаемого результата:

 (map vector [[0 1] [2 1]])         ; (3)
;=> ([[0 1]] [[2 1]])
  

Итак, я написал этот простой макрос, где umap расшифровывается как «unsplice map».:

 (defmacro umap [fun args-list]
  "umap stands for unspliced map.
  Let args-list be a list of args [a1 a2 ... an].
  umap is the same of (map fun a1 a2 .. an)"
  `(map ~fun ~@args-list))
  

Очевидно, что это работает как шарм:

 (umap vector [[0 1] [2 1]])        ; (4)
;=> ([0 2] [1 1])
  

Итак, вот мой вопрос: изобретаю ли я колесо?
Есть ли другой способ сделать то же самое, что и (4)?

Пока и заранее спасибо,

Альфредо

Ответ №1:

apply распаковывает все элементы в последовательности в конце списка аргументов.

 user> (apply map vector [[0 1] [2 1]])
([0 2] [1 1])
  

Комментарии:

1. Спасибо, я был уверен, что изобретаю колесо заново 🙂

Ответ №2:

Совсем не очевидно, что ваш umap работает как шарм. На самом деле это сработает только в том случае, если во время компиляции у вас есть вектор аргументов в виде литерала — а это именно то время, когда вы в любом случае могли бы передать несколько аргументов map !

 user> (umap vector [[1 2] [1 2]])
([1 1] [2 2])
user> (let [args [[1 2] [1 2]]]
        (umap vector args))
java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol
[Thrown class java.lang.RuntimeException]
  

Макрос имеет доступ только к символу args , потому что он выполняется во время компиляции и не может объединить его для использования map. Правильный ответ — использовать apply , который обрабатывает свой последний аргумент как последовательность и объединяет его в серию дополнительных аргументов для данной функции:

 user> (let [args [[1 2] [1 2]]]
        (apply map vector args))
([1 1] [2 2])
  

Комментарии:

1. Спасибо, амаллой, твои ответы всегда полезны!

Ответ №3:

Я что-то упускаю, или mapcat подходит для ваших нужд?

Комментарии:

1. Вы, должно быть, чего-то не хватает, но я не уверен, чего. mapcat это ни на йоту не приближает его к решению его проблемы.

2. Ах, ладно. Кажется, теперь я понимаю, чего мне не хватало.