Skip to content

Commit d87889e

Browse files
committed
Added break to while loop and binary search example
1 parent a3b2348 commit d87889e

File tree

4 files changed

+159
-5
lines changed

4 files changed

+159
-5
lines changed

bibliography/bibliography.bib

+34
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ @string { a_baumgartner_gary
3535
@string { a_beazley_david_m = "David M.\ Beazley" }
3636
@string { a_beigel_richard = "Richard Beigel" }
3737
@string { a_ben_david_shai = "Shai Ben{-}David" }
38+
@string { a_bentley_jon = "Jon Bentley" }
3839
@string { a_berg_sebastian = "Sebastian Berg" }
3940
@string { a_beuke_fabian = "Fabian Beuke" }
4041
@string { a_beygelzimer_alina = "Alina Beygelzimer" }
@@ -112,6 +113,7 @@ @string { a_grus_joel
112113
@string { a_grys_slawomir = "S{\l}awomir Gry{\'s}" }
113114
@string { a_haberland_matt = "Matt Haberland" }
114115
@string { a_haldane_allan = "Allan Haldane" }
116+
@string { a_hammoud_mohammad = "Mohammad Hammoud" }
115117
@string { a_hance_jared = "Jared Hance" }
116118
@string { a_harris_charles_r = "Charles R.\ Harris" }
117119
@string { a_hausenblas_michael = "Michael Hausenblas" }
@@ -316,6 +318,7 @@ @string { l_iceland_reykjavik
316318
@string { l_india_noida = "{{Noida}, {Uttar Pradesh}, {India}}" }
317319
@string { l_netherlands_amsterdam = "{{Amsterdam}, {The~Netherlands}}" }
318320
@string { l_portugal_lisbon = "{{Lisbon}, {Portugal}}" }
321+
@string { l_qatar_doha = "{{Doha}, {Qatar}}" }
319322
@string { l_russia_petropolis = "{{Petropolis} {(St.~Petersburg)}, {Russia}}" }
320323
@string { l_spain_leioa = "{{Leioa}, {Bizkaia}, {Spain}}" }
321324
@string { l_switzerland_cham = "{{Cham}, {Switzerland}}" }
@@ -372,6 +375,7 @@ @string { p_bell_labs
372375
@string { p_birkhauser_boston = "{Birkh{\"a}user}" }
373376
@string { p_bruhin_software = "{Bruhin Software}" }
374377
@string { p_cambridge_uni_press_ass = "{Cambridge University Press \& Assessment}" }
378+
@string { p_carnegie_mellon_university_qatar = "{Carnegie Mellon University Qatar}" }
375379
@string { p_cnri = "{Corporation for National Research Initiatives~({CNRI})}" }
376380
@string { p_crc_press = "{{CRC} Press}" }
377381
@string { p_cornell_university_library = "{Cornell Universiy Library}" }
@@ -431,6 +435,7 @@ @string { pa_bell_labs
431435
@string { pa_birkhauser_boston = l_use_boston }
432436
@string { pa_bruhin_software = l_switzerland_winterthur }
433437
@string { pa_cambridge_uni_press_ass = l_uk_cambridge }
438+
@string { pa_carnegie_mellon_university_qatar = l_qatar_doha }
434439
@string { pa_cnri = l_usa_reston }
435440
@string { pa_cornell_university_library = l_usa_ithaca }
436441
@string { pa_crc_press = l_usa_boca_raton }
@@ -768,6 +773,16 @@ @article{B1991IWNT
768773
urldate = {2024-07-06},
769774
}
770775

776+
@book{B1999PP,
777+
author = a_bentley_jon,
778+
title = {Programming Pearls},
779+
edition = {2},
780+
publisher = p_addison_wesley,
781+
address = pa_addison_wesley,
782+
date = {1999-10-07},
783+
isbn = {978-0201657883}
784+
}
785+
771786
@article{B2012DPWP,
772787
author = a_beazley_david_m,
773788
title = {Data Processing with \pandas},
@@ -1289,6 +1304,25 @@ @book{M2022RAEFPLACFWIR
12891304
urldate = {2024-08-23}
12901305
}
12911306

1307+
@book{M2024POIC,
1308+
author = a_hammoud_mohammad,
1309+
title = {\mbox{15-122}:~Principles of Imperative Computation},
1310+
date = {2024-21},
1311+
publisher = p_carnegie_mellon_university_qatar,
1312+
address = pa_carnegie_mellon_university_qatar,
1313+
url = {https://web2.qatar.cmu.edu/~mhhammou/15122-s24/},
1314+
urldate = {2024-09-26},
1315+
}
1316+
1317+
@inbook{H2024POICBS,
1318+
title = {Binary Search},
1319+
crossref = {M2024POIC},
1320+
chapter = {Lecture~06},
1321+
date = {2024-01-29},
1322+
url = {https://web2.qatar.cmu.edu/~mhhammou/15122-s24/lectures/06-binsearch/slides},
1323+
urldate = {2024-09-26},
1324+
}
1325+
12921326
@article{N1939TTOP,
12931327
author = a_niven_ivan,
12941328
title = {The Transcendence of~\numberPi},

styles/myindex.sty

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
%
1515
%% dots without end-of-sentence spacing at the end
1616
\protected\gdef\idxdots{%
17+
\texorpdfstring{%
1718
.\kern\fontdimen3\font%
1819
.\kern\fontdimen3\font%
19-
.\@\strut}%
20+
.\@\strut}{{.}{.}{.}}}%
2021
%

text/main/controlFlow/conditionals/conditionals.tex

+3-3
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
\FloatBarrier%
6464
\endhsection%
6565
%
66-
\hsection{The \texttt{if{\idxdots}else} Statement}%
66+
\hsection{The \texttt{if}\idxdots\texttt{else} Statement}%
6767
%
6868
\gitPythonAndOutput{\programmingWithPythonCodeRepo}{03_conditionals}{if_else_example.py}{--args format}{conditionals:if_else}{%
6969
An example for using the \pythonil{if ... else}\pythonIdx{if{\idxdots}else} statement.}%
@@ -133,7 +133,7 @@
133133
\FloatBarrier%
134134
\endhsection%
135135
%
136-
\hsection{The \texttt{if{\idxdots}elif{\idxdots}else} Statement}%
136+
\hsection{The \texttt{if}\idxdots\texttt{elif}\idxdots\texttt{else} Statement}%
137137
%
138138
\gitPythonAndOutput{\programmingWithPythonCodeRepo}{03_conditionals}{if_elif_example.py}{--args format}{conditionals:if_elif}{%
139139
An example for using the \pythonil{if ... elif}\pythonIdx{if{\idxdots}elif{\idxdots}else} statement.}%
@@ -214,7 +214,7 @@
214214
\FloatBarrier%
215215
\endhsection%
216216
%
217-
\hsection{The Inline \texttt{if{\idxdots}else} Statement}%
217+
\hsection{The Inline \texttt{if}\idxdots\texttt{else} Statement}%
218218
\label{sec:inlineIfThenElse}%
219219
%
220220
\gitPythonAndOutput{\programmingWithPythonCodeRepo}{03_conditionals}{if_else_could_be_inline.py}{--args format}{conditionals:if_else_could_be_inline}{%

text/main/controlFlow/loops/loops.tex

+120-1
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,10 @@
409409
There can always be inputs that cause endless oscillations between values or the appearance of \pythonilIdx{nan} values (see \cref{sec:float:special}).%
410410
}%
411411
%
412-
The only reason why we did it here is that it looks nice and is easy to read as a functional and yet brief example for a \pythonilIdx{while} loop.
412+
The only reason why we did it here is that it looks nice and is easy to read as a functional and yet brief example for a \pythonilIdx{while} loop.\footnote{%
413+
To enforce that no oscillation between two neighboring \pythonil{float} values could occur, one could use the condition \pythonil{abs(old_guess - guess) / max(old_guess, guess) >= 1e-15} instead. %
414+
This makes the loop stop when the relative difference between \pythonil{guess} and \pythonil{old_guess} becomes less than~$10^-{15}$.%
415+
}%
413416
Anyway, the loop condition necessitates us to store some value different from~\pythonil{1.0} in \pythonil{old_guess} initially (and we picked~\pythonil{0.0}).
414417
In the loop, first the current guess becomes the old guess via \pythonil{old_guess = guess}.
415418
Then we update the guess as specified in \cref{eq:heronGuessUpdate}, by setting \pythonil{guess = 0.5 * (guess + number / guess)}.
@@ -422,5 +425,121 @@
422425
\FloatBarrier%
423426
\endhsection%
424427
%
428+
%
429+
\hsection{The \texttt{else} Statement at the Bottom of Loops}%
430+
%
431+
Now, you have learned before that we can leave a loop body immediately by calling the~\pythonilIdx{break} statement.
432+
Let's say that we want to perform a certain action \emph{after} the loop if the loop has completed normally, i.e., if \pythonilIdx{break} was not invoked.
433+
We can do this by declaring a Boolean variable denoting whether the loop has completed normally before the loop and initializing it with \pythonil{True}.
434+
If we invoke \pythonilIdx{break}, then we would first set this variable to \pythonil{False}.
435+
After the loop, we could place an \pythonil{if} to check if the variable is still \pythonil{True} and then execute the action.
436+
This is totally fine, but \python\ offers us a much less verbose method:
437+
Using the \pythonilIdx{else} statement at the bottom of the loop, which works for both \pythonilIdx{for} and \pythonilIdx{while} loops:%
438+
%
439+
\begin{pythonSyntax}
440+
for loopVariable in sequence:
441+
loop body statement 1
442+
loop body statement 2
443+
# ...
444+
else:
445+
code executed if break not invoked 1
446+
# ...
447+
normal statement 1
448+
normal statement 2
449+
# ...
450+
\end{pythonSyntax}
451+
%
452+
\begin{pythonSyntax}
453+
while booleanExpression:
454+
loop body statement 1
455+
loop body statement 2
456+
# ...
457+
else:
458+
code executed if break not invoked 1
459+
# ...
460+
normal statement 1
461+
normal statement 2
462+
# ...
463+
\end{pythonSyntax}
464+
%
465+
\gitPythonAndOutput{\programmingWithPythonCodeRepo}{04_loops}{while_loop_search.py}{--args format}{loops:while_loop_search}{%
466+
We implement binary search~\cite{K1998SAS,H2024POICBS,B1999PP} using a \pythonilIdx{while} loop with a \pythonilIdx{break} statement.}%
467+
%
468+
We now use this construct to implement a binary search~\cite{K1998SAS,H2024POICBS,B1999PP}.
469+
Binary search works is an algorithm that finds the index of an element in a \emph{sorted} sequence \pythonil{data} of values.
470+
The core concept of binary search is that we consider a segment~$S$ of the list in which the element~$E$ we search may be contained.
471+
In each step of the algorithm, we want to reduce the size of this segment by ruling out the \emph{half} in which~$E$ cannot be.
472+
We do this looking at the element~$M$ right in the middle.
473+
Now, the whole sequence~\pythonil{data} and, hence, also the segment~$S$, are sorted.
474+
If~$M$ is bigger than~$E$, then $E$ must be in the lower half, i.e., in the sub-segment from the start of~$S$ to right before~$M$.
475+
If~$M$ is smaller than~$E$, then $E$ must be in the upper half, i.e., in the sub-segment starting right after~$M$ and reaching until the end of~$S$.
476+
Otherwise, we must have found the element.
477+
This means in one step we have effectively halved the size of~$S$.
478+
If~$n=\pythonil{len(data)}$, then we can do this at most $\log_2 n$~times and the time complexity of binary search is in~\bigOb{\log n}~\cite{K1998SAS,H2024POICBS,B1999PP}.%
479+
%
480+
\begin{sloppypar}%
481+
In~\cref{lst:loops:while_loop_search}, we want to find the indices of some characters in the alphabetically sorted string~\pythonil{data = "abdfjlmoqsuvwyz"}.
482+
Of course, there is the \pythonilIdx{find}\pythonIdx{str!find} method that can do this, but we want to take advantage of the fact that the characters in \pythonil{data} are sorted.
483+
We search for the six characters~\pythonil{"a"}, \pythonil{"c"}, \pythonil{"o"}, \pythonil{"p"}, \pythonil{"w"}, and~\pythonil{"z"}.
484+
Four of them are in \pythonil{data}, but \pythonil{"c"} and \pythonil{"p"} are not.
485+
To search them anyway, we let a variable \pythonil{search} iterate over the list \pythonil{["a", "c", "o", "p", "w", "z"]}.%
486+
\end{sloppypar}%
487+
%
488+
Now in the inner loop, we implement the binary search.
489+
This search will maintain and update two indices \pythonil{lower} and \pythonil{upper}.
490+
\pythonil{lower} is the inclusive lower end of the segment in which \pythonil{search} could be contained.
491+
It is therefore initialized with~\pythonil{0}.
492+
\pythonil{upper} is the exclusive upper end of the segment in which \pythonil{search} could be contained.
493+
We initialize it with \pythonil{len(data)}; as it is \emph{exclusive}, it will be 1 bigger than the largest valid index \pythonil{len(data) - 1}.
494+
Our segment is not empty, i.e., contains at least one element, as long as \pythonil{lower < upper}.
495+
This is therefore the loop condition of the inner loop.
496+
497+
Inside the binary search loop, we first compute the mid index as \pythonil{mid = (lower + upper) // 2}\footnote{%
498+
Interestingly, this works only because \python~3 has integers of infinite range (see \cref{sec:int}). %
499+
In programming languages like \pgls{C} or \pgls{Java} where integer types have limited ranges, we need to do \pythonil{mid = lower + (upper - lower) // 2}~\cite{H2024POICBS}.%
500+
}.
501+
We obtain the value \pythonil{mid_str} as the single character at that index via \pythonil{mid_str = data[mid]}.
502+
503+
We know that if \pythonil{mid_str < search}, then our character \pythonil{search} cannot be located at any index in the range~\intRange{0}{\pythonil{mid}}.
504+
So in this case, we can update the \emph{inclusive} index \pythonil{lower} to become \pythonil{mid + 1}.
505+
Otherwise, if \pythonil{mid_str > search}, then we know that \pythonil{search} could not possibly located anywhere in the range~\intRange{\pythonil{mid}}{\pythonil{len(data) - 1}}.
506+
We thus would set the \emph{exclusive} index \pythonil{upper} to \pythonil{mid}, which excludes all items starting at index \pythonil{mid} from further consideration.%
507+
%
508+
\begin{sloppypar}%
509+
Now if neither \pythonil{mid_str < search} nor \pythonil{mid_str > search} were \pythonil{True}, it must be that \pythonil{mid_str == search}.
510+
This means that we found the location of \pythonil{search} -- it is at the index~\pythonil{mid}.
511+
Therefore, we print this result to the output.
512+
(Notice that the \pythonilIdx{!r} format specifiers in the \pgls{fstring} we use add the nice single quotes around \pythonil{search} and \pythonil{data}.)
513+
After printing the information, we exit the \pythonilIdx{while} loop using the \pythonilIdx{break} statement.%
514+
\end{sloppypar}%
515+
%
516+
Now, there is the possibility that we cannot find \pythonil{search} in \pythonil{data} because it is simple in there.
517+
In this case, we will never print the output and also not leave the loop with \pythonilIdx{break}.
518+
In each iteration that does not end with \pythonilIdx{break}, we will either increase \pythonil{lower} or decrease \pythonil{upper}.
519+
Thus, eventually \pythonil{lower < upper} will become \pythonil{False}.
520+
This is when the \pythonilIdx{else} statement at the bottom of the loop is executed.
521+
Then and only then we print that we did not find the string.%
522+
\endhsection%
523+
%
524+
\hsection{Summary}%
525+
With this, we depart from the subject of loops.
526+
We have learned two ways to execute code iteratively:
527+
The \pythonilIdx{for} loop iterates of sequences of objects, which can either be \pythonilsIdx{range} of numbers or arbitrary collections.
528+
The \pythonilIdx{while} loop permits us to specify an arbitrary Boolean expression as loop condition.
529+
In the bodies of both loops, we can jump to the next iteration at any time using the \pythonilIdx{continue} statement or we can exit the loops entirely using the \pythonil{break} statement.
530+
Finally, placing an \pythonilIdx{else} statement at the bottom of the loop allows us to execute some code when the loop completes regularly, i.e., not via~\pythonilIdx{break}.
531+
532+
We now have some nice tools in our hands.
533+
We can create code that branches and conditionally performs actions via \pythonil{if}-\pythonil{else}.
534+
And we can repeatedly perform actions via \pythonil{for} and \pythonil{while}.
535+
Our examples also have become more elaborate and interesting.
536+
We can now approximate~\numberPi\ with arbitrarily many steps of the LIU Hui's approach.
537+
We can implement Heron's Method to compute the square root of a number and we perform binary search over arbitarily large (sorted) data.
538+
We can do quite a lot!
539+
540+
What we cannot yet do is to have a block of code that we want to re-use in \emph{different} places.%
541+
\endhsection%
542+
%
543+
\FloatBarrier%
425544
\endhsection%
426545
%

0 commit comments

Comments
 (0)