gpt4 book ai didi

macros - 有没有办法混合 LISP 的宏 &optional 和 &key 参数?

转载 作者:太空宇宙 更新时间:2023-11-03 18:39:28 24 4
gpt4 key购买 nike

我想定义一个 LISP 宏,例如 dolist,它可以让我定义一个可选的输出参数。在下面的案例研究中,这个宏将被称为doread。它将从文件中读取行并返回以这种方式找到的行数。

(let ((lines 0))
(doread (line file lines)
;; do something with line
(incf lines)))

问题是让在上面的宏中工作

我可以用 &key 做我想做的事,但不能用 &optional &key (而且 &key 是必需的,因为我想控制文件的读取方式;例如 readread-行 或其他)。

现在下面的工作但是以错误的方式工作。这里的 out 参数必须是 &key 而不是 &optional:

;; this way works... 

(defmacro doread ((it f &key out (take #'read)) &body body)
"Iterator for running over files or strings."
(let ((str (gensym)))
`(with-open-file (,str f)
(loop for ,it = (funcall ,take ,str nil)
while ,it do
(progn ,@body))
,out)))

;; lets me define something that reads first line of a file
(defun para1 (f)
"Read everything up to first blank line."
(with-output-to-string (s)
(doread (x f :take #'read-line)
(if (equalp "" (string-trim '(#\Space #\Tab) x))
(return)
(format s "~a~%" x)))))

(print (para1 sometime)) ; ==> shows all up to first blank line

我想做的是如下(注意 out 现在已经移入 &optional:

(defmacro doread ((it f &optional out &key   (take #'read)) &body body)
"Iterator for running over files or strings."
(let ((str (gensym)))
`(with-open-file (,str f)
(loop for ,it = (funcall ,take ,str nil)
while ,it do
(progn ,@body))
,out)))

如果可行,我可以做类似的事情。

(defun para1 (f)
"Print everything up to first blank line.
Return the lines found in that way"
(let ((lines 0))
(doread (x f lines :take #'read-line)
(if (equalp "" (string-trim '(#\Space #\Tab) x))
(return)
(and (incf lines) (format t "~a~%" x)))))

但我使用 &optional out 我得到了

 loading /Users/timm/gits/timm/lisp/src/lib/macros.lisp
*** - GETF: the property list (#'READ-LINE) has an odd length

最佳答案

您不能混合使用 &optional&key 并期望只能传递关键字参数。但是,您可以定义一个语法允许与关联的参数的可选列表来源。

例如:

(defpackage :so (:use :cl :alexandria))
(in-package :so)

(defmacro do-read ((entry source &optional result) &body body)
(destructuring-bind (source &key (take '#'read)) (ensure-list source)
(once-only (take)
`(loop
:with ,entry
:do (setf ,entry (handler-case (funcall ,take ,source)
(end-of-file () (loop-finish))))
(progn ,@body)
:finally (return ,result)))))

DO-READ 的语法可以写成:

(DO-READ (ENTRY [SOURCE|(SOURCE &KEY TAKE)] &OPTIONAL RESULT) . BODY)

这不是一个不寻常的语法 w.r.t.标准 Lisp 形式(参见 LET、lambda 列表中的关键字 synax、defstruct 等)。您可以添加更多关键字参数以及 TAKE

备注

  • 在宏中,我更喜欢将 LOOP 关键字作为关键字发出,而不是符号在宏的定义包中;否则,当宏展开代码,您可能会得到以宏为前缀的符号包(即 SO::WITH 而不是 :WITH),它变得很快不可读。

  • READ-LINE 返回 NIL 没问题,但从 READ 返回 NIL 就不行,因为 NIL 可能是一个成功读取的值。在一般来说,由于 TAKE 是由用户提供的,你不知道 NIL 是否是可以接受的结果。这就是为什么我捕获END-OF-FILE 代替。如果您想从其他来源读取信息,您还可以检查辅助返回值,或记录它们也发出条件信号。

  • ENTRY 变量的范围已扩展,因此 RESULT 可以是ENTRY 本身;在你的例子中,OUT 不能等于 IT,因为一旦退出循环,就无法再访问它。这是一个小问题,但这可能很有用。

  • 我没有包含 WITH-OPEN-FILE,以防你想从文件(流)以外的东西。

  • #'READ 被引用,这在这里并不重要,但在宏中有一个好习惯,这样你就可以在评估时实际评估事物,而不是在宏展开时。

例子

(with-input-from-string (in "abcdef")
(do-read (char (in :take #'read-char) char)
(print char)))

打印所有字符并返回#\f

(with-input-from-string (in (format nil "~{~a~%~}" *features*))
(let ((lines 0))
(do-read (line in lines)
(incf lines))))

打印字符串中的行数。

关于macros - 有没有办法混合 LISP 的宏 &optional 和 &key 参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56776557/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com