|
28 | 28 | \gitPythonAndOutput{\programmingWithPythonCodeRepo}{04_loops}{for_loop_pi_liu_hui.py}{--args format}{loops:for_loop_pi_liu_hui}{%
|
29 | 29 | A variant of \cref{lst:variables:pi_liu_hui} which uses a \pythonilIdx{for} loop instead of five copies of the same instructions.}%
|
30 | 30 | %
|
31 |
| -\gitPythonAndOutput{\programmingWithPythonCodeRepo}{04_loops}{for_loop_continue_break.py}{--args format}{loops:for_loop_continue_break}{% |
32 |
| -An example for the \pythonilIdx{continue} and \pythonilIdx{break} statements in a \pythonilIdx{for} loop.}% |
33 |
| -% |
34 | 31 | The keyword~\pythonilIdx{for} is followed by a loop variable.
|
35 | 32 | Then comes the keyword~\pythonilIdx{in}, the \pythonil{sequence} we want to iterate over, and finally a colon~(\pythonilIdx{:}).
|
36 | 33 | This variable will iteratively take on the values in the \pythonil{sequence}.
|
37 | 34 | The loop body statements in the following, indented block are executed for each of these values.
|
38 |
| -After the loop, we leave a blank line followed by the code that will be executed after the loop completes. |
| 35 | +Each time the body of the loop is executed is called an~\emph{iteration}\pythonIdx{iteration} of the loop. |
| 36 | +After the loop, we leave one blank line followed by the code that will be executed after the loop completes. |
39 | 37 |
|
40 | 38 | In its most simple form, the \pythonilIdx{for} loop is applied to a \pythonilIdx{range} of integer numbers.
|
41 | 39 | Ranges are sequences which work basically like slices\pythonIdx{slice} (see \cref{sec:lists:basicFunctions,sec:strBasicOperations}).
|
|
49 | 47 |
|
50 | 48 | In \cref{lst:loops:for_loop_range}, we loop over exactly these ranges.
|
51 | 49 | In this listing, we try to create a dictionary (see \cref{sec:dictionaries}) where some integer numbers are mapped to their squares.
|
52 |
| -We use four \pythonilIdx{for} loops to fill this dictionary with data.% |
| 50 | +We use four \pythonilIdx{for}~loops to fill this dictionary with data. |
| 51 | +In each of these first four \pythonilIdx{for}~loops, we use \pythonil{i} as the loop variable. |
| 52 | + |
| 53 | +When iterating over the \pythonil{range(5)} in the first loop, \pythonil{i} will hold the value \pythonil{0} in the first iteration (=~execution of the loop body). |
| 54 | +The loop body \pythonil{squares[i] = i * i} will thus effectively be \pythonil{squares[0] = 0} and thus store the value~\pythonil{0} under key~\pythonil{0} into the dictionary \pythonil{squares}. |
| 55 | +In the second iteration, \pythonil{i} will hold the value~\pythonil{1}. |
| 56 | +Then, the body \pythonil{squares[i] = i * i} will effectively be \pythonil{squares[1] = 1}. |
| 57 | +In the third iteration, \pythonil{i} will hold the value~\pythonil{2} and the body will perform \pythonil{squares[2] = 4}. |
| 58 | +Next, \pythonil{i = 3} and \pythonil{squares[3] = 9} will be executed and in the laste iteration of the first loop, we store \pythonil{squares[4] = 16}. |
| 59 | + |
| 60 | +In the second loop, which uses \pythonil{range(6, 9)}, \pythonil{i} will take on the values \pythonil{6}, \pythonil{7}, and \pythonil{8}, one by one. |
| 61 | +The dictionary \pythonil{squares} will thus be extended with the values \pythonil{squares[6] = 36}, \pythonil{squares[7] = 49}, and \pythonil{squares[8] = 64}. |
| 62 | +In the third loop, iterating over \pythonil{range(20, 27, 2)}, the following updates will be performed one by one \pythonil{squares[20] = 400}, \pythonil{squares[22] = 484}, \pythonil{squares[24] = 576}, and \pythonil{squares[26] = 676}. |
| 63 | +In the fourth loop, \pythonil{i} takes on the values of the sequence \pythonil{range(40, 30, -3)}, which has the negative step length~\pythonil{-3}. |
| 64 | +\pythonil{i} therefore first becomes~\pythonil{40}, then \pythonil{37} in the second iteration, then \pythonil{34}, and, finally,~\pythonil{31}. |
| 65 | +We then print\pythonIdx{print} the dictionary and get the expected output in \cref{exec:loops:for_loop_range}.% |
| 66 | +% |
| 67 | +\bestPractice{underscore}{% |
| 68 | +If we do not care about the value of a variable (or parameter), we should name it~\pythonil{_}\pythonIdx{\_}~\cite{PEP635}. % |
| 69 | +This information is useful for other programmers as well as static code analysis tools.% |
| 70 | +}% |
| 71 | +% |
| 72 | +At the end of \cref{lst:loops:for_loop_range} we show this special case: |
| 73 | +We want to print\pythonIdx{print} \pythonil{"Hello World!"} three times. |
| 74 | +Instead of copying the line \pythonil{print("Hello World!")} three times, we put it in a loop. |
| 75 | +However, nowhere in the loop body we care about the value of the loop variable. |
| 76 | +We thus simply call it \pythonil{_}\pythonIdx{\_}. |
| 77 | +If we would not call it that, then another programmer seeing our code (or a static code analysis tool for that matter) could be confused as to why we do not use the loop variable. |
| 78 | +Always remember that \inQuotes{real} code could be much more complicated, and any semantic hint we can include to convey our intentions will be helpful. |
| 79 | + |
| 80 | +With these new tools at hand, we can revisit our old program \cref{lst:variables:pi_liu_hui} to approximate~$\pi$ from back in \cref{sec:approximatePiLiuHui} |
| 81 | +In this program, we executed the same code five times. |
| 82 | +Instead of doing this, we can put this into a loop, which reduces the lines of code from over~25 to about~10 in \cref{lst:loops:for_loop_pi_liu_hui}. |
| 83 | +The outputs in \cref{exec:variables:pi_liu_hui,exec:loops:for_loop_pi_liu_hui} are exactly the same.% |
| 84 | +% |
| 85 | +\endhsection% |
| 86 | +% |
| 87 | +\hsection{The \texttt{continue} and \texttt{break} Statements}% |
| 88 | +% |
| 89 | +\gitPythonAndOutput{\programmingWithPythonCodeRepo}{04_loops}{for_loop_continue_break.py}{--args format}{loops:for_loop_continue_break}{% |
| 90 | +An example for the \pythonilIdx{continue} and \pythonilIdx{break} statements in a \pythonilIdx{for} loop.}% |
| 91 | +% |
| 92 | +Loops often have complex bodies, maybe containing conditional statements or other loops. |
| 93 | +It is not an uncommon situation that, after performing some computations in the body of the loop, we already know that we can continue directly with the next iteration instead of executing the remainder of the loop body. |
| 94 | +Sometimes we also find that we can entirely stop with the loop and continue with whatever instructions come after it, even if we did not yet exhaust the sequence over which we are iterating. |
| 95 | +The former can be achieved using the \pythonilIdx{continue} and the latter with the \pythonilIdx{break} statement. |
| 96 | + |
| 97 | +An example for both statements is given in \cref{lst:loops:for_loop_continue_break}. |
| 98 | +Here, we iterate the variable~\pythonil{i} over the 15~values from~\pythonil{0} to~\pythonil{14}. |
| 99 | +In the loop body, we first create a string~\pythonil{s} with the current value of~\pythonil{i} via the \pgls{fstring} \pythonil{f"i is now \{i\}."} |
| 100 | +The very last instruction of the loop body, \pythonil{print(s)}\pythonIdx{print}, prints this string. |
| 101 | + |
| 102 | +While \pythonil{i} would go from~\pythonil{0} to~\pythonil{14}, we actually want to abort the loop as soon as \pythonil{i} becomes greater than~\pythonil{10}. |
| 103 | +Instead of modifying the \pythonilIdx{range} over which we loop (which would be reasonable), we here want to use the \pythonilIdx{break} statement. |
| 104 | +We therefore wrap it into the conditional \pythonil{if i > 10:}, meaning that it will only be executed if~\pythonil{i > 10}. |
| 105 | +As soon as \pythonilIdx{break} is reached, the loop will immediately be aborted. |
| 106 | +No further instruction in the loop body is executed and no further iteration is performed. |
| 107 | +Instead, the process will continue after the loop, with the line~\pythonil{print("All done.")}. |
| 108 | + |
| 109 | +If \pythonil{i > 10} did not hold, the rest of the loop body is executed. |
| 110 | +For the case that $\pythonil{i}\in\intRange{5}{8}$, we now want to directly jump to the next loop iteration by invoking the \pythonilIdx{continue}~statement. |
| 111 | +This means that if \pythonil{i == 5}, the \pythonilIdx{continue}~statement lets the control directly return to the head of the loop, which will set~\pythonil{i = 6}. |
| 112 | +This will happen until~\pythonil{i == 9}. |
| 113 | +The condition \pythonil{5 <= i <= 8} is \emph{not} met for all $\pythonil{i}\in\intRange{0}{4}\cup\intRange{9}{15}$. |
| 114 | +The next line, namely the \pythonil{print(s)}\pythonIdx{print}, can only be reached in these cases. |
| 115 | +Of course, we already know that we will never even reach this code as soon as \pythonil{i == 11}. |
| 116 | + |
| 117 | +As a result the program will print the string~\pythonil{s} only for~$\pythonil{i}\in\intRange{0}{4}\cup\{9, 10\}$ before finally outputting~\textil{All done.}. |
| 118 | +This can be observed in the program output collected in \cref{exec:loops:for_loop_continue_break}. |
| 119 | +With \pythonilIdx{break} and \pythonilIdx{continue}, we now have two tools that can help us to either abort any loop prematurely or to abort the current iteration of the loop prematurely (and continue with the next one, if any), respectively.% |
53 | 120 | %
|
54 | 121 | \endhsection%
|
55 | 122 | %
|
|
0 commit comments