GNU CLISP 中的堆栈溢出(而不是 SBCL 中的)

Stack overflow in GNU CLISP (& not in SBCL)

我在使用以下代码时出现堆栈溢出,然后我在 SBCL 中尝试了它并且成功了。想知道是什么导致了那里的差异。

具体来说:虽然我确实计划在某个时候转到 SBCL,但可以 这可以在 CLISP 中工作吗?

(defvar *objs* nil)                                 ; [1]

(defun parents (obj) (gethash :parents obj))

(defun obj (&rest parents)                          ; [2]
  (let ((obj (make-hash-table)))
    (push obj *objs*)
    (setf (parents obj) parents)
    obj))

(defun (setf parents) (val obj)                     ; [3]
  (prog1 (setf (gethash :parents obj) val)
         (make-precedence obj)))

(defun make-precedence (obj)                        ; [4]
  (setf (gethash :preclist obj) (precedence obj))
  (dolist (x *objs*)
    (if (member obj (gethash :preclist x))
        (setf (gethash :preclist x) (precedence x)))))

(defun precedence (obj)                             ; [5]
  (delete-duplicates (traverse obj)))

(defun traverse (x)                                 ; [6]
  (cons x (mapcan #'traverse (gethash :parents x))))

;; [1] We'll store a list of objects we create in *obj*.
;; [2] Function to create an object, called like (setf scoundrel (obj)).
;; [3] Set an objects (multiple) parents & rebuild precedence list for all affected objs.
;; [4] Rebuild precedence list for obj, then for all affected objs.
;; [5] Returns a list of object and all its ancestors in precedence order as we define it.
;;     (Can read it like (-> obj traverse delete-duplicates) if it helps)
;; [6] Cons an object to all its parents recursively; depth first search.
;;     I pulled this out of labels in precedence above it for clarity & testability.
;; Source: PG's ANSI Common Lisp, Chapter 17, "Example: Objects".

示例 - SBCL

(setf scoundrel (obj))
; #<HASH-TABLE :TEST EQL :COUNT 2 {1001A01893}>
(setf sc2 (obj scoundrel))
; #<HASH-TABLE :TEST EQL :COUNT 2 {1001A1F153}>
*objs*
; (#<HASH-TABLE :TEST EQL :COUNT 2 {1001A1F153}>
;  #<HASH-TABLE :TEST EQL :COUNT 2 {1001A01893}>)
(parents scoundrel)
; NIL
; T
(parents sc2)
; (#<HASH-TABLE :TEST EQL :COUNT 2 {1001A01893}>)
; T

示例 - GNU CLISP

(setf scoundrel (obj))
;; - Lisp stack overflow. RESET
*objs*
;; - Lisp stack overflow. RESET

可能值得一提的是,我还没有深入研究 lisp 的双重 interpreted and compiled 特性。到目前为止,我只是将它用作一种解释性语言;通过将上述函数粘贴到 clisp repl 中。

所以我怀疑编译所有这些函数可能是需要考虑的一件事。我注意到我们可以 compilecompile-file,但我看不到编译所有用户定义函数的运算符。

GNU CLISP 默认打印散列 table 的内容。在你的情况下,它包含圆形结构。

  • 要么将 *PRINT-CIRCLE* 设置为 T,以启用打印循环结构而不会出现堆栈溢出。
> (setq *print-circle* t)
T
> *objs*
(#1=#S(HASH-TABLE :TEST FASTHASH-EQL (:PRECLIST . (#1#)) (:PARENTS . NIL)))
  • 或将 *PRINT-ARRAY**PRINT-READABLY* 设置为 NIL,以禁用打印散列 table.
  • 的内容
> (setq *print-circle* nil *print-array* nil *print-readably* nil)
NIL
> *objs*
(#<HASH-TABLE :TEST FASTHASH-EQL :COUNT 2 #x000335098D40>)