Syntax 公共lisp宏语法关键字:我该怎么称呼它?

Syntax 公共lisp宏语法关键字:我该怎么称呼它?,syntax,macros,common-lisp,keyword,Syntax,Macros,Common Lisp,Keyword,为了自己回答这个问题,我查阅了Lisp、实用公共Lisp和SO文档,但是由于我无法说出我感兴趣的概念,这些尝试都失败了。如果有人能告诉我这类事情的标准术语,我将不胜感激 这个问题最好用一个例子来解释。假设我想在CommonLisp中实现Python风格的列表理解。我将用Python编写: [x*2 for x in range(1,10) if x > 3] 因此,我首先写下: (listc (* 2 x) x (range 1 10) (> x 3)) 然后定义一个宏,将上述内

为了自己回答这个问题,我查阅了Lisp、实用公共Lisp和SO文档,但是由于我无法说出我感兴趣的概念,这些尝试都失败了。如果有人能告诉我这类事情的标准术语,我将不胜感激

这个问题最好用一个例子来解释。假设我想在CommonLisp中实现Python风格的列表理解。我将用Python编写:

[x*2 for x in range(1,10) if x > 3]
因此,我首先写下:

(listc (* 2 x) x (range 1 10) (> x 3))
然后定义一个宏,将上述内容转换为正确的理解。到目前为止还不错

然而,对于还不熟悉Python列表理解的读者来说,该表达式的解释是不透明的。我真正想写的是以下内容:

(listc (* 2 x) for x in (range 1 10) if (> x 3)) 
但是我还没有找到这个常用的Lisp术语。似乎
循环
宏正是这样做的。它叫什么,我如何实现它?我尝试对一个循环表达式示例进行宏扩展,以查看它是如何组合在一起的,但生成的代码无法理解。有人能给我指引正确的方向吗


提前感谢。

好吧,它的基本功能是解析作为其主体提供的表单。例如:

(defmacro listc (expr &rest forms)
  ;; 
  ;;
  ;; (listc EXP for VAR in GENERATOR [if CONDITION])
  ;;
  ;;
  (labels ((keyword-p (thing name)
             (and (symbolp thing)
                  (string= name thing))))
    (destructuring-bind (for* variable in* generator &rest tail) forms
      (unless (and (keyword-p for* "FOR") (keyword-p in* "IN"))
        (error "malformed comprehension"))
      (let ((guard (if (null tail) 't
                       (destructuring-bind (if* condition) tail
                         (unless (keyword-p if* "IF") (error "malformed comprehension"))
                         condition))))
        `(loop 
            :for ,variable :in ,generator 
            :when ,guard 
            :collecting ,expr)))))


(defun range (start end &optional (by 1))
  (loop
     :for k :upfrom start :below end :by by
     :collecting k))
除了我使用的黑客“解析器”之外,这个解决方案还有一个缺点,这在普通lisp中是不容易解决的,即如果您想链接您的理解,那么中间列表的构造:

(listc x for x in (listc ...) if (evenp x))
由于在common lisp中没有与
yield
道德等价的工具,因此很难创建一个不需要完全实现中间结果的工具。一种解决方法可能是在
listc
的扩展器中编码可能的“生成器”形式的知识,以便扩展器可以优化/内联基本序列的生成,而不必在运行时构建整个中间列表

另一种方法可能是引入(链接点到scheme,因为在common lisp中没有等效的工具——您必须首先构建它,尽管这并不特别困难)

此外,您可以随时查看其他人的代码,特别是当他们试图解决相同或类似的问题时,例如:

(defmacro listc (expr &rest forms)
  ;; 
  ;;
  ;; (listc EXP for VAR in GENERATOR [if CONDITION])
  ;;
  ;;
  (labels ((keyword-p (thing name)
             (and (symbolp thing)
                  (string= name thing))))
    (destructuring-bind (for* variable in* generator &rest tail) forms
      (unless (and (keyword-p for* "FOR") (keyword-p in* "IN"))
        (error "malformed comprehension"))
      (let ((guard (if (null tail) 't
                       (destructuring-bind (if* condition) tail
                         (unless (keyword-p if* "IF") (error "malformed comprehension"))
                         condition))))
        `(loop 
            :for ,variable :in ,generator 
            :when ,guard 
            :collecting ,expr)))))


(defun range (start end &optional (by 1))
  (loop
     :for k :upfrom start :below end :by by
     :collecting k))
  • (哪个做了懒惰列表的事情)

补充一下德克的答案: 为此编写自己的宏是完全可行的,而且可能是一个很好的练习。 然而,有几种高质量的工具(尽管是以lisp惯用的方式),例如

循环非常有表现力,但其语法与普通lisp的其余部分不同。有些编辑不喜欢它,而且缩进很差。然而,循环是在标准中定义的。通常不可能将扩展写入循环

Iterate更具表现力,并且具有熟悉的lispy语法。这不需要任何特殊的缩进规则,因此所有正确缩进lisp的编辑器也可以很好地缩进迭代。Iterate不在标准中,因此您必须自己获得它(使用quicklisp)


Series是处理序列的框架。在大多数情况下,序列可以不存储中间值。

宏是代码转换器

有几种实现宏语法的方法:

  • 解构
Common Lisp提供了一个宏参数列表,该列表还提供了一种解构形式。使用宏时,源窗体将根据参数列表进行分解

这限制了宏语法的外观,但对于许多宏的使用,它提供了足够的机制

请参见Common Lisp中的

(loop for i from 0 when (oddp i) collect i)
  • 解析
Common Lisp还允许宏访问整个宏调用表单。然后宏负责解析表单。解析器需要由宏作者提供,或者是作者完成的宏实现的一部分

例如中缀宏:

(infix (2 + x) * (3 + sin (y)))
宏实现需要实现中缀解析器并返回前缀表达式:

(* (+ 2 x) (+ 3 (sin y)))
  • 基于规则的
一些Lisp提供语法规则,这些规则与宏调用表单相匹配。对于匹配的语法规则,将使用相应的转换器创建新的源表单。可以在CommonLisp中轻松实现这一点,但默认情况下,CommonLisp中没有提供这一机制

(loop for i from 0 when (oddp i) collect i)
见计划

循环

为了实现类似
循环的语法,需要编写一个解析器,在宏中调用该解析器来解析源表达式。请注意,解析器不处理文本,而是处理内部Lisp数据

在过去(20世纪70年代),Interlisp在所谓的“会话Lisp”中使用了这种语法,这是一种Lisp语法,具有更自然的语言外观。迭代是其中的一部分,迭代思想随后被引入到其他Lisp中(比如Maclisp的
循环
,然后被引入到Common Lisp中)

请参阅沃伦·泰尔曼(Warren Teitelmann)上世纪70年代的PDF格式

循环
宏的语法有点复杂,不容易看到各个子语句之间的边界

请参见公共Lisp中的

(loop for i from 0 when (oddp i) collect i)
同:

(loop
   for i from 0
   when (oddp i)
   collect i)
(iter
  (for i from 0)
  (when (oddp i)
    (collect i)))
循环宏存在的一个问题是,诸如
FOR
FROM
WHEN
COLLECT
等符号与“COMMON-LISP”包(名称空间)中的符号不同。当我现在使用另一个包(名称空间)在源代码中使用
LOOP
时,这将导致在这个源名称空间中出现新的符号。出于这个原因,有些人喜欢写:

(loop
   :for i :from 0
   :when (oddp i)
   :collect i)
在上述代码中,
循环
相关符号的标识符位于键中