For vectors, this will be pretty fast - one of the promises vectors is the quick creation of slightly modified copies.
For lists, this will not work at all - they are not associative ( clojure.lang.Associative ) and therefore are not valid arguments for assoc . As for the other possible approaches, if you need to get the actual list back (unlike seq (maybe)), you are out of luck - accessing / replacing the end element of the list is basically a linear time operation. If, on the other hand, you are fine with a lazy seq that looks like your list, with the exception of the final element, you can implement the semi-lazy version of butlast (the one in clojure.core not lazy at all) and use this with using lazy-cat :
(defn semi-lazy-butlast [s] (cond (empty? s) s (empty? (next s)) nil ; or perhaps () :else (lazy-seq (cons (first s) (semi-lazy-butlast (next s)))))) (defn replace-last [sx] (if (empty? s) ; do nothing if there is no last element to replace s (lazy-cat (semi-lazy-butlast s) [x])))
This will cancel the end element replacement until you get close to it consuming your seq.
Interaction with the sample:
user=> (take 10 (replace-last (range) :foo)) (0 1 2 3 4 5 6 7 8 9) user=> (take 10 (replace-last (range 10) :foo)) (0 1 2 3 4 5 6 7 8 :foo) user=> (replace-last () :foo) ()
source share