2012-03-08 123 views
4

我想建立一个功能,其中,由于一个二维矩阵,并从该矩阵的一些元素,将返回元素的位置的指标:评估参数宏观

(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-指数)要快,所以我想这样做宏将扩展到类似的(cond ...)部分东西此功能(但是对于每个尺寸为NxN的2D矩阵都是通用的):

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

回答

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

相反,宏这是根本不可能的。宏是用于编译时编写代码的 - 如果在运行时不知道矩阵的大小,在编译时如何为二维矩阵生成基于cond的代码?

+0

请注意,您可以使用重复的“调入”调用来更轻松地编写此代码 - 我选择一次遍历矩阵中的一行和一个元素,以避免随机访问和(希望提高)性能,因为您似乎关心。 – amalloy 2012-03-08 22:12:08

+0

感谢您的澄清和功能!它真的很快(虽然看起来不比想要写的更快 - 而且不可能写出来 - 宏;-)。只有一个问题:你的意思是什么随机访问? – mnicky 2012-03-09 10:54:23

+0

哦,也许我已经看到你的意思了:在增加索引时调用'get-in',从而随机访问矩阵,不是吗? – mnicky 2012-03-09 11:13:18