;; Creating our zipper abstraction
(define (make-zipper l p r)
"Create a zipper with what's to come left of point,
what's at point and what's right of the point."
(list l p r))
(define (zipper-point z)
"Get the point of the zipper."
(cadr z))
(define (zipper-left z)
"Get what's left of the zipper, in the order as if
the point moved to the left."
(car z))
(define (zipper-right z)
"Get what's right of the zipper."
(caddr z))
;; Conversion into our new data type
(define (zipper-from-list l)
"Create a zipper from a (non empty) list, and
place the point at the first element."
(make-zipper '() (car l) (cdr l)))
;; Movement on zippers.
;; (2 1) 3 (4 5)
;; move-right => (3 2 1) 4 (5)
;; move-left => (1) 2 (3 4 5)
(define (zipper-move-right z)
"Return a zipper with the point moved one to the
right."
(make-zipper
(cons (zipper-point z) (zipper-left z))
(car (zipper-right z))
(cdr (zipper-right z))))
(define (zipper-move-left z)
"Return a zipper with the point moved one to the
left."
(make-zipper
(cdr (zipper-left z))
(car (zipper-left z))
(cons (zipper-point z) (zipper-right z))))
;; A special kind of moving, destructing the value at point.
;; (2 1) 3 (4 5)
;; zipper-squash-left => (1) 2 (4 5)
;; zipper-squash-right => (2 1) 4 (5)
(define (zipper-squash-right z)
"Squash the value at point and close the gap
with a value from right."
(make-zipper
(zipper-left z)
(car (zipper-right z))
(cdr (zipper-right z))))
(define (zipper-squash-left z)
"Squash the value at point and close the gap
with a value from left."
(make-zipper
(cdr (zipper-left z))
(car (zipper-left z))
(zipper-right z)))
;; Testing for the end points of the zipper.
(define (zipper-left-end? z)
"Check whether the zipper is at the left end."
(eq? '() (zipper-left z)))
(define (zipper-right-end? z)
"Check whether the zipper is at the right end."
(eq? '() (zipper-right z)))
;; Pull out a list from the current position of the
;; point.
(define (pull-list-from-zipper z)
"Pull out a list from the current point of the
zipper. The list will have the point as first value, followed
by the one right to it, then the one left of it, then another
one from the right and so on."
(cond
((zipper-left-end? z) (cons (zipper-point z) (zipper-right z)))
((zipper-right-end? z) (cons (zipper-point z) (zipper-left z)))
(else
(let* ((p1 (zipper-point z))
(z1 (zipper-squash-right z))
(p2 (zipper-point z1))
(z2 (zipper-squash-left z1)))
(cons p1 (cons p2 (pull-list-from-zipper z2)))))))
;; Utility function
(define (repeated f n)
"Return a function that repeatedly applies the
given function."
(cond
((= 0 n) (lambda (o) o))
(else (lambda (o) ((repeated f (- n 1)) (f o))))))
;; What we wanted to to.
(define (unwind l)
"Move to the mid and pull a list out of the list."
(let ((steps (quotient (- (length l) 1) 2)))
(pull-list-from-zipper
((repeated zipper-move-right steps) (zipper-from-list l)))))
;; Demos
(display (unwind '(1 2 3 4 5 6)))
(newline)
(display (unwind '(7 8 2 9 5 6)))
(newline)
(newline)
;; Other examples
(display ((repeated zipper-move-right 5) (zipper-from-list '(1 2 3 4 5 6 7 8 9))))
(newline)
(display (pull-list-from-zipper (zipper-move-right
(zipper-move-right (zipper-from-list '(1 2 3 4))))))
(newline)