我想建立一个函数,给定一个2D矩阵和该矩阵中的一些元素,它将返回元素位置的索引:
(get-indices [[1 2 3] [4 5 6] [7 8 9]] 6)
;=> [1 2]
,返回到get-in,将返回元素本身:
(get-in [[1 2 3] [4 5 6] [7 8 9]] [1 2])
;=> 6
我希望函数(get-indices)快,所以我在考虑做一个宏,它会扩展到类似于这个函数的(cond ...)
部分(但对于每个大小的2D矩阵都是通用的)的N×N):
(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)
对我而言,它似乎是符号"矩阵"并没有决定在宏扩展时间或类似的东西上重视价值,但我是宏的绝对初学者......
如何让这个宏也能作为参数使用vars?
我也在考虑使用语法引用等,但我希望避免将(let ...)
作为宏输出的一部分,并且也不知道如何实现{{ 1}}在syntax-quote ....
答案 0 :(得分:10)
将其写为宏是一个灾难性的选择。作为一个函数,它非常简单,并且比你想要的宏扩展到更高效:
(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)))))
相反,作为一个宏,它根本不可能。宏用于在编译时编写代码 - 如果在运行时之前不知道矩阵的大小,如何在编译时为2D矩阵生成基于cond的代码?