Presentation is loading. Please wait.

Presentation is loading. Please wait.

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

Similar presentations


Presentation on theme: "List manipulation Consider student database, where each student is represented by the following list: * (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2."— Presentation transcript:

1 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”.

2 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 ( )) (status withdrawn)) (tests ( )) (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 )) ((NAME (PAUL BENNETT)) (MAJOR CS) (HOMEWORKS ( )) (TESTS ( )) (CLASSWORK 10.0) (PROJECT 18) (FINAL 28)) * (setf student2 (construct-student '(Abe Cadman) 'CS 'withdrawn)) ((NAME (ABE CADMAN)) (MAJOR CS) (STATUS WITHDRAWN))

3 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 ) (construct-student '(Abe Cadman) 'CS 'withdrawn) (construct-student '(Nelson DaCunha) 'CS ) (construct-student '(Susan Melville) 'CS ) (construct-student '(Igor Pevac) 'CS 'withdrawn)))

4 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 ( )) (TESTS ( )) (CLASSWORK 10.0) (PROJECT 18) (FINAL 28)) ((NAME (ABE CADMAN)) (MAJOR CS) (STATUS WITHDRAWN)) ((NAME (NELSON DACUNHA)) (MAJOR CS) (HOMEWORKS ( )) (TESTS ( )) (CLASSWORK 10.0) (PROJECT 17) (FINAL 25)) ((NAME (SUSAN MELVILLE)) (MAJOR CS) (HOMEWORKS ( )) (TESTS ( )) (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))

5 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 (list-1) (if (endp list-1) NIL (cons ( (first list-1)) ( (rest list-1)))))

6 The MAPCAR primitive transforms lists Mapcar has the following format: (mapcar #’... ), where: – supplies the name of the transforming procedure, –,..., supply lists of elements to be transformed. Examples: * (mapcar #'zerop '( )) (NIL NIL T NIL T NIL) * (mapcar #'= '( ) '( )) (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 WITHDRAWN)

7 Filtering undesired elements Consider the list (5.0 WITHDRAWN 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)) ( )

8 Filtering procedures: a general format Procedures for filtering out elements that do not satisfy the desired property have the following general format: (defun (list-1) (cond ((endp ) nil) (( (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.

9 The REMOVE-IF and REMOVE-IF-NOT primitives simplify filtering procedures n Remove-if removes all elements of list, which satisfy the predicate serving as a filter. Its general format is the following: (remove-if #’ ) n 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 #’ ) where: – supplies the name of the filtering procedure, – is the list of elements to be filtered.

10 Examples: To filter all symbols (non-numerical atoms) from the grades list: * (remove-if #‘symbolp (mapcar #'get-hw2 students)) ( ) Or, also we can say * (remove-if-not #‘numberp (mapcar #'get-hw2 students)) ( ) Filter zeros from a given list of numbers: * (remove-if #'zerop '( )) (2 4 6) Filter non-even elements of a given list of numbers: * (remove-if-not #'evenp '( )) (4 6 8)

11 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)) # * (setf square #'(lambda (n) (* n n))) # * square # * (mapcar square '( )) ( ) * (mapcar #'(lambda (n) (* n n)) '( )) ( ) Lambda expressions make it possible to create new functions at run time. Such run-time functions are called closures.

12 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 (list-1) (cond ((endp list-1) 0) (( (first list-1)) (+ 1 ( (rest list-1)))) (t ( (rest list-1)))))

13 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

14 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 '( )) 3 * (every #'oddp '( )) NIL * (some #'oddp '( )) T

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

16 General format of a searching procedure and its substitute primitives FIND-IF and FIND-IF-NOT (defun (list-1) (cond ((endp list-1) nil) (( (first list-1)) (first list-1)) (t ( (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)

17 The FUNCALL and APPLY primitives allow procedures to be passed as arguments n The funcall primitive has the following format: (funcall #’... ) n The apply primitive has the following format: (apply #’ (... )) Examples: * (defun pass-operator (operand-1 operand-2 operator) (funcall operator operand-1 operand-2)) PASS-OPERATOR * (pass-operator '+) 55 * (pass-operator '-) 11 * (pass-operator '*) 726

18 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 '+) *** Debugger warning: leftover specials *** >>> Error: {Determining function in error.} >>> Error:Undefined 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 '+) 55 * (pass-operator '-) 11

19 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 #' '(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 #' '(4 5 6)) 21


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

Similar presentations


Ads by Google