Skip to content

Instantly share code, notes, and snippets.

@kylebgorman
Created September 18, 2019 14:49
Show Gist options
  • Save kylebgorman/6d229f7ab8548c33b7a1db90dbb65f86 to your computer and use it in GitHub Desktop.
Save kylebgorman/6d229f7ab8548c33b7a1db90dbb65f86 to your computer and use it in GitHub Desktop.
LING78100 Lecture 2
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Lecture 2: Control flow"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In the previous lecture, we saw how to create, compare, and compute with numbers, strings, lists and tuples. In today's lecture we'll cover techniques that allow us to build up these operations into more complex routines:\n",
"\n",
"* Control flow, includings:\n",
" - conditional execution (`if`/`elif`/`while`)\n",
" - looping (`while`/`for`/`continue`/`break`)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Control flow"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"_Control flow_ refers to mechanisms which determine the order in which expressions in a program are executed. Two key control flow concepts in Python are:\n",
"\n",
"* _conditionals_, which control which expressions are executed (or rather, whether they're executed), and\n",
"* _loops_, which allow a set of expressions to be repeatedly executed."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Conditionals"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### `if`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The simplest form of a conditional is the _if-statement_, which indicates code to be executed if and only if some condition is satisfied. An `if`-clause takes the following form:\n",
"\n",
" if CONDITION:\n",
" EXPR\n",
" ...\n",
" \n",
"Here `if` is a keyword, `CONDITION` is a conditional expression (i.e., some Python expression which can be evaluated as true or false), and `EXPR` (etc.) is an expression to be executed conditionally. The conditional expression(s) must be indented (usually with two or four spaces)."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Fizz\n"
]
}
],
"source": [
"i = 3645\n",
"if i % 3 == 0: # The conditional here is `i % 3 == 0`.\n",
" print(\"Fizz\") # Note, two spaces."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If-statements can be nested. Conditional execution ceases once we return to the original indentation level. (This is what we mean when we say that Python has \"meaningful whitespace\".)"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Divisible by 3\n",
"Divisible by 5 as well\n",
"Executed unconditionally\n"
]
}
],
"source": [
"i = 3645 # Divisible by 3 and 5.\n",
"if i % 3 == 0:\n",
" print(\"Divisible by 3\")\n",
" if i % 5 == 0:\n",
" print(\"Divisible by 5 as well\") # Now, four spaces.\n",
"print(\"Executed unconditionally\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### `else` and `elif`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can also pair `if`-statements with `else`-statements. These execute only when the condition of a matching `if`-clause's (i.e., at the same level of indentation) is false. An `if`/`else` clause has the following form:\n",
"\n",
" if CONDITION:\n",
" EXPR\n",
" ...\n",
" else:\n",
" EXPR\n",
" ..."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Odd\n"
]
}
],
"source": [
"i = 3645\n",
"if i % 2 == 0:\n",
" print(\"Even\")\n",
"else:\n",
" print(\"Odd\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Finally, we can combine `if` and `else` using `elif`. An `elif` is paired with a condition which is checked only if the condition of all preceding matching `if`s (or other `elif`s) is false. An `if`/`elif`/`else` clause has the following form:\n",
"\n",
" if CONDITION:\n",
" EXPR\n",
" ...\n",
" elif CONDITION:\n",
" EXPR\n",
" ...\n",
" else:\n",
" EXPR\n",
" ..."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Divisible by 3\n"
]
}
],
"source": [
"i = 3645\n",
"if i % 2 == 0:\n",
" print(\"Even\")\n",
"elif i % 3 == 0:\n",
" print(\"Divisible by 3\")\n",
"else:\n",
" print(\"Neither even nor divisible by 3\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Finally, note that **the `else` clause is always optional**, must occur after any `elif`s, and there can never be more than one `else` per `if`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Ternaries"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Python also allows one to place a single `if`/`else` expression on a single line. For historical reasons, this is sometimes called a _ternary_ or _ternary expression_. Such expressions are often the right-hand side of an assignment, like below. They have the following form:\n",
"\n",
" EXPR if CONDITION else EXPR2"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"cigar\n"
]
}
],
"source": [
"age = 21\n",
"birthday_gift = \"cigar\" if age >= 18 else \"lollipop\"\n",
"print(birthday_gift)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Loops"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"_Loops_ allow us to specify _iteration_, that is, repeatedly executed code."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### `while`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" The simplest type of loop in Python is the `while` loop, which has the following form:\n",
"\n",
" while CONDITION:\n",
" EXPR\n",
" ...\n",
"\n",
"where `CONDITION` is a conditional expression and `EXPR` is one or more indented expressions. When executing a `while` block, we first evaluate the condition `CONDITION`; if it is true, then `EXPR` is executed. Then, the condition is checked again, and `EXPR` is again executed. This continues until `CONDITION` is false, at which point execution terminates."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0\n",
"1\n",
"2\n",
"3\n",
"4\n"
]
}
],
"source": [
"i = 0\n",
"while i < 5:\n",
" print(i)\n",
" i += 1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### `for`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In most cases, however, we wish to loop over all values of an _iterable_ (such as a list, tuple, or string). For this, we use the `for` loop, which has the following form:\n",
"\n",
" for VARIABLE in ITERABLE:\n",
" EXPR\n",
" ...\n",
" \n",
"where `VARIABLE` is a variable name, `ITERABLE` is an _iterable_ literal or variable (like a list, tuple, or string), and `EXPR` is one or more indented expressions. This then executes `EXPR` once for each value in `ITERABLE`, changing `VARIABLE` to the appropriate value. For instance, the following computes the sum of a tuple of integers."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"38\n"
]
}
],
"source": [
"numbers = (8, 6, 7, 5, 3, 0, 9)\n",
"total = 0\n",
"for number in numbers:\n",
" total += number\n",
"print(total)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Iterating over a string returns single-character strings."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"O\n",
"w\n",
"s\n",
"l\n",
"e\n",
"y\n"
]
}
],
"source": [
"name = \"Owsley\"\n",
"for char in name:\n",
" print(char)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### `range`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Python provides a built-in object `range` for iterating over integer sequences. It can be constructed with one, two, or three integer arguments, with the following interpretations:\n",
"\n",
"* `range(i)`: every integer from $0$ (inclusive) to $i$ (exclusive)\n",
"* `range(i, j)`: every integer $i$ (inclusive) to $j$ (exclusive)\n",
"* `range(i, j, k)`: from $i$ (inclusive) to $j$ (exclusive), adding $k$ at each stem"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0\n",
"1\n",
"2\n",
"3\n",
"4\n"
]
}
],
"source": [
"for i in range(5):\n",
" print(i)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"7\n",
"10\n",
"13\n",
"16\n"
]
}
],
"source": [
"for i in range(7, 19, 3):\n",
" print(i)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### `continue`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A `continue` statement, used from within a loop, jumps directly to the next iteration of a loop. One common use of `continue` is to filter certain values in the loop from within an `if`. For instance, in the following example counts the number of numbers between $[1, 100)$ which are not divisible by 10."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"90\n"
]
}
],
"source": [
"not_divisible_by_ten = 0\n",
"for i in range(1, 100):\n",
" if i % 10 == 0: # Ignore if divisible by 10.\n",
" continue\n",
" not_divisible_by_ten += 1 # Otherwise increment the counter.\n",
"print(not_divisible_by_ten)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"An alternative to the `continue` in the above example would be to simply use the `if` to determine whether or not to increment the counter."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"90\n"
]
}
],
"source": [
"not_divisible_by_ten = 0\n",
"for i in range(1, 100):\n",
" if i % 10 != 0: \n",
" not_divisible_by_ten += 1\n",
"print(not_divisible_by_ten)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We often prefer the former version, with the `continue` statement, over an `if`/`else`, because it more clearly separates regular (\"increment the counter\") and exceptional (\"ignore this example\") behavior, and it is easier to read when there are [many filtering conditions](http://www.wellformedness.com/blog/how-why-and-when-to-flatten-your-conditionals/)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### `break`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A `break` statement immediately exits the current (innermost) loop. The following example uses it in a brute-force solution to find the first number divisible by both 8 and 9."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"72\n"
]
}
],
"source": [
"i = 1\n",
"while True: # This will run forever unless we have a `break` within.\n",
" if i % 8 == 0: # We could also use an `and` here, see below.\n",
" if i % 9 == 0:\n",
" break\n",
" i += 1 \n",
"print(i)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment