I'd like to build a function, which, given a 2D matrix and some element from that matrix, will return the indexes of the element's position:
(get-indices [[1 2 3] [4 5 6] [7 8 9]] 6)
;=> [1 2]
which, given back to get-in, will return the element itself:
(get-in [[1 2 3] [4 5 6] [7 8 9]] [1 2])
;=> 6
I wanted the function (get-indices) to be fast, so I was thinking about doing a macro which will expand to something similar to the (cond ...)
part of this function (but generic for every 2D matrix of size 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])))
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)] [x y])]
(cons 'cond (interleave compare-parts indices))))
It seemed just nice... But when called with var, not 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)
To me it seems like the symbol "matrix" isn't resolved to value at macro expansion time or something like that, but I'm absolute beginner in macros...
How can I make this macro to work also with vars as arguments?
I was also thinking about using syntax-quote etc., but I'd like to avoid having (let ...)
as a part of the macro output and also didn't know how to implement (interleave compare-parts indices)
within the syntax-quote....