【问题标题】:SIMPLE-READER-ERROR , illegal sharp macro character, Subcharacter #\< not defined for dispatch char #\#SIMPLE-READER-ERROR ,非法锐宏字符,子字符 #\< 未定义调度字符 #\#
【发布时间】:2020-07-13 13:49:39
【问题描述】:

在 David B. Lamkins 所著的《成功的 Lisp》一书的第 4 章中, 有一个简单的应用程序可以跟踪银行支票。

https://dept-info.labri.fr/~strandh/Teaching/MTP/Common/David-Lamkins/chapter04.html

最后,我们编写了一个宏来保存和恢复函数。 当我执行 reader 函数并且要读取的值是哈希表时,就会出现问题。

保存和恢复函数的宏是:

(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)))

这是我的哈希表以及发生了什么:

    (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}>.

谁能向我解释问题出在哪里,以及我需要在我的代码中修复什么才能使其正常工作。 预先感谢您可以给我的解释和您可以给我的帮助。

【问题讨论】:

  • 使用函数SET?

标签: macros lisp common-lisp hashtable dispatch


【解决方案1】:

无法回读的值使用#&lt; 打印,请参阅Sharpsign Less-Than-Sign。 Common Lisp 没有定义如何以可读的方式打印哈希表(哈希表没有阅读器语法):

* (make-hash-table)
#<HASH-TABLE :TEST EQL :COUNT 0 {1006556E53}>

书中的例子只适合与前面展示的bank例子一起使用,并不是一个通用的序列化机制。

根据您的需要,有多种方法可以打印哈希表并将其读回,但没有默认表示。有关存储任意对象的库,请参阅 cl-store

自定义dump函数

让我们编写一个计算结果为等效哈希表的表单;让我们定义一个名为 dump 的通用函数,以及一个简单地按原样返回对象的默认方法:

(defgeneric dump (object)
  (:method (object) object))

给定一个哈希表,我们可以将其序列化为 plist(列表中键/值元素的序列),同时在键和值上调用 dump,以防我们的哈希-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 专门用于哈希表:

(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))))))

循环的第一部分计算所有哈希表属性,例如要使用的哈希函数类型或重新哈希大小,并构建 args,调用 make-hash-table 的参数列表具有相同的价值观。

finally 子句中,我们构建了一个loop 表达式(见反引号),它首先分配哈希表,然后根据哈希的当前值填充它,最后返回新的哈希。请注意,生成的代码不依赖于 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-06-30
    • 2012-09-14
    • 2019-03-29
    • 2012-07-20
    • 2011-09-09
    • 2011-09-09
    • 2013-09-05
    • 2021-07-21
    相关资源
    最近更新 更多