Lecture 3: Control and recursion
Introduction to programming (LT2111)
Markus Forsberg SprΣakbanken University of Gothenburg
2011-09-20
Conditionals: pitfall
if ’tree’.isupper:
print "strange, ’tree’ is not uppercase."
=>
strange, ’tree’ is not uppercase.
>>> ’tree’.isupper
<built-in method isupper of str object at 0x7fc4bd90de10>
>>> ’tree’.isupper() False
Local variables
I
Variables defined in a function, is local to that function.
name = ’global’
def function():
name = ’local’
>>> function()
>>> name
’global’
I
Calling function() does not change the value of the
global name.
Local variables cont.
I
However, we can change the content of mutable types, such as lists.
name = [’global’,’global’]
def function():
name[0] = ’local’
>>> function()
>>> name [’local’, ’global’]
I
But we are not able to change what name refers to.
tuples
I
An immutable data structure used to group values.
I
Works like a list except that we have no way of changes the values.
>>> name = (1,2)
>>> type(name)
<type ’tuple’>
>>> (m,n) = name
>>> m 1
>>> n
2
Control and Recursion
I
Control is about what is available to control the flow of execution, e.g., conditionals and loops.
I
Recursion is a way of defining a function where its definition includes a call to itself.
I
Recursion can be thought of as a kind of loop.
While loop
I
We use while when we want to loop based on a condition.
while CONDITION:
CODE
x = 0 while x < 5:
print x x = x + 1
=>
0 1 2 3 4
I
If you are not careful with the condition, you end up
with an infinite loop.
break and continue
I
We use break to exit a loop, and continue to skip a step.
x = 10 while True:
x = x-1 if x == 0:
break
>>> x 0
for x in range(10):
if x%2 == 0:
continue print x
=>
1 3 5 7 9
Calling a function in another function
I
Every time a function is called, a function frame is created containing local variables and parameters.
I
When we call another function, we put the current function frame on a stack.
I
Think of it as a stack of notes, where the top note
describe the function we are currently at.
Recursion
I
A recursive function is a function that calls itself.
I
A recursive function needs a termination condition, a base case, or we get infinite recursion.
def countdown(n):
if n <= 0:
print ’Blastoff!’
else:
print n
countdown(n-1)
Recursion and mathematics
I
Mathematical formulas are typically defined recursively, e.g., factorial, n!:
0! = 1
n! = n ∗ (n − 1)!
def factorial(n):
if n==0:
return 1 else:
return n*factorial(n-1)
Python and Recursion
I
Recursion is not well supported by Python, the limit of the number of function frames on the stack is by default 1000.
>>> factorial(999) ...
RuntimeError: maximum recursion depth exceeded
I
Instead, we mostly use loops in Python.
I
(for the interested: the limit can be changed with
sys.setrecursionlimit).
Factorial as a loop
def factorial(number):
result = 1
for n in range(1,number+1):
result = n*result return result
>>> factorial(1000)
HUGE NUMBER
String formatting
I
String formatting is an elegant way of creating new strings. We avoid building complex strings with concatenation.
I
format % value(s)
I
format is a string with holes in it and value(s) are hole fillers.
>>> s = ’this %s string %s’ % (’is’,’formatting’)
>>> s
’this is string formatting’
List comprehension
I
List comprehension is a convenient way of defining new lists.
I
We use looping and conditions to define the values of the list.
>>> range(1,10) [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [n*2 for n in range(1,10)]
[2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> [n*2 for n in range(1,10) if n % 3 == 0]
[6, 12, 18]
>>> [(x,y) for x in range(1,5) for y in range(1,5)]
[(1, 1), (1, 2), (1, 3), (1, 4), (2, 1), (2, 2), (2, 3), (2, 4), (3, 1), (3, 2), (3, 3), (3, 4), (4, 1), (4, 2), (4, 3), (4, 4)]
Module with test code
# test_code.py def print_hello():
print ’hello’
if __name__ == ’__main__’:
print_hello()
>>> import test_code
>>> test_code.print_hello() hello
$ python test_code.py
hello
Comments
I
Comments are used to inactivate code:
# def function():
# print ’hello’
I
and, more importantly, to explain code:
x = x+1 # increment x by 1 NO!
x = x+1 # keep track of number of entries YES!
num_of_entries = num_of_entries + 1 # self-documenting name
Module interface
I
A module interface gives an overview of what a module contains without giving any implementation details.
# test.py
def print_name(name):
print name
>>> import test
>>> help(test) ...
>>> dir(test) [ ... , ’print_name’]
Adding documentation to the interface
I
We use documentation strings to add documentation to the interface.
I
A documentation string is just ordinary strings placed on specific places in the code.
# test.py
"""This is a test module"""
def print_name(name):
"""Doc-string for print_name"""
print name
>>help(test)
Escape characters
I
We use escape characters to write special characters, such as newlines, and to cancel a character’s formal meaning.
>>> print "tab: \t newline: \n next line"
tab: newline:
next line
>>> print "\" is an escaped double quote"
" is an escaped double quote
>>> print ’\’ is an escaped single quote’
’ is an escaped single quote
Characters in a computer
I
There are no characters in a computer, only numbers representing characters.
I
Character encoding: what numbers are used to represent what characters.
I
We have to know the character encoding of a text to
be able to display a text properly.
ASCII table
I
In the beginning was English (7 bits = 128
characters):
Extended ASCII table (latin1)
I
And then they though of other languages close by
and some symbols (8 bits = 256 characters):
Unicode
I
And then they decided to go for every character.
I
Every character gets a number. However, this
mapping is abstract, not the actually character
encoding.
Unicode character encodings
I
Encodings to represent Unicode: UTF-8, UTF-16, UTF-32
I
UTF-8 is popular since 1-127 are the same as ASCII (English text is valid).
I
UTF-8 has started to replace Latin1 as the default
encoding.
Unicode in Python
I
Python has a special kind of strings called unicode strings.
>> type(u’this is a unicode string’)
<type ’unicode’>
I
We use s.decode(ENCODING), to decode a string s encoded with ENCODING into a Python unicode string.
I
We use s.encode(ENCODING) for doing the reverse.
Reading a file with Cyrillic characters
with open(’cyrillic.txt’) as f:
s = f.read()
>>> print s
Письмо В июне 1935 года я на полгода вернулся в Англию с моего ранчо в Южной Америке. Жизнь ...
>>> print type(s), ’ length:’, len(s)
<type ’str’> length: 637
>>> print s.upper() # doesn’t work
Письмо В июне 1935 года я на полгода вернулся в Англию с моего ранчо в Южной Америке. Жизнь в Америке сложилась для нас непросто. Вместе со всеми мы страдали от последствий мирового кризиса. В Англии у меня был ряд дел, которые, как мне представлялось, требовали для своего разрешения моего личного присутствия. Моя жена осталась управлять нашим ранчо.
Reading a file with Cyrillic characters (cont.)
>>> s_unicode = s.decode(’UTF-8’)
>>> print type(s_unicode), ’ length:’, len(s_unicode)
<type ’unicode’> length: 353
>>> print s_unicode.upper() # works!
ПИСЬМО В ИЮНЕ 1935 ГОДА Я НА ПОЛГОДА ВЕРНУЛСЯ В АНГЛИЮ С МОЕГО РАНЧО В ЮЖНОЙ АМЕРИКЕ. ЖИЗНЬ В АМЕРИКЕ СЛОЖИЛАСЬ ДЛЯ НАС НЕПРОСТО. ВМЕСТЕ СО ВСЕМИ МЫ СТРАДАЛИ ОТ ПОСЛЕДСТВИЙ МИРОВОГО КРИЗИСА. В АНГЛИИ У МЕНЯ БЫЛ РЯД ДЕЛ, КОТОРЫЕ, КАК МНЕ ПРЕДСТАВЛЯЛОСЬ, ТРЕБОВАЛИ ДЛЯ СВОЕГО РАЗРЕШЕНИЯ МОЕГО ЛИЧНОГО ПРИСУТСТВИЯ. МОЯ ЖЕНА ОСТАЛАСЬ УПРАВЛЯТЬ НАШИМ РАНЧО.
>>> with open(’cyrillic_upper.txt’,mode=’w’) as f:
... f.write(s_unicode.upper()) ...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
UnicodeEncodeError: ’ascii’ codec can’t encode characters in position 0-5: ordinal not in range(128)
Reading a file with Cyrillic characters (cont.)
s_unicode_utf8 = s_unicode.upper().encode(’UTF-8’)
with open(’cyrillic_upper.txt’,mode=’w’) as f:
f.write(s_unicode_utf8)
$ cat rus_upper.txt
ПИСЬМО В ИЮНЕ 1935 ГОДА Я НА ПОЛГОДА ВЕРНУЛСЯ В АНГЛИЮ С МОЕГО РАНЧО В ЮЖНОЙ АМЕРИКЕ. ЖИЗНЬ В АМЕРИКЕ СЛОЖИЛАСЬ ДЛЯ НАС НЕПРОСТО. ВМЕСТЕ СО ВСЕМИ МЫ СТРАДАЛИ ОТ ПОСЛЕДСТВИЙ МИРОВОГО КРИЗИСА. В АНГЛИИ У МЕНЯ БЫЛ РЯД ДЕЛ, КОТОРЫЕ, КАК МНЕ ПРЕДСТАВЛЯЛОСЬ, ТРЕБОВАЛИ ДЛЯ СВОЕГО РАЗРЕШЕНИЯ МОЕГО ЛИЧНОГО ПРИСУТСТВИЯ. МОЯ ЖЕНА ОСТАЛАСЬ УПРАВЛЯТЬ НАШИМ РАНЧО.
Reading and writing UTF-8 files
def read_file_utf8(filename):
with open(filename) as f:
s = f.read()
return s.decode(’UTF-8’)
def write_file_utf8(filename,unicode_str):
utf8_str = unicode_str.encode(’UTF-8’) with open(filename,mode=’w’) as f:
f.write(utf8_str)
codecs module
import codecs
def read_file_utf8(filename):
with codecs.open(filename,encoding=’utf-8’) as f:
s = f.read() return s
def write_file_utf8(filename,unicode_str):
with codecs.open(filename,mode=’w’,encoding=’utf-8’) as f:
f.write(unicode_str)
String literals
# unicode_ex.py
alphabet_murders = u"Письмо В июне 1935 года я на полгода вернулся\
в Англию с моего ранчо в Южной Америке."
>>> import unicode_ex Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "unicode_ex.py", line 3
SyntaxError: Non-ASCII character ’\xd0’ in file unicode_ex.py on line 3, but no encoding declared;
see http://www.python.org/peps/pep-0263.html for details
# unicode_ex.py
# -*- coding: utf-8 -*-
alphabet_murders = u"Письмо В июне 1935 года я на полгода вернулся\
в Англию с моего ранчо в Южной Америке."
Solving a problem
Top-down (divide-and-conquer) Start with the top function, and analyze what needs to be done by giving subtasks function names (without defining the functions first). Iteratively continue with the subtasks until you end up with trivial subtasks that you solve right away.
Bottom-up Start with writing help functions that solves
problems that you know must be solved in order
to solve the whole problem. Use the help
functions to build new functions until you have
solved the whole problem.
What is a good solution?
I
A problem of some complexity has a large number of solutions, so which one is to be preferred?
I
Readability is extremely important. Scary example of the opposite:
print filter(None,map(lambda y:y*reduce(
lambda x,y:x*y!=0,map(lambda x,y=y:y%x, range(2,int(pow(y,0.5)+1))),1),range(2,1000)))
I
Using the idioms of current programming language.
I
Reasonable efficiency.
I
Reusability.
Zen of Python
>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren’t special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you’re Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it’s a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let’s do more of those!