550 likes | 563 Views
This article explores the internals of lists and their operations, focusing on an extended example in Lab #1. It also covers Homework #1, discussing different card games and their point values. The article concludes with a discussion on dealing with card games using data-driven functions and anonymous functions.
E N D
Lists CSC 358/458 4.3.2006
Outline • Lab #1 / Homework #1 • Lists • A question • List • Internals of Lists • List operations • Extended Example
Lab #1 (defun add-car (lst1 lst2) (+ (car lst1) (car lst2))) (defun deal-hand (size deck) (let ((hand '())) (dotimes (i size) (push (pop deck) hand)) hand)) (defun count-face-cards (hand) (let ((counter 0)) (dolist (card hand) (if (face-cardp card) (incf counter))) counter)) (defun face-cardp (card) (not (numberp (card-rank card))))
Lab #1 (Extra credit) (defun average-face-cards (trials size) (let ((deck (make-deck)) (count 0)) (dotimes (k trials) (setf deck (shuffle-deck deck)) (incf count (count-face-cards (deal-hand size deck)))) (/ (float count) trials)))
Homework #1 ;; Cheating version (using case) (defun card-blackp (card) (case (card-suit card) ((H D) nil) ((S C) t))) ;; Non-cheating version (using cond) (defun card-blackp (card) (let ((suit (card-suit card))) (cond ((or (eq suit 'H) (eq suit 'D)) nil) (t t)))) ;; Functional version with boolean return value (defun card-blackp (card) (let ((suit (card-suit card))) (or (eq suit 'S) (eq suit 'C))))
Homework #1 (defun not-twenty-onep (card1 card2) (let ((rank1 (card-rank card1)) (rank2 (card-rank card2))) (if (and (= rank1 1) (= rank2 1)) ;; special case 12 (let ((value1 (card-points-bj rank1)) (value2 (card-points-bj rank2))) (let ((value-sum (+ value1 value2))) (if (= value-sum 21) nil value-sum)))))) (defun card-points-bj (rank) (case rank ((k q j) 10) (1 11) (otherwise rank)))
Homework #1 (defun card-points-skat (rank) (case rank (1 11) (10 10) (k 4) (q 3) (j 2) (otherwise 0))) (defun skat-trick (trick) (let ((score 0)) (dolist (card trick) (incf score (card-points-skat (card-rank card)))) score))
Homework #1 (XC) (defun can-buildp (hand center) (dolist (hand-card hand) (dolist (center-card center) (if (can-build-cards hand-card center-card hand) (return-from can-buildp (list hand-card center-card))))))) (defun can-build-cards (hand-card center-card hand) (let ((hand-value (card-points-cassino (card-rank hand-card))) (center-value (card-points-cassino (card-rank center-card)))) (let ((build-value (+ hand-value center-value))) (dolist (card hand) (if (= build-value (card-points-cassino (card-rank card))) (return-from can-build-cards t)))))) (defun card-points-cassino (rank) (case rank (k 13) (q 12) (j 11) (otherwise rank)))
Homework #1 (defun can-buildp (hand center) (let ((ans nil)) (dolist (hand-card hand) (dolist (center-card center) (if (can-build-cards hand-card center-card hand) (setf ans (list hand-card center-card))))) ans))
Last week • How to deal with card games with different point values? • One possibility • lots of named functions • ick • Better approach • be data-driven • let the functions be defined by the data • Problem • don't want an extra argument to a comparison operator • suppose I want to supply the comparison to a sort function?
Lisp solution • Create an anonymous function using the data • use this function for comparing • Benefits • can create as many as needed • symbol table not filled with unnecessary stuff
card-comparator (defvar *default-card-values* '((K 13) (Q 12) (J 11))) (defvar *ace-high-values* '((1 14) (K 13) (Q 12) (J 11))) (defvar *skat-card-values* '((1 14) (10 13) (K 12) (Q 11) (J 10))) (defun rank-to-number2 (rank card-values) (let ((value-pair (assoc rank card-values))) (if value-pair (cadr value-pair) rank))) (defun card-comparator (&optional (card-values *default-card-values*)) #'(lambda (card1 card2) (let ((rank1 (card-rank card1)) (rank2 (card-rank card2))) (< (rank-to-number2 rank1 card-values) (rank-to-number2 rank2 card-values)))))
Some syntax: lambda • lambda • a lot more about this next week • this is how we define an anonymous function • defun is just a convenience • (setf (function foo) #'(lambda (x y) (+ x y))) • the same as • (defun foo (x y) (+ x y)) • lambda is fundamental
Optional arguments • More about this next week • I can specify in the parameter list that some arguments are optional • and then supply default values • Optional arguments must be at the end • for obvious reasons
Closure • Note • the function I define using lambda • refers to a parameter defined in the enclosing scope • card-values • the function is returned to the caller • the enclosing scope disappears • can I do this? • Yes! • technical term = "lexical closure" • more about this next week
A Question • Why can’t I really deal from my deck? • Why does this function not change the value of its argument? (defun mypop (lst) (pop lst)) > (setf l1 '(a b c)) > (mypop l1) A > l1 (A B C)
Lists • sequences of items • atoms or lists • car • first element • cdr • rest • cons • builds a list
Other List Builders • list • a list containing the arguments • append • a list combining the arguments
What Does It Do?* • (setf l1 '(a (b c))) • (setf l2 '((d) e)) • (car l2) • (cdr l1) • (cons 'b l1) • (list l2 'd) • (append l1 l2)
What does it do? • (pop l1) • special form • short for • (setf l1 (cdr l1)) • (defun mypop (lst) (pop lst)) • (defun my pop (lst) (setf lst (cdr lst))) • (mypop l1) • l1 doesn't change • why should it? • it is still bound to the same thing
How can we achieve this? • Global variable • ick • Game state structure • pass in and out • contents manipulated • better
Example: Blackjack (defun make-bj-game (player-count) (list (shuffle-deck (make-deck)) (make-list player-count :initial-element nil))) (defun player-count (game) (length (hands game))) (defun deck (game) (cadr game)) (defun hands (game) (caddr game)) (defun hand (player game) (nth player (hands game)))
New function • make-list • note the function call • :initial-element • keyword argument • we'll see this again later
Shared Structure • It is easy to create lists that share structure • (setf c (append a b)) • Not a problem unless a or b is destructively modified
List Functions • Accessors • navigating around in lists • Operations • manipulate the whole list • Destructive operations • modify the list
List Accessors • car, cdr • positional • first, second, third • (nth i lst) • (nthcdr i lst) • end of the list • last • actually returns last cdr
List Operations • Many! • Mapping functions • Boolean operations • Finding • Modifying (copy) • Reduce
Mapping • Functions that apply a function over and over across a list • (mapcar #’fn lst) • applies the function to each element in the list • returns a list of the results • Also works with multiple lists • (mapcar #’fn lst1 lst2)
Example • Matt's question • What if I want to add 5 to everything in a list? • (mapcar #'(lambda (x) (+ x 5)) '(1 2 3))
Other Mapping Functions • maplist • applies to successive cdrs • mapcan • joins results with append • mapc, mapl • like mapcar and maplist but doesn't return results
Boolean Operations • logical summary of a list • relative to a function • (some #’fn lst) • true if any element of lst returns true when the function is applied • every • false if any element returns false • notany • notevery
Example • flushp • is a poker hand a flush? • 5-card hand, please • logic • all cards the same suit • simple method • all spades or all clubs or ... • better way?
Finding Operations • (member elem lst) • if elem is in list, returns the cdr headed by elem • (position elem lst) • if elem is in list, return position index • both return nil is absent
Modifications • (remove elem lst) • returns a copy of lst with elem removed • not “destructive” • Also "if" version • remove-if • has a test instead of an element • (substitute old new lst) • returns a copy of lst with old replaced by new
Reduce • “reduce” list into a single value • (reduce #’fn lst) • applies fn to first two elements • applies fn to that result and next element • etc. • (reduce #’+ ‘(1 2 3 4)) => 10
Example • count-face-cards • mapcar and reduce • Original version (defun count-face-cards (hand) (let ((counter 0)) (dolist (card hand) (if (face-cardp card) (incf counter))) counter)) (defun face-cardp (card) (not (numberp (card-rank card))))
Destructive Operations I • Most Lisp operations return “new” lists • remove • (setf lst ‘(b a b a b)) • (remove ‘a lst) => (b b b) • lst => (b a b a b) • Use the result of the function • New cons cells created • not always what you want
Destructive Operations II • Alter the contents of cons cells • rplaca = replace the car • rplacd = replace the cdr • nconc = destructive append • delete = destructive remove • nsubst = destructive subst • Others • “n” means “non-consing”
Generalized Variables • setf • can be similarly used • (setf (car lst) val) • (setf (cdr lst) val) • Main reason to use • efficiency • creating new cons cells • creating garbage
Problems with Destructive Operations • Shared structure • Effects hard to predict • Not necessary • Main reason to use • efficiency • but remember Knuth • “Premature optimization is the root of all evil.”
Example • Look at deck after shuffle-deck
defsetf • One principle of Lisp is that the programmer has the same power as the language designer • You can create your own generalized variables • use defsetf
Example (defun 3rd (lst) (nth 2 lst)) (defun set-3rd (lst value) (setf (nth 2 lst) value)) (defsetf 3rd set-3rd) (setf lst '( a b c d)) (setf (3rd lst) 'q) lst => (a b q d) (setf (3rd lst))
Association List • Allows lookup of values associated with keys • ((key1 . val1) (key2 . val2) ... (keyn . valn)) • OK for short associations • large associations a hash table is better • (assoc key alist) • returns the pair • (rassoc val alist) • returns the pair
Keyword parameters • what if the value is not an atom? (rassoc '(21 M) '((John 21 M) (Jane 19 F) (Joe 25 M))) • doesn’t work because rassoc uses eq • extra arguments to rassoc • :test • specifies which function to use instead of default (rassoc '(21 M) '((John 21 M) (Jane 19 F) (Joe 25 M)):test #'equal)
Keyword Parameters • Many functions have these • Most common • :test • :key • specifies a function to apply before testing (rassoc '21 '((John 21 M) (Jane 19 F) (Joe 25 M)):key #'car) • :start • :end