Initial commit
This commit is contained in:
commit
0c053e8624
|
@ -0,0 +1,57 @@
|
|||
(define-library (floyd)
|
||||
(import (scheme base)
|
||||
(list-error))
|
||||
(export floyd-generator)
|
||||
(begin
|
||||
|
||||
(define (floyd-generator lists)
|
||||
;; fast == null or pair [tail] or boolean [circular?]
|
||||
(let* ((lists (list->vector lists))
|
||||
(slows (vector-copy lists))
|
||||
(fasts (vector-copy lists))
|
||||
(first-round? #t))
|
||||
(let loop ((n (vector-length lists)))
|
||||
(unless (zero? n)
|
||||
(let* ((i (- n 1))
|
||||
(list (vector-ref lists i)))
|
||||
(if (or (null? list)
|
||||
(pair? list))
|
||||
(loop i)
|
||||
(error (make-list-error list list))))))
|
||||
(lambda ()
|
||||
(let loop ((n (vector-length lists))
|
||||
(items '())
|
||||
(all-circular? #t))
|
||||
(if (zero? n)
|
||||
(cond (all-circular?
|
||||
(eof-object))
|
||||
(else
|
||||
(set! first-round? #f)
|
||||
items))
|
||||
(let* ((i (- n 1))
|
||||
(list (vector-ref lists i))
|
||||
(slow (vector-ref slows i))
|
||||
(fast (vector-ref fasts i)))
|
||||
(cond ((null? slow)
|
||||
(eof-object))
|
||||
((not (pair? slow))
|
||||
(error (make-list-error list slow)))
|
||||
(else
|
||||
(unless (boolean? fast)
|
||||
(set! fast
|
||||
(cond ((and (eq? slow fast)
|
||||
(not first-round?))
|
||||
#t)
|
||||
((not (pair? fast))
|
||||
#f)
|
||||
((not (pair? (cdr fast)))
|
||||
#f)
|
||||
((not (pair? (cdr (cdr fast))))
|
||||
#f)
|
||||
(else
|
||||
(cdr (cdr fast))))))
|
||||
(vector-set! fasts i fast)
|
||||
(vector-set! slows i (cdr slow))
|
||||
(loop i
|
||||
(cons (car slow) items)
|
||||
(and all-circular? (eqv? fast #t)))))))))))))
|
|
@ -0,0 +1,35 @@
|
|||
(define-library (list-error)
|
||||
(import (scheme base))
|
||||
(export make-list-error
|
||||
list-error-list
|
||||
list-error-tail
|
||||
list-error-message
|
||||
list-error?
|
||||
circular-list-error?
|
||||
dotted-list-error?)
|
||||
(begin
|
||||
|
||||
(define-record-type <list-error>
|
||||
(make-list-error list tail)
|
||||
list-error?
|
||||
(list list-error-list)
|
||||
(tail list-error-tail))
|
||||
|
||||
(define (list-error-message err)
|
||||
(let ((tail (list-error-tail obj)))
|
||||
(cond ((null? tail)
|
||||
"Proper list")
|
||||
((pair? tail)
|
||||
"Circular list")
|
||||
((eq? tail (list-error-list obj))
|
||||
"Not a list")
|
||||
(else
|
||||
"Dotted list"))))
|
||||
|
||||
(define (circular-list-error? obj)
|
||||
(and (list-error? obj)
|
||||
(pair? (list-error-tail obj))))
|
||||
|
||||
(define (dotted-list-error? obj)
|
||||
(and (list-error? obj)
|
||||
(not (null-or-pair? (list-error-tail obj)))))))
|
|
@ -0,0 +1,63 @@
|
|||
# SR 2022-x: Floyd library
|
||||
|
||||
## Author
|
||||
|
||||
Lassi Kortela
|
||||
|
||||
## Status
|
||||
|
||||
Draft
|
||||
|
||||
## Abstract
|
||||
|
||||
Floyd's "tortoise and hare" is a simple, space-efficient algorithm to
|
||||
detect a circular linked list. This is an exploration of how to
|
||||
package Floyd as a convenient abstraction and how to use it to
|
||||
implement well-known list procedures.
|
||||
|
||||
## Specification
|
||||
|
||||
`(floyd-generator lists) -> procedure`
|
||||
|
||||
Return a generator (in the sense of SRFI 158). Successive calls to the
|
||||
generator yield lists of adjacent elements from _lists_. All _lists_
|
||||
are traversed in order.
|
||||
|
||||
For example, calls to the generator returned by `(floyd-generator '((1
|
||||
2 3) (a b c)))` yield `(1 a)` then `(2 b)` then `(3 c)`. An exhausted
|
||||
generator returns an end-of-file object.
|
||||
|
||||
If no _lists_ are given, the generator is exhausted immediately.
|
||||
|
||||
If one or more non-circular _lists_ are given, the generator stops at
|
||||
the end of the shortest. If both circular and non-circular lists are
|
||||
given, the circular lists are cycled until the end of the shortest
|
||||
non-circular list is reached.
|
||||
|
||||
The generator is also exhausted if and when it notices that all
|
||||
_lists_ are circular.
|
||||
|
||||
It is acceptable for _lists_ to contain the same list more than once.
|
||||
|
||||
It is acceptable for any list in _lists_ to be a tail of another list
|
||||
in _lists_.
|
||||
|
||||
It is acceptable for the caller to mutate any list in _lists_ that the
|
||||
generator will not visit again. Once a given list element has been
|
||||
returned by the generator, the cdr of the pair containing that element
|
||||
may be mutated; the generator remembers the old cdr.
|
||||
|
||||
## Examples
|
||||
|
||||
```
|
||||
> (generator->list (floyd-generator '((1 2 3 4) '(a b c d) '(x y z))))
|
||||
((1 a x)
|
||||
(2 b y)
|
||||
(3 c z))
|
||||
|
||||
> (generator->list (floyd-generator `((1 2 3 4) ,(circular-list 'odd 'even))))
|
||||
((1 odd)
|
||||
(2 even)
|
||||
(3 odd)
|
||||
(4 even))
|
||||
```
|
|
@ -0,0 +1,40 @@
|
|||
(import (scheme base)
|
||||
(scheme write)
|
||||
(list-error)
|
||||
(floyd))
|
||||
|
||||
(define (circular-list first . rest)
|
||||
(let ((head (list first)))
|
||||
(let loop ((tail head) (rest rest))
|
||||
(cond ((null? rest)
|
||||
(set-cdr! tail head)
|
||||
head)
|
||||
(else
|
||||
(set-cdr! tail (list (car rest)))
|
||||
(loop (cdr tail) (cdr rest)))))))
|
||||
|
||||
(define (generator->list g)
|
||||
(let loop ((xs '()))
|
||||
(let ((x (g)))
|
||||
(if (eof-object? x)
|
||||
(reverse xs)
|
||||
(loop (cons x xs))))))
|
||||
|
||||
(define-syntax show
|
||||
(syntax-rules ()
|
||||
((show expr)
|
||||
(begin (write 'expr)
|
||||
(write-string " => ")
|
||||
(write expr)
|
||||
(newline)))))
|
||||
|
||||
(show (generator->list (floyd-generator '((1 2 3 4) (a b c d) (x y z)))))
|
||||
|
||||
(show (generator->list (floyd-generator '((1 2 3 4) (a b c d) (x y z å)))))
|
||||
|
||||
(show (generator->list (floyd-generator '((1 2 3 4) (a b c d) ()))))
|
||||
|
||||
(show (generator->list (floyd-generator (list '(1 2 3 4)
|
||||
(circular-list 'a 'b 'c)))))
|
||||
(show (generator->list (floyd-generator (list (circular-list 1 2 3 4 5 6)
|
||||
(circular-list 'a 'b)))))
|
Loading…
Reference in New Issue