list manipulation consider student database, where each student is represented by the following...

19
List 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))) ==> 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))) That is, any change in the data representation requires a change in the “getter” procedure. Furthermore, all of the details of the data representation must be remembered in order to construct an appropriate “getter”.

Upload: cameron-hamilton

Post on 16-Dec-2015

213 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: List manipulation Consider student database, where each student is represented by the following list: * (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2

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

That is, any change in the data representation requires a change in the “getter” procedure.

Furthermore, all of the details of the data representation must be remembered in order to

construct an appropriate “getter”.

Page 2: List manipulation Consider student database, where each student is represented by the following list: * (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2

Better 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:

* (setf student1 (construct-student '(Paul Bennett) 'CS 4.3 5.0 3.5 4.8 4.9 9.5 8.7 10.0 18 28))

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

* (setf student2 (construct-student '(Abe Cadman) 'CS 'withdrawn))

((NAME (ABE CADMAN)) (MAJOR CS) (STATUS WITHDRAWN))

Page 3: List manipulation Consider student database, where each student is represented by the following list: * (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2

Example (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) (construct-student '(Igor Pevac) 'CS 'withdrawn)))

Page 4: List manipulation Consider student database, where each student is represented by the following list: * (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2

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

Page 5: List manipulation Consider student database, where each student is represented by the following list: * (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2

List 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 <transformation-proc> (list-1)

(if (endp list-1)

NIL

(cons (<get-desired-element-proc> (first list-1))

(<transformation-proc> (rest list-1)))))

Page 6: List manipulation Consider student database, where each student is represented by the following list: * (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2

The MAPCAR primitive transforms lists

Mapcar has the following format:

(mapcar #’<procedure object> <list-1> ...<list-n> ), where:– <procedure object> supplies the name of the transforming procedure,– <list-1>,..., <list-n> 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)

Page 7: List manipulation Consider student database, where each student is represented by the following list: * (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2

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

Page 8: List manipulation Consider student database, where each student is represented by the following list: * (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2

Filtering procedures: a general format

Procedures for filtering out elements that do not satisfy the desired property

have the following general format:

(defun <filtering-procedure> (list-1)

(cond ((endp <list-1>) nil)

((<testing-for-desired-property-proc> (first list-1))

(cons (first list-1) (filtering-procedure> (rest list-1))))

(t (filtering-procedure> (rest list-1))))

The resulting list may contain the same or smaller number of elements

than the original list.

Page 9: List manipulation Consider student database, where each student is represented by the following list: * (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2

The 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 #’<procedure object> <list>)

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 #’<procedure object> <list>)

where:

– <procedure object> supplies the name of the filtering procedure,

– <list> is the list of elements to be filtered.

Page 10: List manipulation Consider student database, where each student is represented by the following list: * (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2

Examples:

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)

Page 11: List manipulation Consider student database, where each student is represented by the following list: * (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2

Mapping 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))#<LISP::SCANNED (LAMBDA (N) (DECLARE) (* N N))>* (setf square #'(lambda (n) (* n n)))#<LISP::SCANNED (LAMBDA (N) (DECLARE) (* N N))> * square#<LISP::SCANNED (LAMBDA (N) (DECLARE) (* N N))> * (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.

Page 12: List manipulation Consider student database, where each student is represented by the following list: * (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2

Counting 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 <counting proc> (list-1) (cond ((endp list-1) 0) ((<testing-desired-prop proc> (first list-1)) (+ 1 (<counting proc> (rest list-1)))) (t (<counting proc> (rest list-1)))))

Page 13: List manipulation Consider student database, where each student is represented by the following list: * (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2

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

3

Page 14: List manipulation Consider student database, where each student is represented by the following list: * (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2

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

Page 15: List manipulation Consider student database, where each student is represented by the following list: * (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2

Searching for an element that satisfies a desired property

Assume we want to search for a student who have 20 points on theproject. 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)

Page 16: List manipulation Consider student database, where each student is represented by the following list: * (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2

General format of a searching procedure and its substitute primitives FIND-IF and FIND-IF-NOT

(defun <searching procedure> (list-1) (cond ((endp list-1) nil) ((<testing-desired-prop proc> (first list-1)) (first list-1)) (t (<searching procedure> (rest list-1)))))

Find-if and find-if-not are primitives that search for the first element of a listsatisfying (not satisfying) a desired property. In the student DB example, to askif 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)

Page 17: List manipulation Consider student database, where each student is represented by the following list: * (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2

The FUNCALL and APPLY primitives allow procedures to be passed as arguments

The funcall primitive has the following format:

(funcall #’<procedure object> <argument-1> ... <argument-n>) The apply primitive has the following format:

(apply #’<procedure object> (<argument-1> ... <argument-n>))

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

Page 18: List manipulation Consider student database, where each student is represented by the following list: * (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2

Examples (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 ***>>> Error: {Determining function in error.}>>> Error:Undefined function: OPERATORwhile 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

Page 19: List manipulation Consider student database, where each student is represented by the following list: * (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2

More 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 ***

>>> Error: {Determining function in error.}

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