More on Lists
<-Previous | ^UP^ | Next->
In the earlier tutorial on Lists we covered Creating Lists, Adding to Lists, Accessing elements within a List, and treating Lists as Property Lists (Plists). In this tutorial we will continue to look at working with lists.
List Properties
Common Lisp provides a number of functions for obtaining properties of lists
- length takes a list as its argument and returns an integer corresponding to the number of elements within that list. Note that if any of the elements of the list passed to length is itself a list or other aggregate data type, it still counts as a single element for the purposes of length.
- member takes an object and list as arguments and returns all the values in the list starting with the first element matching object. If there is no match the function returns nil
- position takes an object and a list as inputs and returns the index number of the first object in the list matching object. The index number is zero-based. Again, the default test for equality is eql but you can override this by specifying a :test keyword input
GDL-USER> (setq a (list 1 2 3))
(1 2 3)GDL-USER> (length a)
3GDL-USER> (setq b (list 1 2 (list 3 4 5 6)))
(1 2 (3 4 5 6))GDL-USER> (length b)
3
GDL-USER> (member 2 a)
(2 3)GDL-USER> (member 4 a)
NIL
GDL-USER> (setq c (list "a" "b" "c"))
("a" "b" "c")GDL-USER> (member "a" c)
NILGDL-USER> (member "a" c :test 'equal)
("a" "b" "c")
GDL-USER> (position 2 a)
1GDL-USER> (position "b" c)
NILGDL-USER> (position "b" c :test 'equal)
2
List Processing
There are a number of Common Lisp functions which allow us to process lists in various ways, such as returning part of a list, returning a new list with elements removed, and re-ordering or sorting a list
- subseq treats a list as a sequence. Its takes a list and start position and optionally an end position, returning a list comprising the elements from and including the start position up to but excluding the end position. Positions are zero-based. Note that if either start or end is a higher index than the maximum index number in the list, then an error will be generated
- remove takes an object and a list as inputs and returns the list with object removed from it. By default the equality test is EQL, but an alternative may be specified using the :test keyword input. If :start or :end keywords are provided, only elements between those positions are tested for a match and removed. Finally if :count is provided only the first count instances of object are removed
- delete is identical to remove except it modifies the input list
- remove-duplicates removes any duplicate values from a list, where duplicate is tested by default using eql. As with remove, :test, :start and :end keyword inputs may be specified. When duplicates are found, only the last occurrence of the duplicate is retained in the return value
- flatten (a GendL function, not included in standard Common Lisp) takes a list as input, which may comprise sub-lists, and returns a one dimensional list. Because nil is an empty list, any occurences of nil in a list passed to flatten will be removed
- reverse takes a list as input and returns a list but with the elements in the reverse order.nreverseis the destructive version of reverse, ie it may alter the supplied list
GDL-USER> (setq lis (list 1 2 3 "a" "B" (list 1 2 3)))
(1 2 3 "a" "B" (1 2 3))GDL-USER> (subseq lis 2)
(3 "a" "B" (1 2 3))GDL-USER> (subseq lis 2 4)
(3 "a")
GDL-USER> (setq rm (list 1 2 3 4 3 4 5 6 4 5 4 5 3 1))
(1 2 3 4 3 4 5 6 4 5 4 5 3 1)GDL-USER> (remove 3 rm)
(1 2 4 4 5 6 4 5 4 5 1)GDL-USER> (remove 3 rm :count 2)
(1 2 4 4 5 6 4 5 4 5 3 1)GDL-USER> (remove 3 rm :start 3)
(1 2 3 4 4 5 6 4 5 4 5 1)GDL-USER> (remove 3 rm :start 3 :end 6)
(1 2 3 4 4 5 6 4 5 4 5 3 1)GDL-USER> (setq rm1 (list (list 1 2) (list 3 4) (list 1 3)))
((1 2) (3 4) (1 3))GDL-USER> (remove (list 3 4) rm1 :test 'equal)
((1 2) (1 3))
GDL-USER> rm1
((1 2) (3 4) (1 3))GDL-USER> (delete (list 3 4) rm1 :test 'equal)
((1 2) (1 3))GDL-USER> rm1
((1 2) (1 3))
GDL-USER> (setq rm (list 1 2 3 4 3 4 5 6 4 5 4 5 3 1))
(1 2 3 4 3 4 5 6 4 5 4 5 3 1)GDL-USER> (remove-duplicates rm)
(2 6 4 5 3 1)GDL-USER> (setq rm (list "a" "b" "c" "a" "A"))
("a" "b" "c" "a" "A")GDL-USER> (remove-duplicates rm)
("a" "b" "c" "a" "A")GDL-USER> (remove-duplicates rm :test 'equal)
("b" "c" "a" "A")GDL-USER> (remove-duplicates rm :test 'equalp)
("b" "c" "A")
GDL-USER> (setq lis (list 1 2 3 (list 1 4 5) (list "a" "b") (list :a 1 :b 2)))
(1 2 3 1 4 5 "a" "b" :A 1 :B 2)GDL-USER> (flatten lis)
(1 2 3 1 4 5 "a" "b" :A 1 :B 2)GDL-USER> (setq lis (list 1 2 3 nil (list 1 4 5) (list "a" "b") nil (list :a 1 :b 2)))
(1 2 3 1 nil 4 5 "a" "b" nil :A 1 :B 2)GDL-USER> (flatten lis)
(1 2 3 1 4 5 "a" "b" :A 1 :B 2)
GDL-USER> (setq a (list 1 2 3 4 5))
(1 2 3 4 5)GDL-USER> (reverse a)
(5 4 3 2 1)GDL-USER> a
(1 2 3 4 5)GDL-USER> (nreverse a)
(5 4 3 2 1)GDL-USER> a
(1)
List Sorting
- sort is a Common Lisp function which takes a List and a predicateas arguments and returns a list such that no 2 successive elements [x] and [y] returns false for (predicate x y) and true for (predicate y x)
A significant downside of using sort is that it is distructive - it can modify the input list. GendL therefore defines a function safe-sort which mirrors sort in every respect apart from it does not modify the input list. It is strongly recomended to use safe-sort for any list sorting
An obvious but sometimes overlooked equirement for the input list is that for sort or safe-sort to be meaningful, the elements in the list must be of types which may be compared with the same predicate
GDL-USER> (setq a (list 1 3 2 5 4))
(1 3 2 5 4)GDL-USER> (setq b (list 1 3 2 5 4)
(1 3 2 5 4)GDL-USER> (sort a '<)
(1 2 3 4 5)GDL-USER> a
(1 2 3 4 5)GDL-USER> (safe-sort b '<)
(1 2 3 4 5)GDL-USER> b
(1 3 2 5 4)
GDL-USER> (setq a (list (list 2 4) (list 1 6) (list 4 3) (list 3 5)))
((2 4) (1 6) (4 3) (3 5))GDL-USER> (safe-sort a '< :key 'first)
((1 6) (2 4) (3 5) (4 3))
Resources
![]() | more-on-lists.lisp |