如何将表达式转换为谓词? (Clojure的)

时间:2012-09-16 01:57:37

标签: clojure expression predicate

鉴于我有一个表格

'(map? %)

如何将其转换为

之类的内容
'#(map? %)

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

的内容
'(apply #(map? %) value)

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

2 个答案:

答案 0 :(得分: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)

答案 1 :(得分:3)

#(...)是一个读者宏。我认为你不能用reader宏生成表达式。例如,'#(map? %)会自动扩展为(fn* [p1__352#] (map? p1__352#))或类似内容。

这是其他阅读器宏的relevant discussion

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

'([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. 不需要转换为字符串,然后将reader宏应用于它。
相关问题