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.

    GDL-USER> (setq a (list 1 2 3))

    (1 2 3)

    GDL-USER> (length a)

    3

    GDL-USER> (setq b (list 1 2 (list 3 4 5 6)))

    (1 2 (3 4 5 6))

    GDL-USER> (length b)

    3
  • 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

    GDL-USER> (member 2 a)

    (2 3)

    GDL-USER> (member 4 a)

    NIL
    member uses the equality test eql as the default test. An alternative test may be provided using the :test keyword input. Whatever function is specified for:test needs to be quoted

    GDL-USER> (setq c (list "a" "b" "c"))

    ("a" "b" "c")

    GDL-USER> (member "a" c)

    NIL

    GDL-USER> (member "a" c :test 'equal)

    ("a" "b" "c")
  • 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> (position 2 a)

    1

    GDL-USER> (position "b" c)

    NIL

    GDL-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
  • 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")
  • 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
  • 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))
  • delete is identical to remove except it modifies the input list
  • 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))
  • 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

    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")
  • 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
  • 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)
  • 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 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)
    sort and safe-sort both have a keyword input :key which allows the element on which the sorting is to be performed to be identified. :key is specified as a quoted function which is applied to each element in the input list and then the sort is applied to that return value

    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))