I. Sequences in Python A. concept of sequences in Python ------------------------------------------ SEQUENCES IN PYTHON A concept, or application programming interface (API), Idea: Main built in sequence types: lists, strings, tuples, ranges lists include strings Subtypes of collections.abc.Sequence ------------------------------------------ How long does it take to access the 10th element of a LispList? How long does it take to access the first element of a LispList? ------------------------------------------ COMMON OPERATIONS ON SEQUENCE OBJECTS len(), e.g., len("hi") indexing, e.g., "hi"[1] membership test, e.g., 'i' in "hi" slicing, e.g., "Drump"[1:4] concatenation, e.g., "hi" + " there" ------------------------------------------ Why isn't the ending index included in a slice? B. tuples ------------------------------------------ PYTHON TUPLES Immutable collections of possibly heterogeneous elements Construction: >>> etup = () >>> etup >>> rbwo = ("RBWO", 5, True, [3, 4]) >>> rbwo Indexing: >>> rbwo[0] ------------------------------------------ C. ranges ------------------------------------------ PYTHON RANGES immutable sequences of numbers Construction: >>> range(5) >>> to6 = range(0,6) >>> to6 Indexing: >>> to6[0] >>> to6[1] >>> to6[5] ------------------------------------------ D. lists ------------------------------------------ PYHTHON'S BUILT-IN LISTS Finite, mutable sequences of elements Construction: list elements in square brackets: >>> emp = [] >>> emp >>> lst012 = [0,1,2] >>> lst012 >>> lst3223 = [1+2, 0+2, 1+1, 2+1] >>> lst3223 >>> list(range(0,5)) Indexing: >>> lst3223[0] >>> lst3223[1] >>> lst3223[3] Slicing: >>> lst3223[1:4] >>> lst3223[0:3] Concatenation: >>> lst012 + lst3223 ------------------------------------------ ------------------------------------------ PYTHON LISTS ARE MUTABLE >>> two = 2 >>> lst3223 = [1+two, 0+two, 1+1, two+1] >>> lst3223[0] = 5 >>> lst3223 >>> lst3223[1] = 4 >>> lst3223 ------------------------------------------ II. Loop Statements in Python A. while loops ------------------------------------------ WHILE LOOPS IN PYTHON Syntax: ::= while : | ... Example Semantics: ------------------------------------------ ------------------------------------------ SYNTAX IN C ::= while () | { } | ... Example: int i = 0; while (i <= num) { printf("%n\n", i); i = i+1; } ------------------------------------------ 1. writing while loops ------------------------------------------ REASONING ABOUT WHILE LOOPS What is true after successfully executing? assert I while P: S assert I ------------------------------------------ a. method 1: deleting a conjunct ----------------------------------------- EXAMPLE LOOP: INT DIVISION Compute the quotient (quot) and remainder (rem) of x divided by y, without using // and % Precondition: x>= 0 and y > 0 Desired postcondition: 0 <= rem and rem < y and quot*y + rem == x Heuristic: ------------------------------------------ ------------------------------------------ CODING THE EXAMPLE def int_division(x, y): """Requires: x >= 0 and y > 0 Ensures, result is a pair (quot, rem) such that 0 <= rem and rem < y and quot*y+rem == x.""" assert x >= 0 and y > 0 assert 0 <= rem and rem < y and quot*y+rem == x return (quot, rem) ------------------------------------------ How can we achieve that last assertion? Why? ------------------------------------------ INITIALIZATION assert x >= 0 and y > 0 assert 0 <= rem and quot*y+rem == x ------------------------------------------ How can we assign to quot and rem to make the assertion true? ------------------------------------------ LOOP BODY assert 0 <= rem and quot*y+rem == x while rem >= y: # decrease rem while maintaining the invariant assert 0 <= rem and quot*y+rem == x ------------------------------------------ Why should we decrease rem? Why do we need to maintain the loop invariant? What can we change about quot and rem that will help? b. method 2: introduce new (local) variables ------------------------------------------ LINEAR SEARCH EXAMPLE Write a function, findname(schools, name), that, given a (Python) list of strings, schools, returns the index, i, of the first element in schools such that schools[i] == name, or if no such i exists, then return -1. type: (list(string), string) -> int Ensures: ------------------------------------------ How do we say that the result is the first index? How do we make that disjunction into a conjunction? ------------------------------------------ GOAL-DIRECTED DEVELOPMENT Overall outline will be: def findname(schools, name): """Ensures: result < len(schools) and allIntsIn(range(0,result), (lambda i: implies(i < result, schools[i] != name))) and implies(0 <= result, schools[result] == name) and implies(result == -1, allIntsIn(range(0,len(schools)), (lambda i: schools[i] != name)))""" if ___________________: return -1 else: return ______ ------------------------------------------ What has to be true when returning -1? What has to be true when returning some other number? ------------------------------------------ NAMING THE INDEX i def findname(schools, name): """Ensures: result < len(schools) and allIntsIn(range(0,result), (lambda i: implies(i < result, schools[i] != name))) and implies(0 <= result, schools[result] == name) and implies(result == -1, allIntsIn(range(0,len(schools)), (lambda i: schools[i] != name)))""" i = _______ if i == len(schools): assert i == len(schools) assert allIntsIn(range(0, i), (lambda i: schools[i] != name)) return -1 else: assert i < len(schools) and schools[i] == name assert allIntsIn(range(0, i), (lambda i: schools[i] != name)) return i ------------------------------------------ What must be true before the if for that to work? How can we achieve that condition? Which part should be the invariant? Which conjunct should be the guard? ------------------------------------------ def findname(schools, name): """Ensures: result < len(schools) and allIntsIn(range(0,result), (lambda i: implies(i < result, schools[i] != name))) and implies(0 <= result, schools[result] == name) and implies(result == -1, allIntsIn(range(0,len(schools)), (lambda i: schools[i] != name)))""" i = __________ assert allIntsIn(range(0,i), (lambda i: schools[i] != name)) \ and i <= len(schools) while i < len(schools) and schools[i] != name: # change i towards len(schools) while preserving invariant assert allIntsIn(range(0,i), (lambda i: schools[i] != name)) \ and i <= len(schools) assert i >= len(schools) or schools[i] == name \ and allIntsIn(range(0,i), (lambda i: schools[i] != name)) and i <= len(schools) if i >= len(schools): assert i == len(schools) assert allIntsIn(range(0,i), (lambda i: schools[i] != name)) return -1 else: assert allIntsIn(range(0, i), (lambda i: schools[i] != name)) assert schools[i] == name return i ------------------------------------------ What can we assign to i that makes the invariant true? How can we change i towards len(schools) when the guard is true, while preserving the invariant? B. break statements ------------------------------------------ BREAK STATEMENT Syntax: ::= break | ... Static semantics: A break statement must occur inside a while or for loop Example: def multPyList(lst): """type: list(number) -> number Ensures: result is the product of the numbers in lst.""" prod = 1 i = 0 while i < len(lst): prod = prod * lst[i] if lst[i] == 0: break else: i = i + 1 return prod Semantics: ------------------------------------------ C also has a break statment, what would it's syntax look like? ------------------------------------------ REASONING ABOUT LOOPS WITH BREAK What is true after executing a break? assert I while P: S1 if P2: assert P2 S2 assert Q break S3 assert I assert ------------------------------------------ What if P is True? What is S2 is not present? C. for loops in Python ------------------------------------------ FOR LOOPS Syntax: ::= for in : | ... Example: Semantics: ------------------------------------------ ------------------------------------------ SYNTAX IN C ::= for ( ; ; ) Example: int num; num = myinput(); for (int i = 0; i < num; i++) { printf("%n\n", i); } ------------------------------------------ 1. reasoning about for loops ------------------------------------------ REASONING ABOUT FOR LOOPS OVER LISTS Let R be a sequence expression (of type list) which has precondition R_pre Let _r, _already, and _rest be fresh identifiers Let I be a function of type: list -> bool assert R_pre _r = R _already = [] _rest = r assert I(_already) for X in R: assert _r == _already + [X] + _rest[1:] S _already = _already + [X] _rest = _rest[1:] I(_already) assert I(_r) and _already == _r ------------------------------------------ ------------------------------------------ EXAMPLE Function product(lst) of type: list(number) -> number that returns the product of all the elements of lst. ------------------------------------------ What should we return? What should that be initialized to? How can we make the last condition true? D. continue statements ------------------------------------------ CONTINUE STATEMENT Syntax: ::= continue | ... Static semantics: A continue statement must occur inside a while or for loop, but not inside a finally clause inside that loop Example: Semantics: ------------------------------------------ What would you guess the continue statement's syntax is in C? ------------------------------------------ REASONING ABOUT LOOPS WITH CONTINUE What is true after executing a continue? _r = R _already = [] _rest = _r assert I(_already) for X in _r: assert _r == _already + [X] + _rest S1 if P2: assert P2 S2 _already = _already + [X] _rest = _rest[1:] assert I(_already) continue S3 _already = _already + [X] _rest = _rest[1:] assert I(_already) assert I(_already) and (_already == _r or Q) ------------------------------------------ So, what has to be true when the code continues? III. Friday problems on while and for loops A. Another for loop example ------------------------------------------ FOR YOU TO DO Using a for loop, write a function product(lst), of type: list(number) -> number that returns the product of all the numbers in lst. The following are examples: assert product([]) == 1 assert product([2,3]) == 6 assert product([1,2,3,4]) == 1 * 2 * 3 * 4 assert product([10,5,1]) == 50 assert product([3,2,2,3]) == 9 * 4 assert product([10] + [3,2,2,3]) == 10*9*4 ------------------------------------------ B. An example where continue may be used in a for loop ------------------------------------------ FOR YOU TO DO Using a for loop and continue, write a function sumReciprocals(lst) of type: list(number) -> float that returns the sum of the numbers 1/x for all x in lst that are not zero. The following are examples: from math import isclose assert isclose(sumReciprocals([]), 0.0) assert isclose(sumReciprocals([0]), 0.0) assert isclose(sumReciprocals([1,0]), 1.0) assert isclose(sumReciprocals([1,2,0]), 1/1 + 1/2) assert isclose(sumReciprocals([3,2,2,3]), 1/3 + 1/2 + 1/2 + 1/3) assert isclose(sumReciprocals([3,0,2,0,0,2,0,3,0]), 1/3 + 1/2 + 1/2 + 1/3) ------------------------------------------ C. An example with less direction ------------------------------------------ FOR YOU TO DO Without using the ** operator of Python or recursion, write a function exp(n, pow) of type: (int, int) -> int where pow >= 0, so that the result is n**pow. For example, assert exp(5,0) == 1 assert exp(2,3) == 2*2*2 assert exp(2,2) == 4 assert exp(2,1) == 2 assert exp(5,3) == 5*5*5 assert exp(10,4) == 10*10*10*10 assert exp(9,5) == 9*9*9*9*9 assert (-3,3) == -3 * -3 * -3 ------------------------------------------