Abstract the (duplicate?) behavior between "sort" and "replace" I have written these two functions that have a similar process.  The first is meant to "split" a string on a given character and the second is meant to "replace-all" instances of a character in a given string.  The behavior between the two seems very similar - can you think of a good way to abstract that behavior into a third, reusable, function?  (which could be executed by calling, for example, (where-found sought-letter searchee-string thing-to-do)).

(defun split (separator splittee)
  (let ((next-index (position separator splittee)))
    (if next-index
      (cons (subseq splittee 0 next-index) (split separator (subseq splittee (1+ next-index))))
      (list splittee))))

(defun replace-all (replacand replacee replacor)
  (let ((next-index (position replacand replacee)))
    (if next-index
      (concatenate 'string (subseq replacee 0 next-index) replacor (replace-all replacand (subseq replacee (1+ next-index)) replacor))
      replacee)))


What do you think?  Is this better than the original code, or is it too complicated?

(defun where-found (sought-letter the-string to-do-onroute to-do-on-leaf)
  (let ((next-index (position sought-letter the-string)))
    (if next-index
      (apply to-do-onroute
         (list (subseq the-string 0 next-index) 
           (subseq the-string (1+ next-index))))
      (apply to-do-on-leaf (list the-string))))) 

(defun split (separator splittee)
  (where-found separator splittee 
           (lambda (pre-string post-string) (cons pre-string (split separator post-string))) 
           (lambda (leaf-string) (list leaf-string))))

(defun replace-all (to-be-replaced-string the-patient-string the-replacement-string)
  (where-found to-be-replaced-string the-patient-string 
           (lambda (pre-string post-string) 
         (format t "pre: ~a post: ~a the-replacement-string: ~a ~%" pre-string post-string the-replacement-string) 
         (concatenate 'string pre-string the-replacement-string (replace-all to-be-replaced-string post-string the-replacement-string)))
           (lambda (leaf-string) leaf-string)))

(format t "the cat is here: ~a ~%" (replace-all #\Space "the cat is here" ""))
(format t "the cat is here: ~a ~%" (split #\Space "the cat is here"))
I think the later versions are unnecessarily complicated and verbose.

My first idea was to just concatenate the splitted list parts in replace-all – for you can do (concatenate 'string seqs ... ) – but that may not be very efficient.

You can use map or loop for replace-all. This map version works for lists too:


(defun replace-all (seq from to)
  (map (type-of seq) (lambda (char) (if (equal from char) to char)) seq))


Naming things clearly can sometimes be hard, and I think sometimes being terse helps readability more than being too-explicit-and-specific. That if statement above could need some better naming of things...

There is an utility library called split-sequence, that has many useful options, like


remove empty subsequences
only split count times
return the index position (useful for further processing)


I'm still learning Lisp, so you may want to hear see other answers also.