{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Lecture 6\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "-Python flow control: [>>](#Python-flow-control) \n", "--For loop: [>>](#For-loop) \n", "--Loops and slices: [>>](#Loops-and-slices) \n", "--Alternative forms of the for loop: [>>](#Alternative-forms-of-the-for-loop) \n", "--While statement: [>>](#While-statement) \n", "--If statement: [>>](#If-statement) \n", "--Continue and break: [>>](#Continue-and-break) \n", "--Precision of floating point numbers: [>>](#Precision-of-floating-point-numbers) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Python flow control\n", "\n", "We have seen how we can do calculations in Python, how we can use library functions and how we can create our own functions. Now we look at how Python allows us to decide how we would like our program to \"flow\", repeating calculations or moving from one calculation or statement to the next. This allows us to deal with situations where we want to do something many times, or do it differently depending on whether a variable is positive or negative, for example. Several ways of steering programs are provided, including the `for`, `while` and `if` statements. We look at these in the following.\n", "\n", "### For loop\n", "\n", "The `for` loop allows us to repeat sections of a program a specified number of times. The full syntax of the loop is shown below:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Index 0\n", "Index 2\n", "Index 4\n", "Final value of index 4\n" ] } ], "source": [ "start = 0\n", "stop = 5\n", "step = 2\n", "#\n", "for i in range(start, stop, step):\n", " print(\"Index\",i)\n", "print(\"Final value of index\",i)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The start of the loop is indicated by the `for` statement, which is followed by the name of the index (here we use `i`) which will steer how many times the loop is executed. The values `i` should take are determined by the statement `in range(start, stop, step)` and `i`, `start`, `stop` and `step` must all be integers. The `for` line is terminated using a colon (:).\n", "\n", "When the program runs, the value of `i` is first set to `start`, then all the indented statements following the `for` statement are carried out. The value of `i` is then incremented by `step`, and if the resulting value is less than `stop`, the loop is executed again. This continues until adding `step` to `i` would give a value greater than or equal to `stop`.\n", "\n", "If `step` is one (which it often is), you can use the simpler version of the `for` loop which omits the `step` parameter:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Index 0 index squared 0\n", "Index 1 index squared 1\n", "Index 2 index squared 4\n", "Index 3 index squared 9\n", "Index 4 index squared 16\n", "Index 5 index squared 25\n", "Index 6 index squared 36\n", "Final value of index 6\n" ] } ], "source": [ "start = 0\n", "stop = 7\n", "for n in range(start, stop):\n", " print(\"Index\",n,\"index squared\",n**2)\n", "print(\"Final value of index\",n)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Again, the indentation indicates the body of the loop, i.e. the section of the program that is repeated. You can see that the loop doesn't run with `n = stop` and that the value of `n` on leaving the loop is the last \"allowed\" value." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Loops and slices\n", "\n", "We introduced the idea of slicing an array in an earlier lecture. Here's a reminder:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "count_arr = [ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]\n", "count_arr[3:6] = [3. 4. 5.]\n" ] } ], "source": [ "import numpy as np\n", "#\n", "count_arr = np.linspace(0, 10, 11)\n", "print(\"count_arr =\",count_arr)\n", "print(\"count_arr[3:6] =\",count_arr[3:6])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The parameters used in slicing are related to those in a `for` loop; they have the meaning `start`, `stop` and `step`. Check this with another example!" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "count_arr = [0. 1. 2. 3. 4. 5. 6.]\n", "count_arr[1:6:2] = [1. 3. 5.]\n" ] } ], "source": [ "count_arr = np.linspace(0, 6, 7)\n", "print(\"count_arr =\",count_arr)\n", "print(\"count_arr[1:6:2] =\",count_arr[1:6:2])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Alternative forms of the for loop\n", "\n", "Loops can also be used to cycle through the elements in a list or a tuple. Examples are shown below:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "one\n", "two\n", "three\n", "End of loop, var is three\n" ] } ], "source": [ "loopList = [\"one\", \"two\", \"three\"]\n", "for var in loopList:\n", " print(var)\n", "print(\"End of loop, var is\",var)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "A\n", "B\n", "C\n", "End of loop, var is C\n" ] } ], "source": [ "loopTuple = (\"A\", \"B\", \"C\")\n", "for var in loopTuple:\n", " print(var)\n", "print(\"End of loop, var is\",var)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### While statement\n", "\n", "The `while` statement offers another way of repeatedly using a section of code. An example follows:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "test = 0.1\n", "test = 0.4\n", "test = 0.7\n", "test = 1.0\n", "Final value of test is 1.3\n" ] } ], "source": [ "test = 0.1\n", "limit = 1.1\n", "step = 0.3\n", "while test < limit:\n", " print(\"test =\",test)\n", " test = test + step\n", "print(\"Final value of test is\",test)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As in the case of the for loop, the line containing `while` finishes with a colon. The body of the loop, indicated by the indentation, is executed until the condition `test < limit` is false. (It won't execute at all if the condition is false the first time it is checked.) Something must be changed in the body of the loop to ensure that at some point `test < limit` becomes false, or the loop will run for ever. In the example above, the value of `test` increases by `step` each time the while loop runs, because we set `test = test + step`.\n", "\n", "The condition that is tested has one of two values, `True` or `False`. If the statement `test < limit` is `True`, execution continues, if it is `False`, it stops. As such logical conditions are used so frequently, Python has a data type, `bool` (short for _boolean_), which can take only the values `True` or `False`. A boolean variable (a variable of type `bool`) can be used explicitly in a `while` loop, as shown below: " ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "test = 0.1 and test_var is True\n", "test = 0.4 and test_var is True\n", "test = 0.7 and test_var is True\n", "test = 1.0 and test_var is True\n", "Final value of test_var is False\n" ] } ], "source": [ "test = 0.1\n", "limit = 1.1\n", "step = 0.3\n", "test_var = True\n", "while test_var:\n", " print(\"test =\",test,\"and test_var is\",test_var)\n", " test = test + step\n", " test_var = test < limit\n", "print(\"Final value of test_var is\",test_var)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This doesn't make the program any clearer, so it isn't a sensible thing to do in this case, but it does illustrate how `bool` variables can be used!\n", "\n", "`While` loops can be supplemented with an `else` statement, which is executed if the condition in the `while` statement is `False`." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "test is 0.1\n", "test is 0.4\n", "test is 0.7\n", "test is 1.0\n", "Value of test in else section is 1.3\n", "Final value of test 1.3\n" ] } ], "source": [ "test = 0.1\n", "limit = 1.1\n", "step = 0.3\n", "while test < limit:\n", " print(\"test is\",test)\n", " test = test + step\n", "else:\n", " print(\"Value of test in else section is\",test)\n", "print(\"Final value of test\",test)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This looks as though it is superfluous. Surely anything after the while statement will be executed after the tested condition becomes `False`, even without an `else` statement? This is indeed the case, but, as we will see later, the `while`, `else` construct is useful in conjunction with other Python control structures, the `continue` and `break` statements. \n", "\n", "*An aside - If you end up with an endless loop while writing and testing a program, you can stop it by interrupting or restarting the kernel - the computing \"engine\" of your Notebook - using the appropriate menu commands.*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### If statement\n", "\n", "The `if`, `elif`, `else` statement has the syntax illlustrated below:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "This is section D\n", "This is the end of the if statement, the value of test is 4.3\n" ] } ], "source": [ "test = 4.3\n", "if test < 1.0:\n", " print(\"This is section A\")\n", "elif test > 2.0 and test <= 3.0:\n", " print(\"This is section B\")\n", "elif test > 3.0 and test <= 4.0:\n", " print(\"This is section C\")\n", "else:\n", " print(\"This is section D\")\n", "print(\"This is the end of the if statement, the value of test is\",test)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Again, the lines starting with the `if`, `elif` (short for _else if_) and `else` statements must end with a colon. The code that is executed when the conditions tested in each of these lines are `True` is indented. Only the first of the sections of the `if`, `elif`, `else` block for which the condition is met is executed. _(Note, Python doesn't check the logic of your control statements, so it won't warn you if the tests in your `if` block don't make sense!)_\n", "\n", "Statements can consist of just an `if`, an `if` and an `else`, or an `if` and one or more `elif`s, or, as above, of an `if`, one or more `elif`s and an `else`.\n", "\n", "In contrast to most computing languages, Python allows you to write the above `if`, `elif`, `else` statement in a more natural way (closer to standard mathematical notation) as follows:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "This is section B\n", "This is the end of the if statement, the value of test is 2.3\n" ] } ], "source": [ "test = 2.3\n", "if test < 1.0:\n", " print(\"This is section A\")\n", "elif 2.0 < test <= 3.0:\n", " print(\"This is section B\")\n", "elif 3.0 < test <= 4.0:\n", " print(\"This is section C\")\n", "else:\n", " print(\"This is section D\")\n", "print(\"This is the end of the if statement, the value of test is\",test)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Continue and break\n", "\n", "These statements allow you to modify the behaviour of a loop. In a `for` or a `while` loop, `continue` causes control to jump back to the beginning of the loop, without executing the statements after the `continue`. Two examples are shown below." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "C\n", "n\n", "s\n", "t\n", "n\n", "t\n", "n\n", "p\n", "l\n", "Final value of letter is e\n" ] } ], "source": [ "for letter in \"Constantinople\":\n", " if letter in \"a, e, i, o, u\":\n", " continue\n", " print(letter)\n", "print(\"Final value of letter is\",letter)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the string \"a, e, i, o u\" above could be replaced by \"a e i o u\" or \"aeiou\" and the routine would still work. It is checking whether `letter` is in the string enclosed in quotes and as `letter` is never \",\" or \" \" (there are no commas or spaces in \"Constantinople\"), their presence in the string makes no difference!\n", "\n", "Here's another example:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "0\n", "1\n", "10\n", "2\n", "20\n", "3\n", "4\n", "5\n", "Final value of i is 5\n" ] } ], "source": [ "for i in range(0, 6):\n", " print(i)\n", " if i > 2:\n", " continue\n", " print(10*i)\n", "print(\"Final value of i is\",i)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In contrast, `break` causes control to jump to the end of the loop:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "C\n", "Final value of letter is o\n" ] } ], "source": [ "for letter in \"Constantinople\":\n", " if letter in \"a, e, i, o, u\":\n", " break\n", " print(letter)\n", "print(\"Final value of letter is\",letter)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "0\n", "1\n", "10\n", "2\n", "20\n", "3\n", "Final value of i is 3\n" ] } ], "source": [ "for i in range(0, 6):\n", " print(i)\n", " if i > 2:\n", " break\n", " print(10*i)\n", "print(\"Final value of i is\",i)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "How the `else` statement can be of use with `while` is apparent in the following examples." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " \n", "While loop with continue statement\n", "test in first position is 0.3\n", "test in second position is 0.5\n", "test in first position is 0.5\n", "test in second position is 0.7\n", "test in first position is 0.7\n", "Value of test in else section is 0.8999999999999999\n", "Final value of test 0.8999999999999999\n", " \n", "While loop with break statement\n", "test in first position is 0.3\n", "test in second position is 0.5\n", "test in first position is 0.5\n", "test in second position is 0.7\n", "test in first position is 0.7\n", "Final value of test 0.8999999999999999\n" ] } ], "source": [ "print(\" \")\n", "print(\"While loop with continue statement\")\n", "test = 0.3\n", "limit = 0.8\n", "step = 0.2\n", "while test < limit:\n", " print(\"test in first position is\",test)\n", " test = test + step\n", " if test >= limit:\n", " continue\n", " print(\"test in second position is\",test)\n", "else:\n", " print(\"Value of test in else section is\",test)\n", "print(\"Final value of test\",test)\n", "#\n", "print(\" \")\n", "print(\"While loop with break statement\")\n", "test = 0.3\n", "while test < limit:\n", " print(\"test in first position is\",test)\n", " test = test + step\n", " if test >= limit:\n", " break\n", " print(\"test in second position is\",test)\n", "else:\n", " print(\"Value of test in else section is\",test)\n", "print(\"Final value of test\",test)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see that, if the `continue` statement is used, the code in the `else` statement is executed. If the `break` condition is applied, the code in the `else` statement is not run, because it is part of the `while` loop: there is a difference between the code that is executed if a `while` loop runs completely, or if it terminates due to a `break` statement.\n", "\n", "### Precision of floating point numbers\n", "\n", "In the example above, we also see (at least on my computer...and this can be machine dependent!) that adding $0.2$ (the value of `step`) to $0.7$ (one of the values that `test` takes in the `while` loop) doesn't give $0.9$, but $0.8999999999999999$. As we have discussed before, this happens because computers use binary representations of numbers with, for `floats`, limited precision. Addition, subtraction and other operations cause an additional loss of accuracy.\n", "If you want to read more, see [this article](https://docs.python.org/3/tutorial/floatingpoint.html).\n", "\n", "A consequence of this is that you should never rely on a `float` having exactly a particular value. For example, the following code is not likely to give the result you want:\n", "\n", "```Python\n", "if test == 0.1397:\n", "```\n", "\n", " Instead, you should use something like:\n", " \n", "```Python\n", "if np.abs(test - 0.1397) < 1e-10:\n", "```\n", " \n", "You can check how precise the representation of `floats` is using the following code." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The precision of your computer is 2.220446049250313e-16\n" ] } ], "source": [ "#\n", "eps = 1.0\n", "while eps + 1.0 > 1.0:\n", " eps = eps/2\n", "eps = 2*eps\n", "print(\"The precision of your computer is\", eps)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Python provides a function which returns the precision with which floats are represented in the `sys` (short for *system*) package:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Precision of float is 2.220446049250313e-16\n" ] } ], "source": [ "import sys\n", "print(\"Precision of float is\",sys.float_info.epsilon)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Numpy also has a function, [described here](https://docs.scipy.org/doc/numpy/reference/generated/numpy.finfo.html), which provides information on the representation of floating point numbers:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Precision of float is Machine parameters for float64\n", "---------------------------------------------------------------\n", "precision = 15 resolution = 1.0000000000000001e-15\n", "machep = -52 eps = 2.2204460492503131e-16\n", "negep = -53 epsneg = 1.1102230246251565e-16\n", "minexp = -1022 tiny = 2.2250738585072014e-308\n", "maxexp = 1024 max = 1.7976931348623157e+308\n", "nexp = 11 min = -max\n", "---------------------------------------------------------------\n", "\n" ] } ], "source": [ "print(\"Precision of float is\",np.finfo(float))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "anaconda-cloud": {}, "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.8.5" } }, "nbformat": 4, "nbformat_minor": 4 }