I. grammar background A. background on grammars ------------------------------------------ CONTEXT-FREE GRAMMARS Example (statements in Python): ::= | assert | pass | return | return | ( ) | ( ) | if : else: | ... ::= = | ... ::= | , ::= ::= | ::= | + | ... ------------------------------------------ What is the relationship between a grammar and a language? What are some examples in the grammar for ? ------------------------------------------ DERIVATIONS 1. Start with a nonterminal 2. Replace any nonterminal with the rhs of a production for that nonterminal 3. If there are nonterminals left, go to 2 Example: --> --> = --> x = --> x = --> x = 5 ------------------------------------------ ------------------------------------------ PARSING AND PARSE TREES Parsing: 1. Lexical grammar classifies strings (e.g., x is an , 5 is a ) 2. Find a production whose right hand side appears in the tree tops, reduce the right hand side to the nonterminal on the left hand side, by making a tree with that left hand side as the root and branches to the right hand side tree tops. 3. If haven't reached the goal symbol (e.g., ), go to step 2. x = 5 | | x = 5 | | | x = 5 | | \ | | | | | | | | | x = 5 | | | \ | | | | | | | | | x = 5 ------------------------------------------ II. object-oriented classes background A. classes define how objects are produced and behave ------------------------------------------ PYTHON CLASSES DEFINE OBJECTS class Cell(object): def __init__(self, value): """Initialize this cell with the given value.""" def value(self): """Return the value in this cell.""" def set(self, value): """Change the value in this cell to be the given value.""" def __repr__(self): """Return a string representation of this cell.""" def __str__(self): """Return a string showing the value in this cell.""" ------------------------------------------ B. object construction ------------------------------------------ OBJECT CONSTRUCTION Objects are constructed by calling the class name as a function, Example: c1 = Cell(1) c2 = Cell(2) ------------------------------------------ C. method calls ------------------------------------------ METHOD CALLS Functions in a class that have a self argument are called Method calls are written: e.m(args) for example: c1.value() c2.set(3) ------------------------------------------ III. flat recursion over (Lisp) lists (Little Schemer ch. 2-4, EOPL 1.2.1) A. inductive definition of lists ------------------------------------------ INDUCTIVE DEFINITION OF LISP LISTS def: Let t be a type of data. Then a list(t) is - either - or Grammar: ------------------------------------------ B. correspondence between grammar and classes in Python ------------------------------------------ CORRESPONDENCE BETWEEN GRAMMARS AND CLASSES Grammars and Sentences|Classes and Objects ======================|=================== Nonterminal Sentence Alternative Example Grammar: ::= Nil() | Cons( , ) Corresponding Classes: import abc class LispList(abc.ABC): pass class Nil(LispList): def __init__(self): pass ... class Cons(LispList): def __init__(self, hd, tl) self.car = hd self.cdr = tl ... Corresponding instances: ------------------------------------------ ------------------------------------------ PICTURES OF LISTS Pictures of Lists: lst = Cons(1, Cons(2, Nil())) lst [ * ] / v [Cons | * | *-]-> [Cons | * | *-]-> [Nil] | | v v 1 2 ------------------------------------------ 1. the details of LispList ------------------------------------------ # $Id: LispList.py,v 1.4 2017/02/05 22:21:24 leavens Exp $ import abc class LispList(abc.ABC): pass class Nil(LispList): def __init__(self): """Initialize this empty list""" pass def __eq__(self, lst): """Return True just when lst is also an instance of Nil.""" return isinstance(lst, Nil) def __repr__(self): """Return a string representing this Nil instance.""" return "Nil()" def __str__(self): """Return a string showing the elements of self.""" return "[]" def isEmpty(self): """Return whether this list is empty.""" return True class Cons(LispList): def __init__(self, hd, tl): """Initialize this Cons with head hd and tail tl.""" self.car = hd self.cdr = tl def __eq__(self, lst): """Return True just when self is structurally equivalent to lst.""" return isinstance(lst, Cons) and lst.first() == self.first() \ and lst.tail() == self.tail() def __repr__(self): """Return a string representing this list.""" return "Cons(" + repr(self.first()) + ", " + repr(self.tail()) + ")" def elements_str(self): """Return a string of the elements of self, separated by commas.""" if self.tail().isEmpty(): return str(self.first()) else: return str(self.first()) + ", " + self.tail().elements_str() def __str__(self): """Return a string showing the elements of self.""" return "[" + self.elements_str() + "]" def isEmpty(self): """Return whether this list is empty.""" return False def first(self): """Return the first element of this list.""" return self.car def tail(self): """Return the rest of this list.""" return self.cdr ------------------------------------------ How does the __eq__() method work? What are equations for .isEmpty()? What equation relates Cons, .first() and .tail()? 2. consequences of LispList's design ------------------------------------------ LispList Methods Methods that work on all LispList objects: .isEmpty() __eq__ (i.e., == ) __repr__ (i.e., repr()) __str__ (i.e., str()) Other methods that work on Cons objects: .first() .tail() ------------------------------------------ ------------------------------------------ CONSEQUENCES - Must test with - In a Cons must use - When building a LispList, with Cons, ------------------------------------------ C. tips for avoiding taking to long to write recursive functions ------------------------------------------ TIPS FOR WRITING EXPLICIT RECURSIONS 1. Understand the problem a. What does it do? b. What's the type? c. What are the grammars for the arguments? d. What are examples for each case in the interesting grammars? e. What are related simpler examples? 2. Write an outline that follows the grammar 3. Fill in the outline using the examples - generalize from the examples - create new examples if needed 4. Use one function per nonterminal 5. Debug by testing larger and larger expressions ------------------------------------------ 1. tips by example ------------------------------------------ AN EXAMPLE RECURSIVE PROBLEM Write a function, sumSquares(lst), with type: LispList(int) -> int that takes a LispList of numbers and returns the sum of the squares of those numbers. a. What does it do? b. What's the type? c. What's the grammar for LispList(int)? d. What are some examples? e. What are some related simpler examples? ------------------------------------------ 2. follow the grammar ------------------------------------------ FOLLOW THE GRAMMAR! 2. Use an outline that matches the grammar for a (recursive) argument's data type. ::= Nil() | Cons(, ) ------------------------------------------ 3. fill in by generalizing examples ------------------------------------------ FILL IN BY GENERALIZING FROM EXAMPLES 3. Fill in the outline by generalizing the examples a. What's returned in the base case(s)? sumSquares(Nil()) == b. How do we get the answer from - recursive call's answer, and - other information? sumSquares(Cons(3, Cons(2, Cons(2, Nil())))) == 17 _______________________________ __ lon sumSquares(lon) sumSquares(Cons(2, Cons(2, Nil()))) == 8 ______________________ _ lon.tail() sumSquares(lon.tail()) ------------------------------------------ 4. when debugging, work bottom up ------------------------------------------ MORE TIPS 4. When debugging, work bottom up from subexpressions lon = Cons(3, Cons(2, Cons(2, Nil()))) lon.tail() lon.first() lon.first()**2 ... If you need to use a recursive call when debugging, use its value instead lon.first()**2 + 9 ------------------------------------------ D. deriving recursive programs from examples 1. example ------------------------------------------ DERIVATION FROM RELATED EXAMPLES Write a function sum2lists(ls1, ls2) that takes two LispLists of the same length and returns a list of the sums of the corresponding elements of each list. Examples: ls342 = Cons(3, Cons(4, Cons(2, Nil()))) ls597 = Cons(5, Cons(9, Cons(7, Nil()))) assert sum2lists(ls342, ls597) \ == Cons(8, Cons(13, Cons(9, Nil()))) assert sum2lists(ls342.tail(), ls597.tail()) \ == Cons(13, Cons(9, Nil())) assert sum2lists(Cons(2, Nil()), Cons(7, Nil())) \ == Cons(9, Nil()) assert sum2lists(Nil(), Nil() == Nil() ------------------------------------------ What's a related simpler example for the second example? What's the type? What are the cases? What's the outline? What is the answer for the base cases? How to decompose? Where is the recursion? How do we get from the answer for the recursion to what we want? 2. trace ------------------------------------------ HOW IT WORKS sum2lists(ls342, ls597) == if ls342.isEmpty(): return Nil() else: return Cons(ls342.first() + ls597.first(), \ sum2lists(ls342.tail(), ls597.tail())) == Cons(ls342.first() + ls597.first(), \ sum2lists(ls342.tail(), ls597.tail())) == Cons(3 + 5, sum2lists(Cons(4, Cons(2, Nil())), \ Cons(9, Cons(7, Nil())))) == Cons(8, sum2lists(Cons(4, Cons(2, Nil())), \ Cons(9, Cons(7, Nil())))) == Cons(8, Cons(4 + 9, sum2lists(Cons(2, Nil())) Cons(7, Nil())) == Cons(8, Cons(13, Cons(2+7, sum2lists(Nil(), Nil())))) == Cons(8, Cons(13, Cons(9, sum2lists(Nil(), Nil())))) == Cons(8, Cons(13, Cons(9, Nil()))) ------------------------------------------ 3. try it ------------------------------------------ FOR YOU TO DO Write a function xerox(lst), of type LispList(int) -> LispList(int) that duplicates each element in lst. For example: xerox(Cons(3, Cons(9, Cons(2, Nil())))) \ == Cons(3, Cons(3, Cons(9, Cons(9, Cons(2, Cons(2, Nil())))))) xerox(Cons(9, Cons(2, Nil()))) \ == Cons(9, Cons(9, Cons(2, Cons(2, Nil())))) xerox(Nil()) == Nil() ------------------------------------------ What's a related example to the first example? What's the type? How do you make [3, 3, 9, 9, 2, 2] from [3, 9, 2] and [9, 9, 2, 2]? E. more complex cases 1. give yourself examples to cover all cases ------------------------------------------ GIVE YOURSELF EXAMPLES TO COVER ALL CASES Write a function insertBefore(where, what, lst) that takes a LispList(t) and two t values where and what and returns a copy of lst with what inserted just before the first occurrence of where, if any. Examples: insertBefore(7, 3, Cons(7, Cons(7, Nil()))) \ == Cons(3, Cons(7, Cons(7, Nil()))) insertBefore(5, 2, Cons(1, Cons(2, Cons(3, Nil())))) \ == Cons(1, Cons(5, Cons(2, Cons(3, Nil())))) insertBefore(5, 2, Cons(3, Nil()) == Cons(3, Nil()) ------------------------------------------ What examples are we missing? Which argument has an inductively-specified type? 2. example for you ------------------------------------------ FOR YOU TO DO (IN PAIRS) Write a function insertBeforeAll(where, what, lst) that takes a LispList(t) and two t values where and what and returns a copy of lst with what inserted just before the first occurrence of where, if any. nil = Nil() insertBeforeAll(9, 5, nil) == nil insertBeforeAll(6, 7, Cons(6, Cons(1, Cons(2, nil)))) insertBeforeAll(6, 7, Cons(5, Cons(6, Cons(1, Cons(2, nil))))) Any other examples you want? What's the type? What's the structure of the code? Write it! ------------------------------------------ How does insertBeforeAll differ from insertBefore? IV. Friday problems on recursion over Lisp Lists A. a reduction ------------------------------------------ FOR YOU TO DO 1. Write a function, multList(lst) of type: LispList(int) -> int that returns the product of all the ints in the argument, lst. Examples: assert multList(Nil()) == 1 assert multList(Cons(2, Nil())) == 2 assert multList(Cons(3, Cons(2, Nil()))) == 6 assert multList(Cons(50, Cons(10, \ Cons(10, Cons(100, Nil()))))) == 500000 assert multList(Cons(5, Cons(0, Cons(7, Nil())))) == 0 ------------------------------------------ B. a filter ------------------------------------------ FOR YOU TO DO 2. Write a function, oddElems(lst) of type: LispList(int) -> LispList(int) that returns a list that has only the odd elements of lst (in their original order). Examples: assert oddElems(Nil()) == Nil() assert oddElems(Cons(1, Cons(2, Cons(3, Nil())))) \ == Cons(1, Nil()) assert oddElems(Cons(0, Cons(1, Cons(2, Cons(3, Nil()))))) \ == Cons(1, Cons(3, Nil())) assert oddElems(Cons(5, Cons(5, Cons(7, Nil())))) \ == Cons(5, Cons(5, Cons(7, Nil()))) assert oddElems(Cons(4, Cons(6, Cons(4, Nil())))) \ == Nil() ------------------------------------------ C. a map ------------------------------------------ FOR YOU TO DO 3. Write a function, cubeAll(lst) of type: LispList(int) -> LispList(int) that returns a list that has each element of lst replaced by its cube. Examples assert cubeAll(Nil()) == Nil() assert cubeAll(Cons(5, Nil())) == Cons(125, Nil()) assert cubeAll(Cons(-1, Cons(3, Cons(1, Nil()))) \ == Cons(-1, Cons(27, Cons(1, Nil()))) ------------------------------------------ D. working with 2 lists ------------------------------------------ FOR YOU TO DO 4. Write a function, append(lst1, lst2) of type: (LispList(t), LispList(t)) -> LispList(t) that returns a list that has the elements of lst1 followed by the elements of lst2. Examples: assert append(Nil(), Cons(1, Nil())) == Cons(1, Nil()) assert append(Cons(2, Nil()), Cons(-1, Nil())) \ == Cons(2, Cons(-1, Nil())) assert append(Cons(5, Cons(2, Nil()), Cons(-1, Nil()))) \ == Cons(5, Cons(2, Cons(-1, Nil()))) assert append(Cons(5, Cons(2, Nil()), Cons(2, Cons(-1, Nil())))) \ == Cons(5, Cons(2, Cons(2, Cons(-1, Nil())))) ------------------------------------------