20

我正在阅读 Let Over Lambda,它处理了一些非常深层次的宏创作。这很有趣,我主要是设法跟上它。

在第 4 章中,Hoyte 为 CL-PPCRE 匹配和替换函数实现了读取器宏,因此您可以执行以下操作:

(#~m/(foo|bar)\d+/ "Some foo99")    ; matches!
(#~s/foo(\d+)/bar\1/, "Some foo99") ; "Some bar99

为了实现这一点,我们定义了一个使用双反引号的宏,因为它实际上是由一个需要引用值的包装宏扩展的(它返回一个 lambda 形式)。在准引用列表中,有一些使用以下序列,',varname,我无法理解。初始,'在这里做什么?

(defmacro! pcre/match-lambda-form (o!args)
  "Expands to a lambda that applies CL-PPCRE:SCAN"
  ``(lambda (,',g!str)
      (cl-ppcre:scan ,(car ,g!args)
                     ,',g!str)))

defmacro实际上,如果你没有读过这本书,为了清楚起见,我将它提炼成使用 just 可能会更好。str是一个符号并且args是一个列表:

(defmacro pcre/match-lambda-form (args)
  "Expands to a lambda that applies CL-PPCRE:SCAN"
  ``(lambda (,',str)
      (cl-ppcre:scan ,(car ,args)
                     ,',str)))

引号基本上是双引号内部部分,以便结果可以被取消引用两次吗?有效地放入'str扩展形式,而不仅仅是str?

编辑 | 感谢 Terje D. 和一些在 REPL 中玩耍的人,情况大致如此:

(defvar a 42)

(equal ``(,,a)  '(list 42)) ; T
(equal ``(,a)   '(list a))  ; T
(equal ``(,',a) ''(42))     ; T
(equal ``(a)    ''(a))      ; T (obviously)

所以:

  • 双引号,表格完全展开。
  • 单引号,形式不展开。
  • 不带逗号,表格完全展开并引用结果。
4

2 回答 2

13

在对双反引号表单求值期间,首先处理内部反引号,结果是单反引号表单。在计算内部反引号形式时,仅计算前面有两个逗号的元素。但是,评估这些双引号元素的结果仍然(单独)未引用,因此在评估得到的单反引号形式时再次评估。要仅以内部反引号形式实现评估,必须插入普通引号,从而导致,',.

怎么看

(let ((tmp (gensym)))
    ``(lambda (,tmp ,,tmp ,',tmp) ()))

评估为

`(LAMBDA (,TMP ,#:G42 #:G42) nil)
于 2013-07-03T09:09:16.610 回答
3

,',X 技巧用于保护 X 免受另一个评估。

怎么看:

     (setq a 'fn)
     (let ((x 'a)) ``(,,x ,',x)) ==>  `(,a a) ==> (fn a)

     ;; ``,',X ==> `,(quote "the value of X") ==> "the value of X"

     ;; ``,,X  ==> `,"the value of X" ==> "the value of the value of X"
于 2013-12-02T14:18:50.340 回答