general pattern for selecting some elements of a list. This negatives example illustrates a general pattern: If you want a function which selects some elements from a list and drops others, you need a recursive function with two recursive conditions. (defun negatives(L) (cond ( (null L) ‘() )
general pattern for selecting some elements of a list
This negatives example illustrates a general pattern:
If you want a function which selects some elements from a list and drops others, you need a recursive function with two recursive conditions.
(defun negatives(L) (cond ( (null L) ‘() )
( (< (first L) 0 )
(cons (first L) (negatives (rest L))) )
( t (negatives (rest L)) ) ))
One recursive condition conses the current first element to the answer that’s being built up, because it matches the criterion
The other recursive condition simply returns the answer built up for the rest of the list, “ignoring” the current first element
Under the bonnet:Cons makes “cons cells”
Lists are implemented as building blocks called cons cells
A cons cell is a data structure with two fields:
a first fieldand a rest field
The first field contains a pointer to the first element of the list. The rest field points to the next cons cell in the list
The list ‘(doe ray mee) is really a chain of cons cells ending in a rest field with a nil
Representing embedded lists
A lists with lists inside it:‘((karl lewis) is a (runner))
Proper lists end in Nil but you can have lists which have another atom in the rest location.
> (cons ‘sonya ‘o-sullivan)
These will print out as “dotted pairs”.
first or CAR, rest or CDR
In the early days of Lisp, people used car (pronounced "CAR") and cdr ("could-a") instead of first and rest.
The only advantage car and cdr have is that you can combine them in a single function name; for example
(cadr L) means (car (cdr L)); same as (first (rest L))
(cddr L) means (cdr (cdr L)); same as (rest (rest L))
> (setf letters '(a (b (c))))
(A (B (C)))
> (caadr letters)
> (first (first (rest letters)))
Generally, except when we benefit from using one of these compound expressions, we will stick to FIRST and REST
Operations on lists: Append
Cons takes an element (atom or list) and a list and puts the element first in a new list with the list argument as the rest.
>(cons ‘( 1 2 3) ‘(4 5 6) )
( (1 2 3) 4 5 6)
Append takes two lists (has to be two lists) and joins them together making a single list.
>(append ‘( 1 2 3) ‘(4 5 6) )
(1 2 3 4 5 6)
Append is a built-in function, like member. We can write append recursively, using cons. Lets see how.
Append is a function that joins two lists together.
Unlike cons, append is recursive .
Append takes two lists as arguments: (append A B)
We first need to figure out the stopping condition for append. We’ll focus on A in getting the stopping condition.
We ask: what is the simplest, smallest version of A so that, asked (append A B), we can return the correct answer straight away without having to do anything much?
>(append ‘() ‘(a b c) )
(a b c)
If asked to append an empty list A to any other list B, we can simply return the other list B without doing anything else.
(null A) is our stopping condition.
Append takes two lists (append A B) and joins them together
We know the stopping condition is (null A).
What is the recursive condition going to be like?
Since the stopping condition is a test on A, we know that in the recursive condition we’re going to have to make the list A smaller on each recursive call (to eventually hit the stopping condition).
We also know that the recursive call will not reduce the size of the list B (since the stopping condition tests only A, not B).
So the recursive condition will contain the call
(append (rest A) B)
We will also need to do something with the bit of A that we’ve “left out” in this recursive call.
Append: Combining the current element with the recursive answer
Suppose A is ‘(1 2 3), B is ‘(4 5 6) and we call (append A B)
We know that in executing (append A B) we will do the recursive call (append (rest A) B)
What will (append (rest A) B) return?
‘(2 3 4 5 6)
So what do we need to do to get the correct answer for (append A B)? We need to add the missing first element from A to the answer from (append (rest A) B).
Code for Append (using if rather than cond for a change):
(defun append(A B)
Append is a built-in function in lisp, as are useful functions like member, replace, substitute etc. To understand recursion it’s good to know how they work.
(reverse ‘(a b c d)) returns ‘(d c b a).
L is empty (null L)
(reverse (rest L))
Suppose L = ‘(a b c d). What will (reverse (rest L)) return?
(d c b).
What do we need to do to ‘(d c b) to get ‘(d b c a)?
(append ‘(d c b) (list ‘a)) will return ‘(d b c a)
(cond ( )
( t )
Specifying the stopping conditions in a recursive function is very important; but it is usually easy.
It is harder to write the recursive conditions, especially to say how the answer from the recursive “smaller” call is combined with the “left-behind” part of the argument.
One trick: Take an example list argument. Assume the recursive call returns the correct answer for the “rest” part of that list. Figure out how to put that answer together with the left-behind “first” part of the list, to get the overall answer.
This “assume the smaller call is correct” approach is called induction.