Saturday, July 7, 2018

Python Flow Controls


This includes conditional statements and loops which are common flow controls
in any programming language.

`while` loop
------------

Executes a command while condition
is still satisfied.
# from previous example
>>> while b < 10:
...   print(b, end=',')
...   b = b + 1
...
0,1,2,3,4,5,6,7,8,9,>>>
>>>

`if` statements
---------------

Most well-known statment type in all
programming languages.

There can be one or more `elif` and
the `else` part is optional.

`elif` is short for `else if`
>>> x = int(input("Please enter an integer: "))
Please enter an integer: 42
>>> if x < 0:
...     x = 0
...     print('Negative changed to zero')
... elif x == 0:
...     print('Zero')
... elif x == 1:
...     print('Single')
... else:
...     print('More')
...
Multiple conditions in newlines
if condition1 == condition2 or \
   condition1 == condition2 or \
   ...
   conditionN == conditionNL:
   ...

if (cond1 == 'val1' and cond2 == 'val2' and
       cond3 == 'val3' and cond4 == 'val4'):
    do_something
Testing multiple flags at once
# Different ways to test multiple
# flags at once in Python
x, y, z = 0, 1, 0

if x == 1 or y == 1 or z == 1:
    print('passed')

if 1 in (x, y, z):
    print('passed')

# These only test for truthiness:
if x or y or z:
    print('passed')

if any((x, y, z)):
    print('passed')

`for` statements
----------------

Pythons's `for` statement iterates
over items in sequences (lists or
strings) in order of their appearance.
>>> # Measure some strings:
>>> words = ['cat', 'window', 'defenestrate']
>>> for w in words:
...     print(w, len(w))
...
cat 3
window 6
defenestrate 12
To modify the sequence, loop over a
slice copy instead of looping over
individual items like in the example
above.
>>> for w in words[:]:  # Loop over a slice copy of the entire list.
...     if len(w) > 6:
...         words.insert(0, w)
...
>>> words
['defenestrate', 'cat', 'window', 'defenestrate']

# looping via every items and trying to edit the sequence will make
# the interpreter hang

>>> for w in words:
...     if len(w) > 6:
...         words.insert(0, w)
...
# hangs here...
looping backwards
Use range's "step" parameter (3rd one) to specify backward movement.

>>> l = '12345'
>>> for i in range(len(l), 0, -1):
...   print(i)
...
5
4
3
2
1
>>>

The `range()` function
----------------------

`range()` allows quick way to generate a
sequence of numbers. By default, start
point starts with 0.
>>> for i in range(5):  # sequence starts with 0 and ends with 4
...     print(i)
...
0
1
2
3
4
>>> range(10)
range(0, 10)  # *
>>> list(range(10))  # you can use list() to generate a list
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
It is possible to specify a start point
(other than 0) as well as an end point.
>>> list(range(5, 10))
[5, 6, 7, 8, 9]
>>>
You may also use increments (steps).
>>> list(range(0, 10, 3))
[0, 3, 6, 9]
>>> list(range(-10, -100, -30))
[-10, -40, -70]
>>>
Combining `rannge()` and `len()` can be
used to iterate over indices of a
sequence.
>>> a = ['Mary', 'had', 'a', 'little', 'lamb']
>>> for i in range(len(a)):
...     print(i, a[i])
...
0 Mary
1 had
2 a
3 little
4 lamb

* A strange thing happens if you just print a range:

>>> print(range(10))
range(0, 10)

In many ways the object returned by range() behaves as if it is a list, but in
fact it isn’t. It is an object which returns the successive items of the desired
sequence when you iterate over it, but it doesn’t really make the list, thus
saving space.

We say such an object is iterable, that is, suitable as a target for functions
and constructs that expect something from which they can obtain successive items
until the supply is exhausted. We have seen that the for statement is such an
iterator. The function list() is another; it creates lists from iterables:

>>> list(range(5))
[0, 1, 2, 3, 4]

`break` and `continue` statements, and `else` clauses on loops
--------------------------------------------------------------

The break statement, like in C, breaks out of the smallest enclosing for or
while loop.

Loop statements may have an else clause; it is executed when the loop terminates
through exhaustion of the list (with for) or when the condition becomes false
(with while), but not when the loop is terminated by a break statement. This is
exemplified by the following loop, which searches for prime numbers:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print(n, 'equals', x, '*', n//x)
...             break
...     else:
...         # loop fell through without finding a factor
...         print(n, 'is a prime number')
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3

(Yes, this is the correct code. Look closely: the else clause belongs to the for
loop, not the if statement.)

When used with a loop, the else clause has more in common with the else clause
of a try statement than it does that of if statements: a try statement’s else
clause runs when no exception occurs, and a loop’s else clause runs when no
break occurs.

The continue statement, also borrowed from C, continues with the next iteration
of the loop:

>>> for num in range(2, 10):
...     if num % 2 == 0:
...         print("Found an even number", num)
...         continue
...     print("Found a number", num)
Found an even number 2
Found a number 3
Found an even number 4
Found a number 5
Found an even number 6
Found a number 7
Found an even number 8
Found a number 9

`pass` statements
-----------------

`pass` does nothing. It can be used to prevent
syntax errors.
>>> while True:
...     pass  # Busy-wait for keyboard interrupt (Ctrl+C)
...
Also used for minimal classes.
>>> class MyEmptyClass:
...     pass
...
Another place pass can be used is as a place-holder for a
function or conditional body when you are working on new
code, allowing you to keep thinking at a more abstract
level. The pass is silently ignored.
>>> def initlog(*args):
...     pass   # Remember to implement this!
...

Looping Techniques
------------------

When looping through dictionaries, the key and corresponding value can
be retrieved at the same time using the items() method.
>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
>>> for k, v in knights.items():
...     print(k, v)
...
gallahad the pure
robin the brave
>>>
You can retrieve both index and element value of a list in a loop using
enumerate()
>>> for i, v in enumerate(['tic', 'tac', 'toe']):
...     print(i, v)
...
0 tic
1 tac
2 toe
>>>
Looping over multiple sequences can be achieve using zip()
>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
...     print('What is your {0}?  It is {1}.'.format(q, a))
...
What is your name?  It is lancelot.
What is your quest?  It is the holy grail.
What is your favorite color?  It is blue.
>>>
Reverses a sequence
>>> for i in reversed(range(1, 10, 2)):
...     print(i)
...
9
7
5
3
1
>>>
Sorts a sequence
>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> for f in sorted(set(basket)):
...     print(f)
...
apple
banana
orange
pear
>>>
This changes the original sequence during looping by making a slice
copy of the sequence first. Not making a slice copy will not change
the original sequence.
>>> words = ['cat', 'window', 'defenestrate']
>>> for w in words[:]:  # Loop over a slice copy of the entire list.
...     if len(w) > 6:
...         words.insert(0, w)
...
>>> words
['defenestrate', 'cat', 'window', 'defenestrate']
>>>

More on conditions
------------------

- `while` and `if` can contain any operators (not only comparisons)
- `in` and `not in` checks whether a value occurs/not occur inside a sequence
- `is` and `is not` compares if 2 mutable objects are same (e.g list)
- comparison operators have same priority
- comparison operators have lower priority than numerical operators
- comparisons can be chained
    e.g
      a < b == c
      a is less than b moreover equals c
- comparisons maybe combined using boolean operators `and` and `or`
- outcome of comparions may be negated with `not`
- `not` has higher priority than `and` and `or`
- `and` and `or` are also called "short-circuit" operators
    * values are evaluated from left to right
    * evaluation stops as soon as outcome is determined
    * e.g if "A" and "C" are true but "B" is false, `A and B and C` does not
          evaluate the expression C --> I don't understadnd this!!!
    * when used as a general value, the return value of a short-circuit
      operator is the last evaluated argument
- you can assign result of comparison to a variable
    * e.g
>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
>>> non_null = string1 or string2 or string3
>>> non_null
'Trondheim'
>>>
- assignment cannot occur inside expressions --> need more example on this!

Comparing Sequences and other Types
-----------------------------------

Sequence objects may be compared to other objects with the same sequence type.
The comparison uses lexicographical ordering: first the first two items are
compared, and if they differ this determines the outcome of the comparison; if
they are equal, the next two items are compared, and so on, until either
sequence is exhausted. If two items to be compared are themselves sequences of
the same type, the lexicographical comparison is carried out recursively. If all
items of two sequences compare equal, the sequences are considered equal. If one
sequence is an initial sub-sequence of the other, the shorter sequence is the
smaller (lesser) one. Lexicographical ordering for strings uses the Unicode
codepoint number to order individual characters. Some examples of comparisons
between sequences of the same type:

(1, 2, 3)              < (1, 2, 4)
[1, 2, 3]              < [1, 2, 4]
'ABC' < 'C' < 'Pascal' < 'Python'
(1, 2, 3, 4)           < (1, 2, 4)
(1, 2)                 < (1, 2, -1)
(1, 2, 3)             == (1.0, 2.0, 3.0)
(1, 2, ('aa', 'ab'))   < (1, 2, ('abc', 'a'), 4)

Note that comparing objects of different types with < or > is legal provided
that the objects have appropriate comparison methods. For example, mixed numeric
types are compared according to their numeric value, so 0 equals 0.0, etc.
Otherwise, rather than providing an arbitrary ordering, the interpreter will
raise a TypeError exception.

No comments:

Post a Comment