How to wrap and execute a lisp s-expression by another s-expression?
我试图用另一个 lisp 表达式package一个 lisp 表达式。我想,一个宏应该做到这一点,但我不明白这一点。谁能帮帮我,谁知道怎么做?
我的实际目标是编写一个宏,将一批
(我想编写一个脚本/程序,它打开一个或两个输入文件,逐行处理它们,但还将处理结果输出到几个不同的独立输出文件中。为此,我希望有
由于
1 2 3 4 5 6 7 8 9 10 | ;; Output file-paths: (defparameter *paths* '("~/out1.lisp""~/out2.lisp""~/out3.lisp")) ;; stream handlers (symbols for the output streams) (defparameter *handlers* '(out1 out2 out3)) ;; code which I would love to execute in the body (print"something1" out1) (print"something2" out2) (print"something3" out3) |
我希望如何调用宏:
1 2 3 4 5 6 7 8 9 10 | (with-open-files (*handlers* *paths* '(:direction :output :if-exists :append)) ;; the third macro argument should be what should be passed to the ;; individual `with-open-file` calls ;; and it might be without `quote`-ing or with `quote`-ing ;; - is there by the way a good-practice for such cases? - ;; - is it recommended to have `quote`-ing? Or how would you do that? - ;; and then follows the code which should be in the macro body: (print"something1" out1) (print"something2" out2) (print"something3" out3)) |
宏调用应该扩展的内容:
1 2 3 4 5 6 | (with-open-file (out1"~/out1.lisp" :direction :output :if-exists :append) (with-open-file (out2"~/out2.lisp" :direction :output :if-exists :append) (with-open-file (out3"~/out3.lisp" :direction :output :if-exists :append) (print"something1" out1) (print"something2" out2) (print"something3" out3)))) |
作为一个步骤,我认为我必须让一个 s 表达式package另一个 s 表达式。
我的第一个问题是:如何用另一个 s 表达式package一个 s 表达式?但我现在无法管理它。
我所能做的就是编写一个函数,它只会溢出一个未执行的表达式。如何编写一个宏,它做同样的事情,但在以这种方式扩展后还执行代码?
1 2 3 4 5 6 7 8 9 10 11 | (defun wrap (s-expr-1 s-expr-2) (append s-expr-1 (list s-expr-2))) (wrap '(func1 arg1) '(func2 arg2)) ;; => (FUNC1 ARG1 (FUNC2 ARG2)) (wrap '(with-open-files (out1"~/out1.lisp" :direction :output :if-exists :append)) '(with-open-files (out2"~/out2.lisp" :direction :output :if-exists :append) (print"something1" out1) (print"something2" out2) (print"something3" out3))) |
给出:
1 2 3 4 5 | (WITH-OPEN-FILES (OUT1"~/out1.lisp" :DIRECTION :OUTPUT :IF-EXISTS :APPEND) (WITH-OPEN-FILES (OUT2"~/out2.lisp" :DIRECTION :OUTPUT :IF-EXISTS :APPEND) (PRINT"something1" OUT1) (PRINT"something2" OUT2) (PRINT"something3" OUT3))) |
这样,连续应用
但是,这些函数只会生成代码而不执行它。
最后我将被迫使用
执行死刑后,我遇到了大麻烦。而且由于不能在宏(而不是函数名)上调用
当完成用另一个s-expression将s-expressionpackage在一个宏中并让它被评估时,下一个问题是,如何处理列表以让代码扩展为所需的代码然后被评估?我只是尝试了几个小时并没有走远。
我需要有编写此类宏经验的人的帮助...
请注意,在 Lisp 中,"处理程序"通常是一个函数,而不是一个符号。您的命名令人困惑。
静止的
如果您正在生成代码,您应该使用宏,而不是函数。
这假设您在编译时知道哪些文件和流
您将使用的变量:
最简单的方法是使用递归:
1 2 3 4 5 6 | (defmacro with-open-files ((streams file-names &rest options &key &allow-other-keys) &body body) (if (and streams file-names) `(with-open-file (,(pop streams) ,(pop file-names) ,@options) (with-open-files (,streams ,file-names ,@options) ,@body)) `(progn ,@body))) |
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | (macroexpand-1 '(with-open-files ((a b c) ("f""g""h") :direction :output :if-exists :supersede) (print"a" a) (print"b" b) (print"c" c))) ==> (WITH-OPEN-FILE (A"f" :DIRECTION :OUTPUT :IF-EXISTS :SUPERSEDE) (WITH-OPEN-FILES ((B C) ("g""h") :DIRECTION :OUTPUT :IF-EXISTS :SUPERSEDE) (PRINT"a" A) (PRINT"b" B) (PRINT"c" C))) (macroexpand-1 '(with-open-files ((a) ("f") :direction :output :if-exists :supersede) (print"a" a))) ==> (WITH-OPEN-FILE (A"f" :DIRECTION :OUTPUT :IF-EXISTS :SUPERSEDE) (WITH-OPEN-FILES (NIL NIL :DIRECTION :OUTPUT :IF-EXISTS :SUPERSEDE) (PRINT"a" A))) (macroexpand-1 '(with-open-files (nil nil :direction :output :if-exists :supersede) (print nil))) ==> (PROGN (PRINT NIL)) |
动态的
如果您在编译时不知道流和文件是什么,例如,
它们存储在
上面的宏 - 你必须自己使用
捕获。注意
评估(即,参数
被评估一次,而不是多次):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | (defmacro with-open-files-d ((streams file-names &rest options &key &allow-other-keys) &body body) (let ((sv (gensym"STREAM-VARIABLES-")) (so (gensym"STREAM-OBJECTS-")) (ab (gensym"ABORT-")) (op (gensym"OPTIONS-"))) `(let* ((,sv ,streams) (,ab t) (,op (list ,@options)) (,so (mapcar (lambda (fn) (apply #'open fn ,op)) ,file-names))) (progv ,sv ,so (unwind-protect (multiple-value-prog1 (progn ,@body) (setq ,ab nil)) (dolist (s ,so) (when s (close s :abort ,ab)))))))) (macroexpand-1 '(with-open-files-d ('(a b c) '("f""g""h") :direction :output :if-exists :supersede) (print"a" a) (print"b" b) (print"c" c))) ==> (LET* ((#:STREAM-VARIABLES-372 '(A B C)) (#:ABORT-374 T) (#:OPTIONS-375 (LIST :DIRECTION :OUTPUT :IF-EXISTS :SUPERSEDE)) (#:STREAM-OBJECTS-373 (MAPCAR (LAMBDA (FN) (APPLY #'OPEN FN #:OPTIONS-375)) '("f""g""h")))) (PROGV #:STREAM-VARIABLES-372 #:STREAM-OBJECTS-373 (UNWIND-PROTECT (MULTIPLE-VALUE-PROG1 (PROGN (PRINT"a" A) (PRINT"b" B) (PRINT"c" C)) (SETQ #:ABORT-374 NIL)) (DOLIST (S #:STREAM-OBJECTS-373) (WHEN S (CLOSE S :ABORT #:ABORT-374)))))) |
这里流变量和文件列表都是在运行时计算的。
重要的
这里一个重要的实用注意事项是,静态版本更健壮,因为它保证所有流都已关闭,而动态版本将无法关闭剩余的流,例如,如果第一个