在Clojure宏中评估参数

mni*_*cky 4 macros clojure

我想构建一个函数,给定一个2D矩阵和该矩阵中的一些元素,它将返回元素位置的索引:

    (get-indices [[1 2 3] [4 5 6] [7 8 9]] 6)
    ;=> [1 2]
Run Code Online (Sandbox Code Playgroud)

返回到get-in,将返回元素本身:

    (get-in [[1 2 3] [4 5 6] [7 8 9]] [1 2])
    ;=> 6
Run Code Online (Sandbox Code Playgroud)

我希望函数(get-indices)快,所以我在考虑做一个宏,它会扩展到类似于(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])))
Run Code Online (Sandbox Code Playgroud)

我想出了这个宏:

      (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))))
Run Code Online (Sandbox Code Playgroud)

它看起来很不错......但是当使用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)
Run Code Online (Sandbox Code Playgroud)

对我而言,似乎符号"矩阵"在宏扩展时或类似的东西没有被解析为值,但我绝对是宏的初学者......

如何使这个宏也可以作为参数使用vars?

我也在考虑使用语法引用等,但我想避免(let ...)作为宏输出的一部分,也不知道如何(interleave compare-parts indices)在语法引用中实现....

ama*_*loy 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)))))
Run Code Online (Sandbox Code Playgroud)

相反,作为一个宏,它根本不可能.宏用于在编译时编写代码 - 如果在运行时之前不知道矩阵的大小,如何在编译时为2D矩阵生成基于cond的代码?