LISP - Remove an Element
Modern Guide to Removing Elements from Sequences in Lisp
Section titled “Modern Guide to Removing Elements from Sequences in Lisp”Common Lisp provides a robust set of functions for removing elements from sequences (like lists and vectors). A crucial distinction in Lisp is between non-destructive functions, which return a new modified sequence, and destructive functions, which may modify the original sequence. This guide covers the modern best practices for these operations.
Non-Destructive Removal (Best Practice)
Section titled “Non-Destructive Removal (Best Practice)”Non-destructive functions are safer because they don’t have side effects on the original data. This aligns with functional programming principles and makes code easier to reason about. You should always prefer these unless you have a specific, proven performance bottleneck.
1. remove function
Section titled “1. remove function”The remove function creates and returns a new sequence with all occurrences of a specific item removed. The original sequence is left untouched.
(remove item sequence &key from-end test test-not start end count key)Let’s see a practical example. We’ll remove the number 3 from a list.
(let ((original-list '(1 2 3 4 3 5))) (let ((new-list (remove 3 original-list))) (format t "Original List: ~a~%" original-list) (format t "New List: ~a~%" new-list)))Output
Section titled “Output”Original List: (1 2 3 4 3 5)New List: (1 2 4 5)2. remove-if and remove-if-not
Section titled “2. remove-if and remove-if-not”These functions are more powerful, as they remove elements based on a condition (a predicate function) instead of a specific item.
(remove-if predicate sequence &key ...)(remove-if-not predicate sequence &key ...)Example: Remove all odd numbers from a list.
;; #'oddp is a predicate that returns true for odd numbers.(let ((new-list (remove-if #'oddp '(1 2 3 4 5 6 7)))) (format t "List with odd numbers removed: ~a~%" new-list))Output
Section titled “Output”List with odd numbers removed: (2 4 6)Destructive Removal (Use with Caution)
Section titled “Destructive Removal (Use with Caution)”Destructive functions like delete and delete-if may modify the original sequence’s memory structure (its cons cells) for efficiency. This can be dangerous if the sequence is shared elsewhere in your program.
Important Best Practice for Destructive Functions
Section titled “Important Best Practice for Destructive Functions”Because a destructive function might modify the very first element of a list, the start of the list can change. Therefore, you must always use the return value of a destructive function, typically by assigning it back to the original variable.
;; CORRECT usage of delete(setq my-list (delete 'a my-list))
;; INCORRECT usage of delete. This can lead to bugs!(delete 'a my-list) ; The binding of my-list is not updated.3. delete and delete-if
Section titled “3. delete and delete-if”These are the destructive counterparts to remove and remove-if.
Example: Destructively remove even numbers from a list.
(let ((my-numbers '(1 2 3 4 5 6 7))) (format t "Original variable before delete-if: ~a~%" my-numbers)
;; The list is modified in place, and we re-assign the variable. (setq my-numbers (delete-if #'evenp my-numbers))
(format t "Variable after delete-if: ~a~%" my-numbers))Output
Section titled “Output”Original variable before delete-if: (1 2 3 4 5 6 7)Variable after delete-if: (1 3 5 7)Advanced Usage: The :key Argument
Section titled “Advanced Usage: The :key Argument”The :key argument is incredibly useful for working with complex data structures. It allows you to specify a function to be called on each element before the test is applied.
Example: Remove all products with a price less than 50 from a list of property lists (plists).
(let ((products '((:name "apple" :price 30) (:name "banana" :price 60) (:name "orange" :price 45)))) (let ((expensive-products (remove-if (lambda (price) (< price 50)) products :key (lambda (product) (getf product :price))))) (format t "Expensive products: ~a~%" expensive-products)))Output
Section titled “Output”Expensive products: ((:NAME "banana" :PRICE 60))