在lastName='Oracle'
的Common Lisp HyperSpec页面上 - 请参阅
here - 它说明了以下两点:
" unread-char旨在成为允许Lisp阅读器和其他阅读器的有效机制 解析器在输入流中执行单字符前瞻。"
"在同一个流上连续两次调用unread-char是错误的 介入调用read-char(或其他隐式读取字符的输入操作) 在那条小溪上。"
我正在调查如何添加对CL流的多字符前瞻的支持 解析器我打算写,并且为了确认上述内容,我运行了以下代码:
unread-char
它没有抛出错误(在SBCL或CCL上,我还没有在其他实现上测试它)但我不知道怎么可能有任何读取
在连续调用之间的流上发生的操作(隐式或显式)
到(defun unread-char-test (data)
(with-input-from-string (stream data)
(let ((stack nil))
(loop
for c = (read-char stream nil)
while c
do (push c stack))
(loop
for c = (pop stack)
while c
do (unread-char c stream)))
(coerce
(loop
for c = (read-char stream nil)
while c
collect c)
'string)))
(unread-char-test "hello")
==> "hello"
。
这种行为对于多字符前瞻来说是个好消息,只要它是一致的,但为什么 是不是会抛出错误?
答案 0 :(得分:5)
在回应用户jkiiski的评论时,我做了一些挖掘工作。我定义了一个类似于上面的函数但是将流作为参数(为了更容易重用):
(defun unread-char-test (stream)
(let ((stack nil))
(loop
for c = (read-char stream nil)
while c
do (push c stack))
(loop
for c = (pop stack)
while c
do (unread-char c stream)))
(coerce
(loop
for c = (read-char stream nil)
while c
collect c)
'string))
然后我在第二个REPL中运行了以下内容:
(defun create-server (port)
(usocket:with-socket-listener (listener "127.0.0.1" port)
(usocket:with-server-socket (connection (usocket:socket-accept listener))
(let ((stream (usocket:socket-stream connection)))
(print "hello" stream)))))
(create-server 4000)
以下第一个REPL:
(defun create-client (port)
(usocket:with-client-socket (connection stream "127.0.0.1" port)
(unread-char-test stream)))
(create-client 4000)
它确实抛出了我预期的错误:
Two UNREAD-CHARs without intervening READ-CHAR on #<BASIC-TCP-STREAM ISO-8859-1 (SOCKET/4) #x302001813E2D>
[Condition of type SIMPLE-ERROR]
这表明jkiiski的假设是正确的。从文本文件中读取输入时也会观察到原始行为,如下所示:
(with-open-file (stream "test.txt" :direction :output)
(princ "hello" stream))
(with-open-file (stream "test.txt")
(unread-char-test stream)))
==> "hello"
我想,在处理本地文件I / O时,实现会将大块文件读入内存,然后read-char
从缓冲区读取。如果正确的话,这也支持这样的假设:当从内容在内存中的流中读取时,典型实现不会抛出规范中描述的错误。