sbcl(和 clisp):什么时候字符不是字符? (使用 defconstant)

sbcl (and clisp): When is a character not a character? (using defconstant)

这个问题是关于 sbcl 的——或者我最初是这么想的。问题:什么时候角色不是角色?考虑以下代码:

(defconstant +asc-lf+    #\Newline)
(defconstant +asc-space+ #\Space)
(prin1 (type-of #\Newline  )) (terpri)
(prin1 (type-of #\Space    )) (terpri)
(prin1 (type-of +asc-lf+   )) (terpri)
(prin1 (type-of +asc-space+)) (terpri)

正如预期的那样,它产生:

STANDARD-CHAR
STANDARD-CHAR
STANDARD-CHAR
STANDARD-CHAR

现在考虑这段代码:

(defun st (the-string)
  (string-trim '(#\Newline #\Space) the-string))
(princ "\"")
(princ (st "   abcdefgh   "))
(princ "\"")
(terpri)

它产生:

"abcdefgh"

但请考虑以下代码:

(defconstant +asc-lf+    #\Newline)
(defconstant +asc-space+ #\Space)
(defun st (the-string)
  (string-trim '(+asc-lf+ +asc-space+) the-string))
(princ "\"")
(princ (st "   abcdefgh   "))
(princ "\"")
(terpri)

当您使用 sbcl 加载它时,它会为您提供:

While evaluating the form starting at line 6, column 0
  of #P"/u/home/sbcl/experiments/type-conflict.d/2.lisp":"

debugger invoked on a TYPE-ERROR:
  The value
    +ASC-LF+
  is not of type
    CHARACTER

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [RETRY   ] Retry EVAL of current toplevel form.
  1: [CONTINUE] Ignore error and continue loading file "/u/home/sbcl/experiments/type-conflict.d/2.lisp".
  2: [ABORT   ] Abort loading file "/u/home/sbcl/experiments/type-conflict.d/2.lisp".
  3:            Exit debugger, returning to top level.

((FLET SB-IMPL::TRIM-CHAR-P :IN SB-IMPL::GENERIC-STRING-TRIM) #\ )
0] 

起初,我期待能够报告 clisp 对 #'string-trim 进行了适当的调用,具有预期的返回值,或者可能会出错。但这两者都不做。函数 returns 传递给它的相同字符串,没有任何修剪。

这是应该发生的事情吗?我错过了什么?

编辑大约。 2017-10-21 08:50 协调世界时

PuercoPop 的精彩回答激发了后续问题。如果我应该 post 这作为一个单独的问题,只要说出我会的。

为什么(至少对于 sbcl 和 clisp)是这样的:

(defconstant +asc-lf+    #\Newline)
(defconstant +asc-space+ #\Space)
(prin1 (type-of (first (list #\Newline #\Space))))
(terpri)
(prin1 (type-of (first '(#\Newline #\Space))))
(terpri)

产生这个?

STANDARD-CHAR
STANDARD-CHAR

有了 PuercoPop 的回答,我本以为它会为第二个表达式产生一些关于符号而不是字符的东西。

问题是您引用了 "character list"。因此,它不是字符列表,而是符号列表。即

(defun st (the-string)
  (string-trim (list +asc-lf+ +asc-space+) the-string))

错误消息提示

The value +ASC-LF+ is not of type CHARACTER

而不是

The value #\Newline is not of type CHARACTER

主要的困惑来自

  1. 列表的双重用途:数据和代码。评估 (+ a b) 是代码,这里是一个函数调用。 (quote (+ a b))'(+ a b) 都是数据,因为它们对引用的文字数据求值。
  2. 阅读已经创建了对象。 #\newline 已被读取为字符对象。它是内置语法:Sharpsign Backslash It is not a string, not a symbol and not some yet unknown piece of data. It is read as an object of type character (I use the wording character object for that here, one could also just say character).

这些是符号:

foo
bar
+foo+
*the-foo*

当符号被求值时,它们求值到它们的值。

这些是角色对象:

#\f
#\O
#\o
#\newline

当角色对象被求值时,它们会自己求值。 因此 '#\foo(quote #\foo)#\foo 对同一个对象求值。

这些是列表

(newline #\newline)   ; the first item is a symbol, the second a character object
(#\a #\b #\c)         ; a list of character objects
(a b c)               ; a list of symbols

如果我们评估列表会发生什么:

(+ a b)               ; the sum of the values of A and B

(list a b)            ; a list gets computed, with the values of variables a and b
(list 'a 'b)          ; a list gets computed, with the symbols A and B

'(a b)                ; a literal list of the symbols A and B
'(#\a #\b)            ; a literal list of the character objects #\a and #\b
'(a #\a)              ; a literal list of the symbol A and the character object #\a

(#\a #\b)            ; an error, #\a is not a function/macro/special-form
(+ a 'b)             ; an error, a symbol B is not a number 

评估反引号列表:

`(a ,a #\a ,#\a)      ; a list of the symbol a, the value of the variable a,
                      ; the character object a and again the character object a

你的错误:

'(+asc-lf+ +asc-space+) 计算出一个符号列表。

函数 STRING-TRIM 需要一个字符序列。

你需要这样写:

(list +asc-lf+ +asc-space+)   ; calling the function list
`(,+asc-lf+ ,+asc-space+)     ; a backquoted list with comma for evaluation
(vector +asc-lf+ +asc-space+) ; the constructed vector is also a sequence

还有:

(list #\Newline #\Space)'(#\Newline #\Space) 都计算为字符列表。 #\ 语法是 Lisp reader 的内置功能,用于构造字符对象。因此 #\newline 在读取时被转换为字符对象:

CL-USER 82 > (describe (read))
#\Newline                           ; we type the nine characters #\Newline
#\Newline is a CHARACTER
Name      "Newline"
Code      10