Оценка аргументов в макросе Clojure - PullRequest
4 голосов
/ 09 марта 2012

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

    (get-indices [[1 2 3] [4 5 6] [7 8 9]] 6)
    ;=> [1 2]

, который при возврате вернет сам элемент:

    (get-in [[1 2 3] [4 5 6] [7 8 9]] [1 2])
    ;=> 6

Я хотел, чтобы функция (get-index) была быстрой, поэтому я думал о создании макроса, который расширится до чего-то похожего на часть (cond ...) этой функции (но универсальную для каждой 2D-матрицы размера NxN) :

      (defn get-indices
        [matrix el]
        (let [[[a b c] [d e f] [g h i]] matrix]
          (cond
            (= a el) [0 0]
            (= b el) [0 1]
            (= c el) [0 2]
            (= d el) [1 0]
            (= e el) [1 1]
            (= f el) [1 2]
            (= g el) [2 0]
            (= h el) [2 1]
            (= i el) [2 2])))

Я придумал этот макрос:

      (defmacro get-indices
        [matrix el]
        (let [size            (count matrix)
              flat            (flatten matrix)
              compare-parts   (map #(list '= % el) flat)
              indices         (for [x (range size) y (range size)] [x y])]
           (cons 'cond (interleave compare-parts indices))))

Это выглядело просто замечательно ... Но при вызове с помощью var, а не прямого значения, выдается исключение:

      (def my-matrix [[1 2 3] [4 5 6] [7 8 9]])

      (get-indices my-matrix 6)
      ;=> java.lang.UnsupportedOperationException: count not supported on this
      ;   type: Symbol (NO_SOURCE_FILE:0)

Мне кажется, что символ "матрица" не разрешен к значению во время расширения макроса или что-то в этом роде, но я абсолютный новичок в макросах ...

Как я могу заставить этот макрос работать также с переменными в качестве аргументов?

Я также думал об использовании синтаксической кавычки и т. Д., Но я хотел бы избежать использования (let ...) в качестве части вывода макроса и также не знал, как реализовать (interleave compare-parts indices) в синтаксической кавычке ....

1 Ответ

10 голосов
/ 09 марта 2012

Запись этого в виде макроса - катастрофический выбор. Как функция, она довольно проста и более эффективна, чем то, что вы хотели, чтобы ваш макрос в любом случае расширился:

(defn get-indices [matrix el]
  (let [h (count matrix), w (count (first matrix))]
    (loop [y 0, x 0, row (first matrix), remaining (rest matrix)]
      (cond (= x w) (recur (inc y) 0 (first remaining), (rest remaining))
            (= y h) nil
            (= (first row) el) [y x]
            :else (recur y (inc x) (rest row) remaining)))))

И наоборот, как макрос это просто невозможно. Макросы предназначены для написания кода во время компиляции - как вы можете сгенерировать код на основе cond для 2D матриц во время компиляции, если вы не знаете размер матрицы до времени выполнения?

...