In my last post, I wrote a long, non-general tail-merging† facility for insertion sort. I thought I should instead write a general-purpose tail-merging function, that works even when the two lists are of different lengths. So here it is (requires SRFI 1):
;;; Returns a list that has the same content as `lst`, sharing tail with `base`.
;;; Element comparisons use `eqv?`.
(define (merge-tail lst base)
;; `uniq1` and `uniq2` are guaranteed not to be `eqv?` to anything else.
;; This ensures that `breakpoint` always has a value, without any hacks.
(define uniq1 (list 'uniq1))
(define uniq2 (list 'uniq2))
(define breakpoint (list-index (lambda (x y) (not (eqv? x y)))
(reverse (cons uniq1 base))
(reverse (cons uniq2 lst))))
(append (drop-right lst breakpoint) (take-right base breakpoint)))
(In Racket, you can replace (list 'uniq1)
with (box 'uniq1)
(and similarly for uniq2
), and (lambda (x y) (not (eqv? x y)))
with (negate eqv?)
.)
Is there a more stylish or performant way to write this code? (Note, I'm trying to not mutate either list. If the lst
list is linear-update, then of course you can use append!
and drop-right!
.)
† The process of merging tails is to ensure that two lists share the longest tail possible. This is especially useful for immutable lists.