定义宏以获取输入流

时间:2017-10-26 13:11:51

标签: macros lisp common-lisp

我正在尝试创建一个宏,它将接受输入流并根据读取的第一行的内容执行不同的操作,然后读取更多输入。我只是有一个宏来接收输入流并从中读取一些值。

一个人为的例子:

(defmacro read-and-print (&optional in)
  `(print
    ,(if (string= (read-line in) "greet")
         `(concatenate 'string "hello" (read-line ,in))
         `(read-line ,in))))

(with-input-from-string (in "greet
bob") (read-and-print in))

但即使这样也会产生以下错误

 There is no applicable method for the generic function
   #<STANDARD-GENERIC-FUNCTION SB-GRAY:STREAM-READ-LINE (1)>
 when called with arguments
   (IN).
   [Condition of type SB-INT:COMPILED-PROGRAM-ERROR]

令我感到困惑的是,甚至更改函数以获取第一行的字符串不起作用:

(defmacro read-and-print (command &optional in)
  `(print
    ,(if (string= command "greet")
         `(concatenate 'string "hello " (read-line ,in))
         `(read-line ,in))))

(with-input-from-string (in "greet
bob")
  (read-and-print (read-string in) in))

这给了我

 The value
   (READ-LINE IN)
 is not of type
   (OR (VECTOR CHARACTER) (VECTOR NIL) BASE-STRING SYMBOL CHARACTER)
 when binding SB-IMPL::STRING1
   [Condition of type SB-INT:COMPILED-PROGRAM-ERROR]

虽然这完全可以执行:

(with-input-from-string (in "greet
bob")
  (read-and-print "greet" in))

我遗失的with-input-from-string宏有什么特别之处吗?我怀疑我遗漏了一些关于宏的非常明显的东西,但谷歌搜索让我无处可去。

2 个答案:

答案 0 :(得分:3)

你问的是什么

宏是一种代码生成工具。 他们评估他们的论点。

您的with-input-from-string示例有效,因为字符串是自我评估的。如果您完全是字符串文字,则会出错。

你应该问什么

需要一个宏。改为使用函数。

在决定功能和宏之间时,你需要问问自己:

  • 我是否定义了新语法?
  • 代码是否生成更多代码?

除非您理解问题并回答,否则您应该使用函数。

另见How does Lisp let you redefine the language itself?

答案 1 :(得分:1)

你的宏

(defmacro read-and-print (&optional in)
  `(print
    ,(if (string= (read-line in) "greet")
         `(concatenate 'string "hello" (read-line ,in))
         `(read-line ,in))))

但是,(if ... in ...)毫无意义,因为in的值通常不是宏扩展时的流,而是代码(符号,表达式......)。由于您的代码没有执行,并且宏看到了源(而不是尚未执行的值的值),因此您无法执行此操作。你也无法有效地修复它:它只是错误的方法。改为使用函数。

使用功能

CL-USER 17 > (defun read-and-print (&optional in)
               (if (string= (read-line in) "greet")
                   (concatenate 'string "hello" (read-line in))
                 (read-line in)))
READ-AND-PRINT

CL-USER 18 > (with-input-from-string (in "greet
foo
bar")
               (read-and-print in))
"hellofoo"

使用宏

您仍然可以将其编写为宏,但是您需要生成代码以便它在运行时运行,而不是在宏扩展时运行:

(defmacro read-and-print (&optional in)
  `(print
    (if (string= (read-line ,in) "greet")
        (concatenate 'string "hello" (read-line ,in))
      (read-line ,in))))

注意:人们实际上可能想要处理in未多次评估。

这个宏会为您提供代码内联的优势。 这个宏会给你带来代码内联的缺点。

上面的函数为您提供了代码通常不内联的优势。 上面的函数通过告诉编译器使用内联声明来实现代码,可以选择内联代码。