Title: List manipulation
1List manipulation
- Consider student database, where each student is
represented by the following list - (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2
5.0) (hw3 3.5) (hw4 4.8) - (hw5 4.9)) ((test1 9.5) (test2 8.7))
(classw 10.0) (project 18) (final 28))) - Given this representation, to find HW2 grade of
student1 - (second (second (second student1))) gt
5.0 - To add student major as a second element of
student list - (setf student1 (append (list (first student1))
(list '(CS major)) (rest student1))) - ((PAUL BENNETT) (CS MAJOR) ((HW1 4.3) (HW2 5.0)
(HW3 3.5) (HW4 4.8) - (HW5 4.9)) ((TEST1 9.5) (TEST2 8.7)) (CLASSW
10.0) (PROJECT 18) (FINAL 28)) - Now to get to student1 HW2 grade, a different
reader procedure must be used - (second (second (third student1)))
2Better representation for student database
example.
- Consider the following association lists for
representing students - ((name (Paul Bennett))
((name (Abe Cadman)) - (major CS)
(major CS) - (homeworks (4.3 5.0 3.5 4.8 4.9))
(status withdrawn)) - (tests (9.5 8.7))
- (claswork 10.0)
- (project 18)
- (final 28))
- The following procedure will construct each of
these a-lists - (defun construct-student (name major hw1
optional hw2 hw3 hw4 hw5 test1 test2 classwork
project final) - (if (eql hw1 'withdrawn) (list (list 'name
name) (list 'major major) (list 'status
'withdrawn)) - (list (list 'name
name) (list 'major major) - (list 'homeworks
(list hw1 hw2 hw3 hw4 hw5)) - (list 'tests (list
test1 test2)) (list 'classwork classwork) - (list 'project
project) (list 'final final)))) - To construct student1 and student2, we say
3Example (cont.)
- We can now create getter procedures to access
each one of the elements of - student1. For example, to access the grade for
HW2 -
- (defun get-hw2 (student)
- (if (eql (second (assoc 'status student))
'withdrawn) - 'withdrawn
- (second (second (assoc 'homeworks
student))))) - Once constructor and getter procedures are
defined, the programmer can forget - about details of the representation. If the
representation changes, only affected - procedures must be re-written.
- To set-up the student database
- (setf students (list
- (construct-student '(Paul Bennett)
'CS 4.3 5.0 3.5 4.8 4.9 9.5 8.7 10.0 18 28) - (construct-student '(Abe Cadman) 'CS
'withdrawn) - (construct-student '(Nelson DaCunha)
'CS 4.8 4.0 4.5 3.8 5.0 8.5 9.7 10.0 17 25) - (construct-student '(Susan Melville)
'CS 3.8 5.0 4.7 4.8 5.0 8.3 9.9 10.0 20 24)
4List transformation returns a list containing
only selected elements of the original list
- Example Transform the students list into a list
containing only student names. - (defun names (students)
- (if (endp students)
- nil
- (cons (get-name (first students))
(names (rest students))))) - (defun get-name (student)
- (second (assoc 'name student)))
- students
- (((NAME (PAUL BENNETT)) (MAJOR CS) (HOMEWORKS
(4.3 5.0 3.5 4.8 4.9)) (TESTS (9.5 8.7)) - (CLASSWORK 10.0) (PROJECT 18) (FINAL 28))
((NAME (ABE CADMAN)) (MAJOR CS) (STATUS - WITHDRAWN)) ((NAME (NELSON DACUNHA)) (MAJOR
CS) (HOMEWORKS (4.8 4.0 4.5 3.8 5.0)) - (TESTS (8.5 9.7)) (CLASSWORK 10.0) (PROJECT
17) (FINAL 25)) ((NAME (SUSAN MELVILLE)) - (MAJOR CS) (HOMEWORKS (3.8 5.0 4.7 4.8 5.0))
(TESTS (8.3 9.9)) (CLASSWORK 10.0) - (PROJECT 20) (FINAL 24)) ((NAME (IGOR PEVAC))
(MAJOR CS) (STATUS WITHDRAWN))) - (names students)
- ((PAUL BENNETT) (ABE CADMAN) (NELSON DACUNHA)
(SUSAN MELVILLE) (IGOR PEVAC))
5List transformation procedures a general format
- When transforming a list into another list, the
resulting list is of the same - length as the original list. The general format
of the transformation procedure - is the following
-
- (defun lttransformation-procgt (list-1)
- (if (endp list-1)
- NIL
- (cons (ltget-desired-element-proc
gt (first list-1)) - (lttransformation-procgt
(rest list-1)))))
6The MAPCAR primitive transforms lists
- Mapcar has the following format
- (mapcar ltprocedure objectgt ltlist-1gt
...ltlist-ngt ), where - ltprocedure objectgt supplies the name of the
transforming procedure, - ltlist-1gt,..., ltlist-ngt supply lists of elements
to be transformed. - Examples
- (mapcar 'zerop '(8 5 0 1 0 5))
- (NIL NIL T NIL T NIL)
- (mapcar ' '(1 2 3 4 5) '(1 3 5 4 8))
- (T NIL NIL T NIL)
- (mapcar 'get-name students)
- ((PAUL BENNETT) (ABE CADMAN) (NELSON DACUNHA)
(SUSAN MELVILLE) (IGOR PEVAC)) - (mapcar 'get-hw2 students)
- (5.0 WITHDRAWN 4.0 5.0 WITHDRAWN)
7Filtering undesired elements
- Consider the list (5.0 WITHDRAWN 4.0 5.0
WITHDRAWN). To compute - the average grade, we must filter non-numerical
atoms. - (defun clean-grade-list (grade-list)
- (cond ((endp grade-list) nil)
- ((numberp (first grade-list))
- (cons (first grade-list)
(clean-grade-list (rest grade-list)))) - (t (clean-grade-list (rest
grade-list))))) - (clean-grade-list (mapcar 'get-hw2 students))
- (5.0 4.0 5.0)
8Filtering procedures a general format
- Procedures for filtering out elements that do not
satisfy the desired property - have the following general format
- (defun ltfiltering-proceduregt (list-1)
- (cond ((endp ltlist-1gt) nil)
- ((lttesting-for-desired-proper
ty-procgt (first list-1)) - (cons (first list-1)
(filtering-proceduregt (rest list-1)))) - (t (filtering-proceduregt
(rest list-1)))) - The resulting list may contain the same or
smaller number of elements - than the original list.
9The REMOVE-IF and REMOVE-IF-NOT primitives
simplify filtering procedures
- Remove-if removes all elements of list, which
satisfy the predicate serving as a filter. Its
general format is the following - (remove-if ltprocedure objectgt ltlistgt)
- Remove-if-not removes all elements of list, which
do not satisfy the predicate serving as a filter.
Its general format is the following - (remove-if-not ltprocedure objectgt ltlistgt)
- where
- ltprocedure objectgt supplies the name of the
filtering procedure, - ltlistgt is the list of elements to be filtered.
10Examples
- To filter all symbols (non-numerical atoms) from
the grades list - (remove-if symbolp (mapcar 'get-hw2
students)) - (5.0 4.0 5.0)
- Or, also we can say
- (remove-if-not numberp (mapcar 'get-hw2
students)) - (5.0 4.0 5.0)
- Filter zeros from a given list of numbers
- (remove-if 'zerop '(2 0 4 6 0 0))
- (2 4 6)
- Filter non-even elements of a given list of
numbers - (remove-if-not 'evenp '(3 4 5 6 7 8))
- (4 6 8)
11Mapping primitives can take as a procedure object
an already defined function or a lambda expression
- Lambda expressions are anonymous functions.
- Example compute the square of m
- '(lambda (n) ( n n))
- ltLISPSCANNED (LAMBDA (N) (DECLARE) ( N N))gt
- (setf square '(lambda (n) ( n n)))
- ltLISPSCANNED (LAMBDA (N) (DECLARE) ( N N))gt
- square
- ltLISPSCANNED (LAMBDA (N) (DECLARE) ( N N))gt
- (mapcar square '(1 2 3 4 5))
- (1 4 9 16 25)
- (mapcar '(lambda (n) ( n n)) '(1 2 3 4 5))
- (1 4 9 16 25)
- Lambda expressions make it possible to create new
functions at run time. Such - run-time functions are called closures.
12Counting list elements that satisfy a desired
property
- Consider the student DB example, and assume that
we want to count students - that have withdrawn from the class. The following
function will do the job - (defun count-w (students)
- (cond ((endp students) 0)
- ((eql (second (assoc 'status (first
students))) 'withdrawn) - ( 1 (count-w (rest
students)))) - (t (count-w (rest students)))))
- The general format of any counting procedure is
- (defun ltcounting procgt (list-1)
- (cond ((endp list-1) 0)
- ((lttesting-desired-prop procgt (first
list-1)) - ( 1 (ltcounting
procgt (rest list-1)))) - (t (ltcounting procgt (rest
list-1)))))
13The COUNT-IF and COUNT-IF-NOT primitives
- To count the number of students that have
withdrawn, we can also say - (count-if '(lambda (student)
- (eql (second
(assoc 'status student)) 'withdrawn)) students) - 2
- Or, if we have defined predicate get-w
- (defun get-w (student)
- (eql (second (assoc 'status student))
'withdrawn)) - the equivalent query is
- (count-if 'get-w students)
- 2
- To count the number of students that have not
withdrawn - (count-if-not 'get-w students)
14COUNT-IF counts the number of elements on a list
that satisfy a given property EVERY / SOME test
if every / some element on the list satisfies a
given property
- Examples
- (count-if 'oddp '(1 2 3 4 5))
- 3
- (every 'oddp '(1 2 3 4 5))
- NIL
- (some 'oddp '(1 2 3 4 5))
- T
15Searching for an element that satisfies a desired
property
- Assume we want to search for a student who have
20 points on the - project. The following functions will do the
job - (defun search-project-20 (students)
- (cond ((endp students) nil)
- ((eql 20 (get-project (first
students))) - (second (assoc 'name
(first students)))) - (t (search-project-20 (rest
students))))) - (defun get-project (student)
- (second (assoc 'project student)))
- (search-project-20 students)
- (SUSAN MELVILLE)
16General format of a searching procedure and its
substitute primitives FIND-IF and FIND-IF-NOT
- (defun ltsearching proceduregt (list-1)
- (cond ((endp list-1) nil)
- ((lttesting-desired-prop procgt (first
list-1)) - (first list-1))
- (t (ltsearching proceduregt (rest
list-1))))) - Find-if and find-if-not are primitives that
search for the first element of a list - satisfying (not satisfying) a desired property.
In the student DB example, to ask - if at least one student has (has not) 20 points
on the project, we can say - (second (assoc 'name (find-if '(lambda
(student) - (eql 20 (get-project
student))) students)) )
- (SUSAN MELVILLE)
- (second (assoc 'name (find-if-not '(lambda
(student) - (eql 20 (get-project
student))) students))) - (PAUL BENNETT)
17The FUNCALL and APPLY primitives allow
procedures to be passed as arguments
- The funcall primitive has the following format
- (funcall ltprocedure objectgt ltargument-1gt
... ltargument-ngt) - The apply primitive has the following format
- (apply ltprocedure objectgt (ltargument-1gt
... ltargument-ngt)) - Examples
- (defun pass-operator (operand-1 operand-2
operator) - (funcall operator operand-1 operand-2))
- PASS-OPERATOR
- (pass-operator 33 22 ')
- 55
- (pass-operator 33 22 '-)
- 11
- (pass-operator 33 22 ')
- 726
18Examples (cont.)
- Notice that without funcall, the pass-operator
function will not work - (defun pass-operator (operand-1 operand-2
operator) - (operator operand-1 operand-2))
- PASS-OPERATOR
- (pass-operator 33 22 ')
- Debugger warning leftover specials
- gtgtgt Error Determining function in error.
- gtgtgt ErrorUndefined function OPERATOR
- while evaluating (OPERATOR OPERAND-1 OPERAND-2)
- The apply primitive can be used instead of
funcall as follows - (defun pass-operator (operand-1 operand-2
operator) - (apply operator (list operand-1 operand-2)))
- PASS-OPERATOR
- (pass-operator 33 22 ')
- 55
- (pass-operator 33 22 '-)
- 11
19More examples
- (funcall 'first '(a b c d))
- A
- (funcall 'list 'a 'b 'c 'd)
- (A B C D)
- (funcall 'append '(a b) '(c d))
- (A B C D)
-
- (funcall ' 1 2 3 '(4 5 6))
- Debugger warning leftover specials
- gtgtgt Error Determining function in error.
- gtgtgt Error wrong type argument (4 5 6)
- A NUMBER was expected.
- (apply 'first '((a b c d)))
- A
- (apply 'list '(a b c d))
- (A B C D)
- (apply 'append '((a b) (c d)))
- (A B C D)
- In some cases, apply may have more
- than two arguments
- (apply ' 1 2 3 '(4 5 6))
- 21