Evaluating Arguments in Clojure Macro

I would like to build a function that, given the 2D matrix and some element from this matrix, will return the position indices of the element:

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

which, returning to the get-in, will return the element itself:

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

I wanted the (get-index) function to be fast, so I was thinking of making a macro that would expand to something similar to the (cond ...) this function (but common to every 2D size matrix NxN)

  (defn get-indices [matrix el] (let [[[abc] [def] [ghi]] 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]))) 

I came up with this macro:

  (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)] [xy])] (cons 'cond (interleave compare-parts indices)))) 

It seemed like it was just nice ... But when called with var, rather than a direct value, it throws an exception:

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

It seems to me that the symbol "matrix" is not allowed for the value at the time of expanding the macro or something like that, but I'm an absolute beginner in macros ...

How can I get this macro to work with vars as arguments?

I also thought about using quote syntax, etc., but I would like (let ...) not to be part of the macro sensor, and also not know how to implement (interleave compare-parts indices) in the syntax quote. ..

+4
source share
1 answer

Writing this macro is a disastrous choice. As a function, this is quite simple and more efficient than you would like your macro to expand anyway:

 (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 (= xw) (recur (inc y) 0 (first remaining), (rest remaining)) (= yh) nil (= (first row) el) [yx] :else (recur y (inc x) (rest row) remaining))))) 

Conversely, as a macro, this is simply not possible. Macros are designed to write code at compile time - how could you generate code based on code for two-dimensional matrices at compile time if you don't know the size of the matrix before runtime?

+10
source

Source: https://habr.com/ru/post/1400456/


All Articles