SIMPLE-READER-ERROR ,非法尖锐宏字符,子字符 #\< 没有为 dispatch char #\# 定义
SIMPLE-READER-ERROR , illegal sharp macro character, Subcharacter #\< not defined for dispatch char #\#
在 David B. Lamkins 的成功的 Lisp 一书的第 4 章中,
有一个简单的应用程序可以跟踪银行支票。
https://dept-info.labri.fr/~strandh/Teaching/MTP/Common/David-Lamkins/chapter04.html
最后,我们写了一个保存和恢复函数的宏。
当我执行 reader 函数并且要读取的值是散列 table.
时出现问题
保存和恢复功能的宏是:
(defmacro def-i/o (writer-name reader-name (&rest vars))
(let ((file-name (gensym))
(var (gensym))
(stream (gensym)))
`(progn
(defun ,writer-name (,file-name)
(with-open-file (,stream ,file-name
:direction :output :if-exists :supersede)
(dolist (,var (list ,@vars))
(declare (special ,@vars))
(print ,var ,stream))))
(defun ,reader-name (,file-name)
(with-open-file (,stream ,file-name
:direction :input :if-does-not-exist :error)
(dolist (,var ',vars)
(set ,var (read ,stream)))))
t)))
这是我的哈希 table 以及发生了什么:
(defvar *payees* (make-hash-table :test #'equal))
(check-check 100.00 "Acme" "Rocket booster T-1000")
CL-USER> *payees*
#<HASH-TABLE :TEST EQUAL :COUNT 0 {25520F91}>
CL-USER> (check-check 100.00 "Acme" "T-1000 rocket booster")
#S(CHECK
:NUMBER 100
:DATE "2020-4-1"
:AMOUNT 100.0
:PAID "Acme"
:MEMO "T-1000 rocket booster")
CL-USER> (def-i/o save-checks charge-checks (*payees*))
T
CL-USER> (save-checks "/home/checks.dat")
NIL
CL-USER> (makunbound '*payees*)
*PAYEES*
CL-USER> (load-checks "/home/checks.dat")
; Evaluation aborted on #<SB-INT:SIMPLE-READER-ERROR "illegal sharp macro character: ~S" {258A8541}>.
在 Lispworks 中,错误信息是:
Error: subcharacter #\< not defined for dispatch char #\#.
为简化起见,如果我执行,我会得到同样的错误:
CL-USER> (defvar *payees* (make-hash-table :test #'equal))
*PAYEES*
CL-USER> (with-open-file (in "/home/checks.dat"
:direction :input)
(set *payees* (read in)))
; Evaluation aborted on #<SB-INT:SIMPLE-READER-ERROR "illegal sharp macro character: ~S" {23E83B91}>.
有人可以向我解释问题出在哪里,以及我需要在我的代码中修复什么才能使其正常工作。
预先感谢您给我的解释和帮助。
无法回读的值用 #<
打印,参见 Sharpsign Less-Than-Sign。 Common Lisp 没有定义 hash-tables 应该如何以可读的方式打印(hash tables 没有 reader 语法):
* (make-hash-table)
#<HASH-TABLE :TEST EQL :COUNT 0 {1006556E53}>
书中的例子只是suitable与之前展示的bank例子一起使用,并不是通用的序列化机制。
有很多方法可以打印散列-table 并读回它们,具体取决于您的需要,但没有默认表示。有关存储任意对象的库,请参阅 cl-store。
自定义dump
函数
让我们编写一个计算等效散列的表单-table;让我们定义一个名为 dump
的通用函数,以及一个简单地 return 原样 return 对象的默认方法:
(defgeneric dump (object)
(:method (object) object))
给定一个散列-table,我们可以将其序列化为 plist(列表中 key/value 个元素的序列),同时调用 dump
在键和值上,以防我们的 hash-tables 包含 hash-tables:
(defun hash-table-plist-dump (hash &aux plist)
(with-hash-table-iterator (next hash)
(loop
(multiple-value-bind (some key value) (next)
(unless some
(return plist))
(push (dump value) plist)
(push (dump key) plist)))))
我们也可以使用 alexandria 系统中的 hash-table-plist
,而不是重新发明轮子。
(ql:quickload :alexandria)
以上等同于:
(defun hash-table-plist-dump (hash)
(mapcar #'dump (alexandria:hash-table-plist hash)))
然后,我们可以将 dump
特化为 hash-tables:
(defmethod dump ((hash hash-table))
(loop
for (initarg accessor)
in '((:test hash-table-test)
(:size hash-table-size)
(:rehash-size hash-table-rehash-size)
(:rehash-threshold hash-table-rehash-threshold))
collect initarg into args
collect `(quote ,(funcall accessor hash)) into args
finally (return
(alexandria:with-gensyms (h k v)
`(loop
:with ,h := (make-hash-table ,@args)
:for (,k ,v)
:on (list ,@(hash-table-plist-dump hash))
:by #'cddr
:do (setf (gethash ,k ,h) ,v)
:finally (return ,h))))))
循环的第一部分计算所有散列-table 属性,例如要使用的散列函数的种类或重新散列大小,并构建 args
的参数列表使用相同的值调用 make-hash-table
。
在 finally
子句中,我们构建了一个 loop
表达式(见反引号),它首先分配散列 - table,然后根据散列的当前值填充它,最后 return 新哈希。请注意,生成的代码不依赖于 alexandria,它可以从另一个没有此依赖性的 Lisp 系统中读回。
例子
CL-USER> (alexandria:plist-hash-table '("abc" 0 "def" 1 "ghi" 2 "jkl" 3)
:test #'equal)
#<HASH-TABLE :TEST EQUAL :COUNT 4 {100C91F8C3}>
转储它:
CL-USER> (dump *)
(LOOP :WITH #:H603 := (MAKE-HASH-TABLE :TEST 'EQUAL :SIZE '14 :REHASH-SIZE '1.5
:REHASH-THRESHOLD '1.0)
:FOR (#:K604 #:V605) :ON (LIST "jkl" 3 "ghi" 2 "def" 1 "abc"
0) :BY #'CDDR
:DO (SETF (GETHASH #:K604 #:H603) #:V605)
:FINALLY (RETURN #:H603))
生成的数据也是有效的Lisp代码,对其进行评估:
CL-USER> (eval *)
#<HASH-TABLE :TEST EQUAL :COUNT 4 {100CD5CE93}>
生成的哈希值 equalp
到原始哈希值:
CL-USER> (equalp * ***)
T
在 David B. Lamkins 的成功的 Lisp 一书的第 4 章中, 有一个简单的应用程序可以跟踪银行支票。
https://dept-info.labri.fr/~strandh/Teaching/MTP/Common/David-Lamkins/chapter04.html
最后,我们写了一个保存和恢复函数的宏。 当我执行 reader 函数并且要读取的值是散列 table.
时出现问题保存和恢复功能的宏是:
(defmacro def-i/o (writer-name reader-name (&rest vars))
(let ((file-name (gensym))
(var (gensym))
(stream (gensym)))
`(progn
(defun ,writer-name (,file-name)
(with-open-file (,stream ,file-name
:direction :output :if-exists :supersede)
(dolist (,var (list ,@vars))
(declare (special ,@vars))
(print ,var ,stream))))
(defun ,reader-name (,file-name)
(with-open-file (,stream ,file-name
:direction :input :if-does-not-exist :error)
(dolist (,var ',vars)
(set ,var (read ,stream)))))
t)))
这是我的哈希 table 以及发生了什么:
(defvar *payees* (make-hash-table :test #'equal))
(check-check 100.00 "Acme" "Rocket booster T-1000")
CL-USER> *payees*
#<HASH-TABLE :TEST EQUAL :COUNT 0 {25520F91}>
CL-USER> (check-check 100.00 "Acme" "T-1000 rocket booster")
#S(CHECK
:NUMBER 100
:DATE "2020-4-1"
:AMOUNT 100.0
:PAID "Acme"
:MEMO "T-1000 rocket booster")
CL-USER> (def-i/o save-checks charge-checks (*payees*))
T
CL-USER> (save-checks "/home/checks.dat")
NIL
CL-USER> (makunbound '*payees*)
*PAYEES*
CL-USER> (load-checks "/home/checks.dat")
; Evaluation aborted on #<SB-INT:SIMPLE-READER-ERROR "illegal sharp macro character: ~S" {258A8541}>.
在 Lispworks 中,错误信息是:
Error: subcharacter #\< not defined for dispatch char #\#.
为简化起见,如果我执行,我会得到同样的错误:
CL-USER> (defvar *payees* (make-hash-table :test #'equal))
*PAYEES*
CL-USER> (with-open-file (in "/home/checks.dat"
:direction :input)
(set *payees* (read in)))
; Evaluation aborted on #<SB-INT:SIMPLE-READER-ERROR "illegal sharp macro character: ~S" {23E83B91}>.
有人可以向我解释问题出在哪里,以及我需要在我的代码中修复什么才能使其正常工作。 预先感谢您给我的解释和帮助。
无法回读的值用 #<
打印,参见 Sharpsign Less-Than-Sign。 Common Lisp 没有定义 hash-tables 应该如何以可读的方式打印(hash tables 没有 reader 语法):
* (make-hash-table)
#<HASH-TABLE :TEST EQL :COUNT 0 {1006556E53}>
书中的例子只是suitable与之前展示的bank例子一起使用,并不是通用的序列化机制。
有很多方法可以打印散列-table 并读回它们,具体取决于您的需要,但没有默认表示。有关存储任意对象的库,请参阅 cl-store。
自定义dump
函数
让我们编写一个计算等效散列的表单-table;让我们定义一个名为 dump
的通用函数,以及一个简单地 return 原样 return 对象的默认方法:
(defgeneric dump (object)
(:method (object) object))
给定一个散列-table,我们可以将其序列化为 plist(列表中 key/value 个元素的序列),同时调用 dump
在键和值上,以防我们的 hash-tables 包含 hash-tables:
(defun hash-table-plist-dump (hash &aux plist)
(with-hash-table-iterator (next hash)
(loop
(multiple-value-bind (some key value) (next)
(unless some
(return plist))
(push (dump value) plist)
(push (dump key) plist)))))
我们也可以使用 alexandria 系统中的 hash-table-plist
,而不是重新发明轮子。
(ql:quickload :alexandria)
以上等同于:
(defun hash-table-plist-dump (hash)
(mapcar #'dump (alexandria:hash-table-plist hash)))
然后,我们可以将 dump
特化为 hash-tables:
(defmethod dump ((hash hash-table))
(loop
for (initarg accessor)
in '((:test hash-table-test)
(:size hash-table-size)
(:rehash-size hash-table-rehash-size)
(:rehash-threshold hash-table-rehash-threshold))
collect initarg into args
collect `(quote ,(funcall accessor hash)) into args
finally (return
(alexandria:with-gensyms (h k v)
`(loop
:with ,h := (make-hash-table ,@args)
:for (,k ,v)
:on (list ,@(hash-table-plist-dump hash))
:by #'cddr
:do (setf (gethash ,k ,h) ,v)
:finally (return ,h))))))
循环的第一部分计算所有散列-table 属性,例如要使用的散列函数的种类或重新散列大小,并构建 args
的参数列表使用相同的值调用 make-hash-table
。
在 finally
子句中,我们构建了一个 loop
表达式(见反引号),它首先分配散列 - table,然后根据散列的当前值填充它,最后 return 新哈希。请注意,生成的代码不依赖于 alexandria,它可以从另一个没有此依赖性的 Lisp 系统中读回。
例子
CL-USER> (alexandria:plist-hash-table '("abc" 0 "def" 1 "ghi" 2 "jkl" 3)
:test #'equal)
#<HASH-TABLE :TEST EQUAL :COUNT 4 {100C91F8C3}>
转储它:
CL-USER> (dump *)
(LOOP :WITH #:H603 := (MAKE-HASH-TABLE :TEST 'EQUAL :SIZE '14 :REHASH-SIZE '1.5
:REHASH-THRESHOLD '1.0)
:FOR (#:K604 #:V605) :ON (LIST "jkl" 3 "ghi" 2 "def" 1 "abc"
0) :BY #'CDDR
:DO (SETF (GETHASH #:K604 #:H603) #:V605)
:FINALLY (RETURN #:H603))
生成的数据也是有效的Lisp代码,对其进行评估:
CL-USER> (eval *)
#<HASH-TABLE :TEST EQUAL :COUNT 4 {100CD5CE93}>
生成的哈希值 equalp
到原始哈希值:
CL-USER> (equalp * ***)
T