3

我正在阅读 On Lisp,但无法弄清楚为什么下面的代码使用了引号。以下是文本的摘录:

为用户保留的另一个字符组合是#[。图 17.3 给出了一个例子,说明如何将这个字符定义为一种更精细的左括号。它定义了一个 #[xy] 形式的表达式,以将其读取为 x 和 y 之间所有整数的列表,包括:

#[2 7]
(2 3 4 5 6 7)

图 17.3:定义分隔符的读取宏。

(set-macro-character #\] (get-macro-character #\)))

(set-dispatch-macro-character #\# #\[
                              #'(lambda (stream char1 char2)
                                   (let ((accum nil)
                                         (pair (read-delimited-list #\] stream t)))
                                     (do ((i (ceiling (car pair)) (1+ i)))
                                         ((> i (floor (cadr pair)))
                                          (list 'quote (nreverse accum)))
                                       (push i accum)))))

         Figure 17.3: A read-macro defining delimiters.

我不明白为什么do** 的结果表单中的行是 **(list 'quote (nreverse accum)))而不是 (nreverse accum)。因为我们可以毫无问题地运行下面不使用引号的代码,对吧?

(let ((acc nil))
  (do ((i 2 (1+ i)))
      ((> i 7)
       (nreverse acc))
    (push i acc)))

有人知道这里的诀窍吗?

4

1 回答 1

2

如果您在 Lisp 侦听器中输入新语法,阅读器将返回一个数字列表。评估这个数字列表将是一个错误,因为 Lisp 需要一个函数或宏作为列表的头部。列表不会像向量、数字、哈希表……那样对自己进行评估。

因此,您有两个选择:

  1. 让用户在区间表达式前面写一个引号以防止评估
  2. 返回一个引用列表

在这里,我们看到选择 2。

CL-USER 7 > (read-from-string "#[5 10]")
(QUOTE (5 6 7 8 9 10))

CL-USER 8 > (eval (read-from-string "#[5 10]"))
(5 6 7 8 9 10)

CL-USER 9 > (let ((e #[5 10]))
              (describe e))

(5 6 7 8 9 10) is a LIST
0      5
1      6
2      7
3      8
4      9
5      10

如果阅读器宏不会返回报价列表形式,我们将不得不编写:

CL-USER 10 > (let ((e '#[5 10]))   ; notice the QUOTE
               (describe e))

(5 6 7 8 9 10) is a LIST
0      5
1      6
2      7
3      8
4      9
5      10

嗯,我个人实际上更喜欢后者,必须明确地写出报价。

我得到:

CL-USER 17 > '(#[5 10] #[20 25])
((QUOTE (5 6 7 8 9 10)) (QUOTE (20 21 22 23 24 25)))

但我更喜欢:

CL-USER 18 > '(#[5 10] #[20 25])
((5 6 7 8 9 10) (20 21 22 23 24 25))
于 2014-04-27T09:05:01.220 回答