LISP - Removing elements from List
Lisp - Removing Elements from a List
Section titled “Lisp - Removing Elements from a List”Common Lisp provides a rich set of functions for removing elements from lists. A critical distinction is made between non-destructive functions, which return a new list, and destructive functions, which may modify the original list. Understanding this difference is key to writing safe and predictable code.
Non-Destructive Removal: remove, remove-if, remove-if-not
Section titled “Non-Destructive Removal: remove, remove-if, remove-if-not”Non-destructive functions are generally safer to use as they do not have side effects. They return a new list, leaving the original list untouched.
remove: Removing a Specific Item
Section titled “remove: Removing a Specific Item”The remove function creates a new list with all occurrences of a specific item removed.
(defparameter *my-list* '(a b c b a))
;; Remove all 'b's. The original list is unaffected.(defparameter *new-list* (remove 'b *my-list*))
(format t "Original List: ~s~%" *my-list*)(format t "New List: ~s~%" *new-list*)Output
Section titled “Output”Original List: (A B C B A)New List: (A C A)You can limit the number of removed items with the :count keyword.
;; Remove only the first occurrence of 'b'(print (remove 'b *my-list* :count 1))Output
Section titled “Output”(A C B A)remove-if: Removing Elements that Match a Predicate
Section titled “remove-if: Removing Elements that Match a Predicate”The remove-if function removes elements for which a given predicate (a function that returns true or false) returns true.
(defparameter *numbers* '(1 2 3 4 5 6))
;; Remove all even numbers using the `evenp` predicate.(print (remove-if #'evenp *numbers*))Output
Section titled “Output”(1 3 5)remove-if-not: Removing Elements that Don’t Match a Predicate
Section titled “remove-if-not: Removing Elements that Don’t Match a Predicate”Conversely, remove-if-not removes all elements for which the predicate returns false (i.e., it keeps only the elements that satisfy the predicate).
(defparameter *numbers* '(1 2 3 4 5 6))
;; Keep only the even numbers.(print (remove-if-not #'evenp *numbers*))Output
Section titled “Output”(2 4 6)Advanced Filtering with :key and :test
Section titled “Advanced Filtering with :key and :test”The removal functions can be made even more powerful with the :key and :test keyword arguments.
:key: Specifies a function to be called on each element before it is tested. This is useful for working with complex data structures.:test: Specifies a function to use for comparison instead of the defaulteql.
Example: Using :key
Section titled “Example: Using :key”;; A list of (name score) pairs(defparameter *scores* '((alice 88) (bob 95) (charlie 72)))
;; Remove any entry where the name is 'bob'(print (remove 'bob *scores* :key #'first))Output
Section titled “Output”((ALICE 88) (CHARLIE 72))Example: Using :test
Section titled “Example: Using :test”(defparameter *numbers* '(10 25 5 40 15))
;; Remove all numbers GREATER THAN 20.;; The test #'> checks if element > item, i.e., element > 20(print (remove 20 *numbers* :test #'>))Output
Section titled “Output”(10 5 15)Destructive Removal: delete
Section titled “Destructive Removal: delete”Use with caution! The delete family of functions (delete, delete-if, etc.) are permitted to modify the original list structure to remove the elements. This can be more memory-efficient but is dangerous if other parts of your code hold a reference to the original list.
Example: The Effect of delete
Section titled “Example: The Effect of delete”;; NOTE: We use `defvar` here because the list itself will be modified.(defvar *my-list* (list 'a 'b 'c 'b 'a))
(format t "List before delete: ~s~%" *my-list*)
;; `delete` returns the modified list, but also changes *my-list*(delete 'b *my-list*)
(format t "List after delete: ~s~%" *my-list*)Output
Section titled “Output”List before delete: (A B C B A)List after delete: (A C A)Best Practice: When to Use Destructive Functions
Section titled “Best Practice: When to Use Destructive Functions”Prefer non-destructive functions like remove by default. Only use destructive functions like delete in performance-critical sections of code where you are certain that no other part of the program holds a reference to the list being modified.