# 1 05-Branches

Previous: 04-ExpressionsTypes.html

## 1.1 Screencasts

• Password for the Vimeo videos is in Zulip chat.
• https://vimeo.com/509924016
• Tip: If anyone want to speed up the lecture videos a little, inspect the page, go to the browser console, and paste this in:
`document.querySelector('video').playbackRate = 1.2`

* http://scipy-lectures.org/intro/language/control_flow.html
* https://automatetheboringstuff.com/2e/chapter2/
* https://books.trinket.io/pfe/03-conditional.html
* https://docs.python.org/3/tutorial/controlflow.html#if-statements
* https://python.swaroopch.com/control_flow.html
* https://www.learnpython.org/en/Conditions
* https://www.tutorialspoint.com/python3/python_decision_making.htm

## 1.3 Boolean algebra

• Actually complete this tutorial!
https://ryanstutorials.net/boolean-algebra-tutorial/

## 1.4 Control flow

https://en.wikipedia.org/wiki/Control_flow
* Control flow (or flow of control) manipulates the order in which individual statements, instructions or function calls of an imperative program are executed or evaluated.

### 1.4.1 Conditional branching

https://en.wikipedia.org/wiki/Control_flow#Choice
https://en.wikipedia.org/wiki/Conditional_(computer_programming)

• The code you have seen so far executes in linear sequence.
• Conditional branching enables executing blocks of sequences in different order, depending on the value of an expression or user input, for example.

## 1.5 if else

### 1.5.1 Simple if

``````if expression
statement-inside
statement-outside``````
• Expression is called a conditional expression, which is any syntactically correct python expression that evaluates to true or false or a number
• If the expression evaluates to true (non-zero),
• then ‘statement-inside’ it will be executed, and only ‘statement-outside’ is executed.
• otherwise ‘statement-inside’ is skipped and only ‘statement-outside’ is executed.
• if and else are reserved words and begin with lower case.
• statements are either simple (single) statements, or a compound statement.
• A compound statement is a sequence of statements all of which are to be executed in order.

#### 1.5.1.1 if else

``````if expression
statement-block1
else
statement-block2``````
• If the expression evaluates to true (non-zero),
• then the ‘statement-block1’ is executed, and ‘statement-block2’ is not executed
• If the expression evaluates to false, the else statement is executed if it exists.
• else ‘statement-block1’ is skipped and ‘statement-block2’ is executed.

#### 1.5.1.2 Nested if else

``````if expression
if expression1
statement-block1
else
statement-block2
else
statement-block3``````
• if ‘expression’ is false the ‘statement-block3’ will be executed, otherwise it continues to perform the test for ‘expression 1’ .
• If the ‘expression 1’ is true the ‘statement-block1’ is executed otherwise ‘statement-block2’ is executed.

The general form of else-if ladder is,

``````if expression 1
statement-block1
elif expression2
statement-block2
elif expression3
statement-block3
else
default-statement``````
• The expression is tested from the top (of the ladder) downwards.
• As soon as the true condition is found, the statement associated with it is executed.

#### 1.5.1.4 General points to remember

• The == operator (equality) must be used for comparison in the expression of if condition, if you use = (assignment) the expression may return true, because it succeeded in performing assignment, not comparison (some languages only).
• Other than 0 (zero), all other values are considered as true.

### 1.5.2 Why use conditions?

With just the syntax/tools we have so far, you could program a mechanic or a physician:

* Is this AI?
* Was it considered AI?

## 1.6 Conditional branching in Python

``````#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
An if-else construct implements branching.
Below, if  x < 0 is true, the first branch executes,
outputting "Negative".
Else, the second branch executes,
outputting "Non-negative".

General form:
if [condexpr]:
[substatements]
elif [condexpr]:
[substatements]
else:
[substatements]
"""

x: int = int(input())

if x < 0:
print("Negative")
else:
print("Non-negative")``````

++++++++++++++++
Cahoot-05.1
https://mst.instructure.com/courses/58101/quizzes/55394

``````#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
elseif is optional, with any number of them being OK
else is optional, though it must be the last block
Conditional expression’s operators:
(), ==, !=, , =, and, or, not
Precedence (high to low):
(), not, * / % + -, < >=, == !=, and, or
"""

x: int = int(input())
y: int = int(input())
z: int = int(input())

if (y - z) < 10:
print("Close")
if x < 10:
print("Small")
# Nested conditions
if x > 8:
print("nine")
elif x < 20:
print("Med")
else:
print("Large")``````

++++++++++++++++
Cahoot-05.2
https://mst.instructure.com/courses/58101/quizzes/55395

``````#!/usr/bin/python3
# -*- coding: utf-8 -*-

M: int = 5
N: int = 2
K: int = M % N

if K == 1:
print("hello")
elif K == 2:
print("world")
else:
print("!")``````

## 1.7 Operators used in branching

### 1.7.1 Relational operators

* The `==` operator is the “is equal” operator and `!=` is the “is not equal” operator.
* Many times, those learning will make a mistake when trying to use the `==` or `=` operator
* They will use the `=` operator instead of the `==` operator.
* Thus, val `=` num will run but not compare the two values.
* It will set the value of the variable val to that of num and will return true.
* This is not the desired result.
* BE CAREFUL.

Assume variable A holds 10 and variable B holds 20:

### 1.7.3 Logical operators in Python3

``````and
or
not``````

### 1.7.4 Truth tables

Suppose that `a=2`, `b=3`, and `c=6`, then, what are the following?

``````( 7 == 5 )
( 5 > 4)
( 3 != 2 )
( 6 >= 6 )
( 5 < 5)
( a == 5 )
( a ∗ b >= c )
( b + 4 > a ∗ c )
( ( b=2) == a )``````

Not has only one operand, to its right, and inverts it, producing false if its operand is true, and true if its operand is false.

What are the following?

``````! ( 5 == 5 )
! ( 6 <= 4 )
! true
! false``````

++++++++++++++++
Cahoot-05.3
https://mst.instructure.com/courses/58101/quizzes/55396

Ask: What is the maximum depth of if-nesting?
It’s 100s deep in many languages.

## 1.8 Examples

### 1.8.1 elif chains

``````#!/usr/bin/python3
# -*- coding: utf-8 -*-

month: int = int(input("Please enter the month as a number: "))
day: int = int(input("Please enter the day of the month as a number: "))

if month == 1:
print("January")
elif month == 2:
print("February")
elif month == 3:
print("March")
elif month == 4:
print("April")
elif month == 5:
print("May")
elif month == 6:
print("June")
elif month == 7:
print("July")
elif month == 8:
print("August")
elif month == 9:
print("September")
elif month == 10:
print("October")
elif month == 11:
print("November")
else:
print("December")

print(day)``````

### 1.8.2 Real diagnostic flowchart

``````#!/usr/bin/python3
# -*- coding: utf-8 -*-

print("Help! My computer doesn't work!\n")
print("Does the computer make any sounds (fans, etc.) or show any lights? (y/n):")
choice: str = input()

if choice == "n":
print("Is it plugged in? (y/n):")
choice = input()
if choice == "n":
print(
"Plug it in.",
"If the problem persists,",
)
else:
print('Is the switch in the "on" position? (y/n):')
choice = input()
if choice == "n":
print(
"Turn it on.",
"If the problem persists,",
)
else:
print("Does the computer have a fuse?  (y/n):")
choice = input()
if choice == "n":
print("Is the outlet OK? (y/n):")
choice = input()
if choice == "n":
print(
"Check the outlet's circuit breaker or fuse.",
"Move to a new outlet, if necessary.",
"If the problem persists,",
)
else:
else:
print(
"Check the fuse. Replace if necessary.",
"If the problem persists,",
"then please run this program again.\n",
)
else:

### 1.8.3 Caesar extended

Last time, we did not have branches, but now we do!

#### 1.8.3.1 Key generation (corrected bug from before)

``````#!/usr/bin/python3
# -*- coding: utf-8 -*-

import random

print("Your randomly chosen Caesar cipher key is: ")
print(random.randint(1, 25))
print("\n Share securily with your communication partner, and don't tell anyone else\n")``````

#### 1.8.3.2 Encryption and decryption (earlier bug fixed, missing features)

Note: Often there are MANY ways to write a correct program for the same goal.

##### 1.8.3.2.1 Solution 1

With if statements, we now only need to have one copy of the code, rather than one program for encryption and one for decryption, as before:
05-Branches/branching_caesar_01.py

``````#!/usr/bin/python3
# -*- coding: utf-8 -*-

print("You have to run this program, once for every character of your message")

key: int = int(input("\nEnter your Caesar key in numeric form (1-25): "))

mode: int = int(input("\nEnter 1 for encryption, and 0 for decryption: "))

encoded_char: int = int(
input(
"\nEnter Caesar encoded number corresponding to one character of your message:"
)
)

print("\nThe translation of your character is:")

if mode == 1:
# 26 is the symbol set size (# letters in alphabet)
# negative mod is language dependent in behavior
print((encoded_char + key) % 26)
else:
print((encoded_char - key) % 26)``````

* Does this work for small letters, big ones?
* Does it work for encryption and decryption?
* Does anything kind of input break it?

##### 1.8.3.2.2 Solution 2

There was an alternative way to have accomplished this without branches/conditions, below:
Logic tricks like this are not always possible.
05-Branches/branching_caesar_02.py

``````#!/usr/bin/python3
# -*- coding: utf-8 -*-

print("You have to run this program, once for every character of your message")

key: int = int(input("\nEnter your Caesar key in numeric form (1-25): "))

mode: int = int(input("\nEnter 1 for encryption, and -1 for decryption: "))

encoded_char: int = int(
input(
"\nEnter Caesar encoded number corresponding to one character of your message:"
)
)

print("\nThe translation of your character is:")

# 26 is the symbol set size (# letters in alphabet)
# negative mod is language dependent in behavior
print((encoded_char + (key * mode)) % 26)``````

* Does this work for small letters, big ones?
* Does it work for encryption and decryption?
* Does anything kind of input break it?
* Does this kind of math trick always let you avoid conditions/branches?

##### 1.8.3.2.3 Solution 3

There was an alternative way to have accomplished a correct solution without modulus, below:
05-Branches/branching_caesar_03.py

``````#!/usr/bin/python3
# -*- coding: utf-8 -*-

print("You have to run this program, once for every character of your message")

key: int = int(input("\nEnter your Caesar key in numeric form (1-25): "))

mode: int = int(input("\nEnter 1 for encryption, and 0 for decryption: "))

encoded_char: int = int(
input(
"\nEnter Caesar encoded number corresponding to one character of your message:"
)
)

print("\nThe translation of your character is:")

if mode == 1:
# 26 is the symbol set size (# letters in alphabet)
if (encoded_char + key) < 26:
print(encoded_char + key)
else:
print((encoded_char + key) - 26)
else:
# 26 is the symbol set size (# letters in alphabet)
if 0 < (encoded_char - key):
print(encoded_char - key)
else:
print((encoded_char - key) + 26)``````

* Does this work for small letters, big ones?
* Does it work for encryption and decryption?
* Does anything kind of input break it?

* Though one solution is not always better, often one can be.
* What is the best of the above three solutions here?
* How do we quantify best?
* Prettiest?
* Easiest to understand?
* Shortest
* Fastest?
* Most computationally efficient?
* Least computationally efficient (sometimes that’s good)?

## 1.9 Language focus

To be stepped through in the python3-spyder IDE and/or python3-pudb debugger:
05-Branches/branching05_overview.py

## 1.10 Funny random point: static block depth

• https://stackoverflow.com/questions/59803109/what-is-the-maximum-depth-level-of-nested-for-loops-in-python-3
• https://stackoverflow.com/questions/44972719/why-does-python-have-a-limit-on-the-number-of-static-blocks-that-can-be-nested
• The if-statement nesting depth in the default C-python interpreter is fixed at 20??
• It is hard-coded, and not flexible by variable assignment.
• It’s a constant located in `cpython/Include/code.h`, the constant being `CO_MAXBLOCKS`
• This also applies to loops (and all statically nested blocks in cpython)
• Note: this is not the same as function call depth, which can effectively be arbitrarily deep, and user-defined.
• Sub-note: Function call depth is mis-named recursion depth in python…

Next: 06-Loops.html