5

鉴于我有一个形式的表达

'(map? %)

我如何将它转换成类似的东西

'#(map? %)

这样我最终可以将其扩展为类似

'(apply #(map? %) value)

我想我应该以某种方式使用宏,但不确定如何。

4

2 回答 2

6

调用读取器#宏和读取器宏扩展发生在正常宏扩展发生之前。因此,要执行您提到的操作,您需要在宏中使用阅读器,read-string如下所示。

(defmacro pred [p v] 
     (let [s# (str \# (last p))] 
         `(apply ~(read-string s#) ~v)))



user=> (pred '(map? %) [{}])
true
user=> (pred '(map? %) [[]])
false

如果数据即谓词表达式在运行时可用,那么您需要使用一个函数(它比宏更灵活)。

(defn pred [p v] 
   (let [s (read-string (str \# p))] 
      (eval `(apply ~s ~v))))

user=> (map #(pred % [12]) ['(map? %)'(even? %)])
(false true)
于 2012-09-16T08:01:29.540 回答
3

#(...)是一个阅读器宏。我认为您不能使用阅读器宏生成表达式。例如'#(map? %)会自动展开成(fn* [p1__352#] (map? p1__352#))或类似的东西。

这是关于其他阅读器宏的一些相关讨论。

是否可以更改谓词的格式?如果它看起来像:

'([arg1] (map? arg1))

然后将其制成一个函数将是微不足道的:

(cons 'fn '([arg1] (map? arg1)))

(def pred (eval (cons 'fn '([p](map? p)))))
#'predicate.core/pred

(pred {})
true

(pred 10)
false

现在请不要因为我接下来要发布的内容而讨厌我。我写了一个过于简化的函数阅读器宏:

(defn get-args [p]
  (filter #(.matches (str %) "%\\d*")
          (flatten p)))

(defn gen-args [p]
  (into [] 
        (into (sorted-set) 
              (get-args p))))

(defmacro simulate-reader [p]
  (let [arglist (gen-args p)
        p (if (= (str (first p)) "quote")
            (second p)
            p)]
    (list 'fn (gen-args p) p)))

使用它非常简单:

((simulate-reader '(map? %)) {}) ; -> true
; or without quote
((simulate-reader (map? %)) {})

; This also works:
((simulate-reader '(+ %1 %2)) 10 5) ; -> 15

与@Ankur 给出的其他解决方案的区别是:

  1. 我不喜欢我的。我只是觉得这是一件有趣的事情。
  2. 不需要转换为字符串,然后对其应用阅读器宏。
于 2012-09-16T07:05:14.417 回答