(define scan-left
(lambda (fn initial elements)
(if (null? elements)
initial
(scan-left fn (fn initial elements) (cdr elements)))))
;;(scan-left (lambda (state current) (display current) (newline)) '() '(A B C))
(define delete-last-occurrence
(lambda (elements token)
;; We're going to scan through the list, keeping a state consisting of
;; - the cons cell *BEFORE* the (currently) last occurrence
;; - the previous cons cell
(define make-state
(lambda (cell-b prev-cell)
(cons cell-b prev-cell)))
(define cell-before
(lambda (state)
(car state)))
(define previous-cell
(lambda (state)
(cdr state)))
;; If the head of the current (sub)list is equal to the token,
;; then remember the previous cons cell as the cell before the
;; (currently) last occurrence.
;; Else just update the previous cell.
(define consume-element
(lambda (state current)
(if (eqv? (car current) token)
(make-state (previous-cell state) current)
(make-state (cell-before state) current))))
;; Handle the easy cases separately.
(cond ((null? elements) '())
((null? (cdr elements))
(if (eqv? (car elements) token) '() elements))
(else
(let ((state
(scan-left consume-element
(make-state '() elements)
(cdr elements))))
(if (pair? (cell-before state))
;; We found an occurrence, and the cell-before is the ... well
;; cell before that last occurrence. Hence we modify it such that
;; its cdr no longer "points" to the cell whose car is the last
;; occurrence but instead "skips" that element.
(let ((cell-to-modify (cell-before state)))
(set-cdr! cell-to-modify
(cdr (cdr (cell-before state))))
elements)
;; No occurrence found. Either there is none, or only the first
;; element is equal to the token.
(if (eqv? (car elements) token)
(cdr elements)
elements)))))))
(display (delete-last-occurrence '() 'A))(newline)
(display (delete-last-occurrence '(A) 'A))(newline)
(display (delete-last-occurrence '(A B) 'A))(newline)
(display (delete-last-occurrence '(A B A) 'A))(newline)
(display (delete-last-occurrence '(A B A B) 'A))(newline)