Skip to content

Commit 0c884df

Browse files
committed
Replace list-equal? with a cycle-aware predicate
This is the final part of issue #1
1 parent 8796264 commit 0c884df

File tree

1 file changed

+30
-5
lines changed

1 file changed

+30
-5
lines changed

scheme-core.lisp

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,36 @@ rules provided in the r7rs-small specification.
7474
(character (typecase y (character (char= x y))))
7575
(t (eq x y))))
7676

77-
;;; TODO: Must always terminate even if the list is circular.
78-
(defun list-equal? (x y)
79-
(or (and (null x) (eql x y))
80-
(and (equal? (car x) (car y))
81-
(list-equal? (cdr x) (cdr y)))))
77+
;;; TODO: If circular and equal?, then this iterates too much because
78+
;;; it goes to the first detected part of the cycle rather than to the
79+
;;; start of it. It can't just stop at the detection of the cycle
80+
;;; because of e.g. '(-1 0 . #1=(1 2 3 4 5 6 7 8 9 . #1#))
81+
(define-function list-equal? ((list1 list) (list2 list))
82+
;; Note: Tested in a more verbose way so that the list lengths match
83+
;; in the ALWAYS test and so lists with cycles always terminate.
84+
(loop :with end? := nil
85+
:with cycle-x := nil
86+
:with cycle-y := nil
87+
:for x := list1 :then (cdr x)
88+
:for y := list2 :then (cdr y)
89+
;; For cycle testing to ensure termination
90+
:for x-fast := list1 :then (cddr x-fast)
91+
:for y-fast := list2 :then (cddr y-fast)
92+
:for i :from 0
93+
:until end?
94+
;; Recursive equality test
95+
:always (or (and (endp x) (endp y))
96+
(equal? (car x) (car y)))
97+
:do
98+
;; End test
99+
(when (or (endp x) (endp y) (eq x cycle-x) (eq y cycle-y))
100+
(setf end? t))
101+
;; Cycle tests
102+
(when (plusp i)
103+
(when (and x-fast (not cycle-x) (eq x x-fast))
104+
(setf cycle-x x))
105+
(when (and y-fast (not cycle-y) (eq y y-fast))
106+
(setf cycle-y y)))))
82107

83108
(defun vector-equal? (x y)
84109
(and (typep y (type-of x))

0 commit comments

Comments
 (0)