Control Flow Tools#

While loop#

  • Don’t forget the ‘:’ character.

  • The body of the loop is indented

# Fibonacci series:
# the sum of two elements defines the next
a, b = 0, 1
while b < 500:
    a, b = b, a+b
    print(round(b/a,5), end=",")
1.0,2.0,1.5,1.66667,1.6,1.625,1.61538,1.61905,1.61765,1.61818,1.61798,1.61806,1.61803,1.61804,

if Statements#

True, False, and, or, not, ==, is, !=, is not, >, >=, <, <=
x = 42
if x < 0:
    x = 0
    print('Negative changed to zero')
elif x == 0:
    print('Zero')
elif x == 1:
    print('Single')
else:
    print('More')
More

switch or case statements don’t exist in Python.

Exercise Collatz conjecture#

Consider the following operation on an arbitrary positive integer:

  • If the number is even, divide it by two.

  • If the number is odd, triple it and add one.

The conjecture is that no matter what initial value of this integer, the sequence will always reach 1.

  • Test the Collatz conjecture for n = 100000.

  • How many steps do you need to reach 1 ?

Loop over an iterable object#

We use for statement for looping over an iterable object. If we use it with a string, it loops over its characters.

for c in "python":
    print(c)
p
y
t
h
o
n
for word in "Python ENSAI september 7th 2020".split(" "):
    print(word, len(word))   
Python 6
ENSAI 5
september 9
7th 3
2020 4

Exercise: Anagram#

An anagram is word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.

Write a code that print True if s1 is an anagram of s2. To do it, remove every character present in both strings. Check you obtain two empty strings.

Hint: s = s.replace(c,"",1) removes the character c in string s one time.

s1 = "pascal obispo"
s2 = "pablo picasso"
..
True

Loop with range function#

  • It generates arithmetic progressions

  • It is possible to let the range start at another number, or to specify a different increment.

  • Since Python 3, the object returned by range() doesn’t return a list to save memory space. xrange no longer exists.

  • Use function list() to creates it.

list(range(5))
[0, 1, 2, 3, 4]
list(range(2, 5))
[2, 3, 4]
list(range(-1, -5, -1))
[-1, -2, -3, -4]
for i in range(5):
    print(i, end=' ')
0 1 2 3 4 

Exercise Exponential#

  • Write some code to compute the exponential mathematical constant \(e \simeq 2.718281828459045\) using the taylor series developed at 0 and without any import of external modules:

\[ e \simeq \sum_{n=0}^{50} \frac{1}{n!} \]

break Statement.#

for n in range(2, 10):     # n = 2,3,4,5,6,7,8,9
    for x in range(2, n):  # x = 2, ..., n-1
        if n % x == 0:     # Return the division remain (mod)
            print(n, " = ", x, "*", n//x)
            break
        else:
            print("%d is a prime number" % n)
            break
3 is a prime number
4  =  2 * 2
5 is a prime number
6  =  2 * 3
7 is a prime number
8  =  2 * 4
9 is a prime number

iter Function#

course = """ Python september 7, 14 2020 ENSAI Rennes """.split()
print(course)
['Python', 'september', '7,', '14', '2020', 'ENSAI', 'Rennes']
iterator = iter(course)
print(iterator.__next__())
Python
print(iterator.__next__())
september

Defining Function: def statement#

def is_palindromic(s):
    "Return True if the input sequence is a palindrome"
    return s == s[::-1]


is_palindromic("kayak")
True
  • Body of the function start must be indented

  • Functions without a return statement do return a value called None.

def fib(n):
    """Print a Fibonacci series up to n."""
    a, b = 0, 1
    while a < n:
         print(a, end=' ')  # the end optional argument is \n by default
         a, b = b, a+b
    print("\n") # new line
     
result = fib(2000)
print(result) # is None
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 

None

Documentation string#

  • It’s good practice to include docstrings in code that you write, so make a habit of it.

def my_function( foo):
     """Do nothing, but document it.

     No, really, it doesn't do anything.
     """
     pass

print(my_function.__doc__)
Do nothing, but document it.

     No, really, it doesn't do anything.
     
help(my_function)
Help on function my_function in module __main__:

my_function(foo)
    Do nothing, but document it.
    
    No, really, it doesn't do anything.

Default Argument Values#

def f(a,b=5):
    return a+b

print(f(1))
print(f(b="a",a="bc"))
6
bca

Important warning: The default value is evaluated only once.

def f(a, L=[]):
    L.append(a)
    return L

print(f(1))
[1]
print(f(2)) # L = [1]
[1, 2]
print(f(3)) # L = [1,2]
[1, 2, 3]

Function Annotations#

Completely optional metadata information about the types used by user-defined functions. These type annotations conforming to PEP 484 could be statically used by MyPy.

def f(ham: str, eggs: str = 'eggs') -> str:
     print("Annotations:", f.__annotations__)
     print("Arguments:", ham, eggs)
     return ham + ' and ' + eggs

f('spam')
help(f)
print(f.__doc__)
Annotations: {'ham': <class 'str'>, 'eggs': <class 'str'>, 'return': <class 'str'>}
Arguments: spam eggs
Help on function f in module __main__:

f(ham: str, eggs: str = 'eggs') -> str

None

Arbitrary Argument Lists#

Arguments can be wrapped up in a tuple or a list with form *args

def f(*args, sep=" "):
    print (args)
    return sep.join(args)

print(f("big","data"))
('big', 'data')
big data
  • Normally, these variadic arguments will be last in the list of formal parameters.

  • Any formal parameters which occur after the *args parameter are ‘keyword-only’ arguments.

Keyword Arguments Dictionary#

A final formal parameter of the form **name receives a dictionary.

def add_contact(kind, *args, **kwargs):
    print(args)
    print("-" * 40)
    for key, value in kwargs.items():
        print(key, ":", value)

*name must occur before **name

add_contact("John", "Smith",
           phone="555 8765",
           email="john.smith@python.org")
('Smith',)
----------------------------------------
phone : 555 8765
email : john.smith@python.org

Lambda Expressions#

Lambda functions can be used wherever function objects are required.

f = lambda x : 2 * x + 2
f(3)
8
taxicab_distance = lambda x_a,y_a,x_b,y_b: abs(x_b-x_a)+abs(y_b-y_a)
print(taxicab_distance(3,4,7,2))
6

lambda functions can reference variables from the containing scope:

def make_incrementor(n):
    return lambda x: x + n

f = make_incrementor(42)
f(0),f(1)
(42, 43)

Unpacking Argument Lists#

Arguments are already in a list or tuple. They can be unpacked for a function call. For instance, the built-in range() function is called with the *-operator to unpack the arguments out of a list:

def chessboard_distance(x_a, y_a, x_b, y_b):
    """
    Compute the rectilinear distance between 
    point (x_a,y_a) and (x_b, y_b)
    """
    return max(abs(x_b-x_a),abs(y_b-y_a))

coordinates = [3,4,7,2] 
chessboard_distance(*coordinates)
4

In the same fashion, dictionaries can deliver keyword arguments with the **-operator:

def parrot(voltage, state='a stiff', action='voom'):
     print("-- This parrot wouldn't", action, end=' ')
     print("if you put", voltage, "volts through it.", end=' ')
     print("E's", state, "!")

d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !

Exercise: Time converter#

Write 3 functions to manipulate hours and minutes :

  • Function minutes return minutes from (hours, minutes).

  • Function hours the inverse function that return (hours, minutes) from minutes.

  • Function add_time to add (hh1,mm1) and (hh2, mm2) two couples (hours, minutes). It takes 2 tuples of length 2 as input arguments and return the tuple (hh,mm).

print(minutes(6,15)) # 375 
print(minutes(7,46)) # 466 
print(add_time((6,15),(7,46)) # (14,01)

Functions Scope#

  • All variable assignments in a function store the value in the local symbol table.

  • Global variables cannot be directly assigned a value within a function (unless named in a global statement).

  • The value of the function can be assigned to another name which can then also be used as a function.

pi = 1.
def deg2rad(theta):
    pi = 3.14
    return theta * pi / 180.

print(deg2rad(45))
print(pi)
0.785
1.0
def rad2deg(theta):
    return theta*180./pi

print(rad2deg(0.785))
pi = 3.14
print(rad2deg(0.785))
141.3
45.0
def deg2rad(theta):
    global pi
    pi = 3.14
    return theta * pi / 180

pi = 1
print(deg2rad(45))
0.785
print(pi)
3.14

enumerate Function#

primes =  [1,2,3,5,7,11,13]
for idx, ele in enumerate (primes):
    print(idx, " --- ", ele) 
0  ---  1
1  ---  2
2  ---  3
3  ---  5
4  ---  7
5  ---  11
6  ---  13

Exercise: Caesar cipher#

In cryptography, a Caesar cipher, is one of the simplest and most widely known encryption techniques. It is a type of substitution cipher in which each letter in the plaintext is replaced by a letter some fixed number of positions down the alphabet. For example, with a left shift of 3, D would be replaced by A, E would become B, and so on.

  • Create a function cipher that take the plain text and the key value as arguments and return the encrypted text.

  • Create a funtion plain that take the crypted text and the key value as arguments that return the deciphered text.

zip Builtin Function#

Loop over sequences simultaneously.

L1 = [1, 2, 3]
L2 = [4, 5, 6]

for (x, y) in zip(L1, L2):
    print (x, y, '--', x + y)
1 4 -- 5
2 5 -- 7
3 6 -- 9

List comprehension#

  • Set or change values inside a list

  • Create list from function

lsingle = [1, 3, 9, 4]
ldouble = []
for k in lsingle:
    ldouble.append(2*k)
ldouble
[2, 6, 18, 8]
ldouble = [k*2 for k in lsingle]
[n*n for n in range(1,10)]
[1, 4, 9, 16, 25, 36, 49, 64, 81]
[n*n for n in range(1,10) if n&1]
[1, 9, 25, 49, 81]
[n+1 if n&1 else n//2 for n in range(1,10) ]
[2, 1, 4, 2, 6, 3, 8, 4, 10]

Exercise#

Code a new version of cypher function using list comprehension.

Hints:

  • s = ''.join(L) convert the characters list L into a string s.

  • L.index(c) return the index position of c in list L

  • "c".islower() and "C".isupper() return True

map built-in function#

Apply a function over a sequence.

res = map(hex,range(16))
print(res)
<map object at 0x7fc24c398c70>

Since Python 3.x, map process return an iterator. Save memory, and should make things go faster. Display result by using unpacking operator.

print(*res)
0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf

map with user-defined function#

def add(x,y):
    return x+y

L1 = [1, 2, 3]
L2 = [4, 5, 6]
print(*map(add,L1,L2))
5 7 9
  • map is often faster than for loop

M = range(10000)
f = lambda x: x**2
%timeit lmap = list(map(f,M))
2.76 ms ± 5.58 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
M = range(10000)
f = lambda x: x**2
%timeit lfor = [f(m) for m in M]
3.05 ms ± 9.26 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

filter#

creates a iterator of elements for which a function returns True.

number_list = range(-5, 5)
odd_numbers = filter(lambda x: x & 1 , number_list)
print(*odd_numbers)
-5 -3 -1 1 3
  • As map, filter is often faster than for loop

M = range(1000)
f = lambda x: x % 3 == 0
%timeit lmap = filter(f,M)
111 ns ± 0.0322 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
M = range(1000)
%timeit lfor = (m for m in M if m % 3 == 0)
286 ns ± 0.789 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

Exercise with map:#

Code a new version of your cypher function using map.

Hints:

  • Applied function must have only one argument, create a function called shift with the key value and use map.

Exercise with filter:#

Create a function with a number n as single argument that returns True if n is a Kaprekar number. For example 45 is a Kaprekar number, because $\(45^2 = 2025\)\( and \)\(20 + 25 = 45\)$

Use filter to give Kaprekar numbers list lower than 10000.

1, 9, 45, 55, 99, 297, 703, 999, 2223, 2728, 4879, 4950, 5050, 5292, 7272, 7777, 9999

Recursive Call#

def gcd(x, y): 
    """ returns the greatest common divisor."""
    if x == 0: 
        return y
    else : 
        return gcd(y % x, x)

gcd(12,16)

Exercises#

Factorial#

  • Write the function factorial with a recursive call

NB: Recursion is not recommended by Guido.

Minimum number of rooms required for lectures.#

Given an array of time intervals (start, end) for classroom lectures (possibly overlapping), find the minimum number of rooms required.

For example, given Input:

lectures = ["9:00-10:30", "9:30-11:30","11:00-12:00","14:00-18:00", "15:00-16:00", "15:30-17:30", "16:00-18:00"]

should output 3.

Non-palindromic skinny numbers#

non-palindromic squares remaining square when written backwards

\[\begin{split} \begin{array}{lclclcl} 10^2 &=& 100 &\qquad& 01^2 &=& 001 \\ 13^2 &=& 169 &\qquad& 31^2 &=& 961 \\ 102^2 &=& 10404 &\qquad& 201^2 &=& 40401 \end{array} \end{split}\]

Narcissistic number#

A number is narcissistic if the sum of its own digits each raised to the power of the number of digits.

Example : \(4150 = 4^5 + 1^5 + 5^5 + 0^5\) or \(153 = 1^3 + 5^3 + 3^3\)

Find narcissitic numbers with 3 digits

Happy number#

  • Given a number \(n = n_0\), define a sequence \(n_1, n_2,\ldots\) where \(n_{{i+1}}\) is the sum of the squares of the digits of \(n_{i}\). Then \(n\) is happy if and only if there exists i such that \(n_{i}=1\).

For example, 19 is happy, as the associated sequence is: $\( \begin{array}{ccccccl} 1^2 &+& 9^2 & & &=& 82 \\ 8^2 &+& 2^2 & & &=& 68 \\ 6^2 &+& 8^2 & & &=& 100 \\ 1^2 &+& 0^2 &+& 0^2 &=& 1 \end{array} \)$

  • Write a function ishappy(n) that returns True if n is happy.

  • Write a function happy(n) that returns a list with all happy numbers < \(n\).

happy(100) = [1, 7, 10, 13, 19, 23, 28, 31, 32, 44, 49, 68, 70, 79, 82, 86, 91, 94, 97]

Longuest increasing subsequence#

Given N elements, write a program that prints the length of the longuest increasing subsequence whose adjacent element difference is one.

Examples:

a = [3, 10, 3, 11, 4, 5, 6, 7, 8, 12]
Output : 6
Explanation: 3, 4, 5, 6, 7, 8 is the longest increasing subsequence whose adjacent element differs by one.
Input : a = [6, 7, 8, 3, 4, 5, 9, 10]
Output : 5
Explanation: 6, 7, 8, 9, 10 is the longest increasing subsequence

Polynomial derivative#

  • A Polynomial is represented by a Python list of its coefficients. [1,5,-4] => \(1+5x-4x^2\)

  • Write the function diff(P,n) that return the nth derivative Q

  • Don’t use any external package 😉

diff([3,2,1,5,7],2) = [2, 30, 84]
diff([-6,5,-3,-4,3,-4],3) = [-24, 72, -240]