Skip to content

Commit 0bf417b

Browse files
committed
finished debugging
1 parent 13ce19d commit 0bf417b

File tree

2 files changed

+117
-24
lines changed

2 files changed

+117
-24
lines changed

text/main/classes/dunder/dunder.tex

+116-24
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,14 @@
543543
The Boolean variable \pythonil{negative} is set to \pythonil{True} if \pythonil{a < 0} and to \pythonil{negative} otherwise.
544544
We then make sure that \pythonil{a} is positive by computing~\pythonil{abs(a)}.
545545
Then we also copy the \pgls{denominator} into a variable~\pythonil{b}.
546-
546+
We will not change the local variable~\pythonil{b} in our function, so we mark it with the \pgls{typeHint}~\pythonilIdx{Final}.%
547+
%
548+
\bestPractice{varFinal}{%
549+
Every time you declare a variable that you do not intend to change, mark it with the \pgls{typeHint}~\pythonilIdx{Final}. %
550+
On one hand, this conveys the intention \inQuotes{this does not change} to anybody who reads the code. %
551+
On the other hand, if you do accidentally change it later, tools like \mypy\ can notify you about this error in your code.%
552+
}%
553+
%
547554
We now want to fill a list~\pythonil{digits} with the digits representing the fraction in a \pythonil{while}~loop.
548555
Let us assume that our fraction is~$-\frac{179}{16}$.
549556
Then \pythonil{negative == True}, \pythonil{a = 179}, and \pythonil{b = 16}.
@@ -638,6 +645,9 @@
638645
Interestingly, they fail!
639646
The output tells us that \pythonil{Fraction(91995, 100000).decimal_str(3)} does not yield the expected~\pythonil{"0.92"}.
640647
Instead, we get \pythonil{"0.9110"}.
648+
Where does the trailing zero come from?
649+
And why do we have two 1s?
650+
Even if we did not round correctly, at least we should get something like~0.919, but certainly not~0.911?
641651

642652
\begin{figure}%
643653
\centering%
@@ -687,8 +697,13 @@
687697
What we did not see in that output is that actually \emph{two} \pglspl{doctest} failed.
688698
A left-click on the second failed test in~\cref{fig:dunder:doctests3} tells us that \pythonil{Fraction(99995, 100000).decimal_str(4)} did not yield the expected~\pythonil{"1"}.
689699
Instead, it produced~\pythonil{"0.99910"}.
700+
Why is there a \pythonil{"0"} at the end of our number?
701+
Where did it come from?
702+
Zeros at the end should not be possible with our code.
703+
Also, there are four~9s in our number, not three.
704+
What went wrong here?
690705

691-
While you might have guessed the problem when reading our code for \pythonil{decimal_str}, let us here assume that we are clueless why these tests fail.
706+
We are clueless why these tests fail.
692707
The question arises:
693708
What can we do?
694709

@@ -754,6 +769,7 @@
754769
\caption{Using the \debugger\ in \pycharm.}%
755770
\label{fig:dunder:debugA}%
756771
\end{figure}%
772+
\afterpage{\clearpage}%
757773
%
758774
In \pycharm, we can apply the \debugger\ to a complete program, but also to \pglspl{doctest}.
759775
This is what we will do in \cref{fig:dunder:debugA}.
@@ -832,6 +848,7 @@
832848
\caption{Using the \debugger\ in \pycharm.}%
833849
\label{fig:dunder:debugB}%
834850
\end{figure}%
851+
\afterpage{\clearpage}%
835852

836853
This test case is already successful, so we are not interested in it.
837854
Among the symbols in \menu{Debug} register, we click \pycharmDebuggerResume, which will let the program continue its execution~(\cref{fig:dunder:debug07}).
@@ -845,9 +862,8 @@
845862
When the debugger arrives at the fifth test case, \pythonil{Fraction(1235, 1000)}, we find that this fraction has been normalized correctly to~$\frac{247}{200}$.
846863
Nonetheless, we can skip this test case via \keys{F9}, too, because we know that it will succeed~(\cref{fig:dunder:debug11}).
847864
This takes us to the last successful \pgls{doctest} case, \pythonil{Fraction(99995, 100000)}, which corresponds to~$\frac{19999}{20000}$ in \cref{fig:dunder:debug12}.
848-
After skipping it by pressing~\pycharmDebuggerResume, we will finally arrive at the cases that did fail and which we hence want to investigate step-by-step.%
849-
\clearpage%
850-
%
865+
After skipping it by pressing~\pycharmDebuggerResume, we will finally arrive at the cases that did fail and which we hence want to investigate step-by-step.
866+
851867
\begin{figure}%
852868
\ContinuedFloat%
853869
\centering%
@@ -902,6 +918,7 @@
902918
\caption{Using the \debugger\ in \pycharm.}%
903919
\label{fig:dunder:debugC}%
904920
\end{figure}%
921+
\afterpage{\clearpage}%
905922
%
906923
\begin{sloppypar}%
907924
\Cref{fig:dunder:debug13} shows that we now arrived at the beginning of the failing \pgls{doctest} case \pythonil{Fraction(91995, 100000).decimal_str(3)}.
@@ -960,27 +977,31 @@
960977
\strut\hfill\strut%
961978
%
962979
\subfloat[][%
963-
Using the \debugger\ in \pycharm.%
980+
\pythonil{digits} now contains the result of \pythonil{a // b}, i.e., is~\pythonil{[0]}. %
981+
We press~\keys{F8}.%
964982
\label{fig:dunder:debug22}%
965983
]{\tightbox{\includegraphics[width=0.48\linewidth]{\currentDir/debug22}}}%
966984
%
967985
\\[10pt]%
968986
%
969987
\subfloat[][%
970-
Using the \debugger\ in \pycharm.%
988+
\pythonil{a} now is \pythonil{183990}. %
989+
We press~\pycharmDebuggerStepOver\ to continue.%
971990
\label{fig:dunder:debug23}%
972991
]{\tightbox{\includegraphics[width=0.48\linewidth]{\currentDir/debug23}}}%
973992
%
974993
\strut\hfill\strut%
975994
%
976995
\subfloat[][%
977-
Using the \debugger\ in \pycharm.%
996+
We hit the \keys{F8} key to continue. %
997+
The loop condition is still met, so the first line of the loop body is marked again.%
978998
\label{fig:dunder:debug24}%
979999
]{\tightbox{\includegraphics[width=0.48\linewidth]{\currentDir/debug24}}}%
9801000
%
9811001
\caption{Using the \debugger\ in \pycharm.}%
9821002
\label{fig:dunder:debugD}%
983-
\end{figure}
1003+
\end{figure}%
1004+
\afterpage{\clearpage}%
9841005

9851006
This executes \pythonil{b = self.b}.
9861007
Thus, the new local variable \pythonil{b} with value \pythonil{20000} is created in~\cref{fig:dunder:debug19}.
@@ -995,49 +1016,61 @@
9951016
In \cref{fig:dunder:debug21}, we find that now the first line of the loop's body is marked.
9961017
This means that \pythonil{a != 0} and \pythonil{len(digits) <= max_frac} are both~\pythonil{True}.
9971018
And they should be, since \pythonil{a} is \pythonil{18399}, \pythonil{len(digits)} if~0, and \pythonil{max_frac} is~3.
998-
We press~\pycharmDebuggerStepOver.
1019+
We press the \pycharmDebuggerStepOver~button to execute the first line of the loop body.
1020+
1021+
\pythonil{digits.append(a // b)} will append the value \pythonil{18399 // 20000} to the list~\pythonil{digits}.
1022+
As this is the result of an integer division where the \pgls{denominator} is larger than the \pgls{numerator}, \pythonil{digits} is now~\pythonil{[0]}~(\cref{fig:dunder:debug22}).
1023+
We press \keys{F8} to continue.
1024+
1025+
Now, \pythonil{a = 10 * (a \% b)} is executed.
1026+
Since \pythonil{18399 \% 20000} is still \pythonil{18399}, \pythonil{a} becomes \pythonil{183990} in \cref{fig:dunder:debug23}.
1027+
The head of the loop is now marked again.
1028+
We press the \pycharmDebuggerStepOver~button to let execute it.
1029+
1030+
In \cref{fig:dunder:debug24}, we again hit \keys{F8}.
1031+
The loop condition is still met, so the first line in the loop body is marked again.
9991032

10001033
\begin{figure}%
10011034
\ContinuedFloat%
10021035
\centering%
10031036
%
10041037
\subfloat[][%
1005-
Using the \debugger\ in \pycharm.%
1038+
\pythonil{9} gets appended to \pythonil{digits}.%
10061039
\label{fig:dunder:debug25}%
10071040
]{\tightbox{\includegraphics[width=0.48\linewidth]{\currentDir/debug25}}}%
10081041
%
10091042
\strut\hfill\strut%
10101043
%
10111044
\subfloat[][%
1012-
Using the \debugger\ in \pycharm.%
1045+
\pythonil{a} gets updated to \pythonil{39900}.%%
10131046
\label{fig:dunder:debug26}%
10141047
]{\tightbox{\includegraphics[width=0.48\linewidth]{\currentDir/debug26}}}%
10151048
%
10161049
\\[10pt]%
10171050
%
10181051
\subfloat[][%
1019-
Using the \debugger\ in \pycharm.%
1052+
The loop condition is still met.%
10201053
\label{fig:dunder:debug27}%
10211054
]{\tightbox{\includegraphics[width=0.48\linewidth]{\currentDir/debug27}}}%
10221055
%
10231056
\strut\hfill\strut%
10241057
%
10251058
\subfloat[][%
1026-
Using the \debugger\ in \pycharm.%
1059+
\pythonil{1} gets appended to \pythonil{digits}.%
10271060
\label{fig:dunder:debug28}%
10281061
]{\tightbox{\includegraphics[width=0.48\linewidth]{\currentDir/debug28}}}%
10291062
%
10301063
\\[10pt]%
10311064
%
10321065
\subfloat[][%
1033-
Using the \debugger\ in \pycharm.%
1066+
\pythonil{a} gets updated to \pythonil{199000}.%
10341067
\label{fig:dunder:debug29}%
10351068
]{\tightbox{\includegraphics[width=0.48\linewidth]{\currentDir/debug29}}}%
10361069
%
10371070
\strut\hfill\strut%
10381071
%
10391072
\subfloat[][%
1040-
Using the \debugger\ in \pycharm.%
1073+
The loop condition is still met.%
10411074
\label{fig:dunder:debug30}%
10421075
]{\tightbox{\includegraphics[width=0.48\linewidth]{\currentDir/debug30}}}%
10431076
%
@@ -1046,51 +1079,110 @@
10461079
\label{fig:dunder:debugE}%
10471080
\end{figure}%
10481081
%
1049-
%
10501082
\begin{figure}%
10511083
\ContinuedFloat%
10521084
\centering%
10531085
%
10541086
\subfloat[][%
1055-
Using the \debugger\ in \pycharm.%
1087+
\pythonil{9} gets appended to \pythonil{digits}.%
10561088
\label{fig:dunder:debug31}%
10571089
]{\tightbox{\includegraphics[width=0.48\linewidth]{\currentDir/debug31}}}%
10581090
%
10591091
\strut\hfill\strut%
10601092
%
10611093
\subfloat[][%
1062-
Using the \debugger\ in \pycharm.%
1094+
\pythonil{a} gets updated to \pythonil{190000}.%
10631095
\label{fig:dunder:debug32}%
10641096
]{\tightbox{\includegraphics[width=0.48\linewidth]{\currentDir/debug32}}}%
10651097
%
10661098
\\[10pt]%
10671099
%
10681100
\subfloat[][%
1069-
Using the \debugger\ in \pycharm.%
1101+
Since the loop condition evaluates to \pythonil{False}, the cursor is now at the next line after the loop body. %
1102+
This line checks whether we should round up the next digit. %
1103+
We hit \keys{F8}.%
10701104
\label{fig:dunder:debug33}%
10711105
]{\tightbox{\includegraphics[width=0.48\linewidth]{\currentDir/debug33}}}%
10721106
%
10731107
\strut\hfill\strut%
10741108
%
10751109
\subfloat[][%
1076-
Using the \debugger\ in \pycharm.%
1110+
The condition of the \pythonil{if} is met, we can now execute its body. %
1111+
We press the \pycharmDebuggerStepOver~button.%
10771112
\label{fig:dunder:debug34}%
10781113
]{\tightbox{\includegraphics[width=0.48\linewidth]{\currentDir/debug34}}}%
10791114
%
10801115
\\[10pt]%
10811116
%
10821117
\subfloat[][%
1083-
Using the \debugger\ in \pycharm.%
1118+
Our code for rounding up actually turned the last digit into a~\pythonil{10}! %
1119+
Because we did not consider that rounding up could cause the next~9 to become a 10, which should be represented as a~0 and lead to the next digit to be rounded up as well.%
10841120
\label{fig:dunder:debug35}%
10851121
]{\tightbox{\includegraphics[width=0.8\linewidth]{\currentDir/debug35}}}%
10861122
%
10871123
\caption{Using the \debugger\ in \pycharm.}%
10881124
\label{fig:dunder:debugF}%
10891125
\end{figure}%
1090-
%
1126+
\afterpage{\clearpage}%
1127+
1128+
In \cref{fig:dunder:debug25,fig:dunder:debug26,fig:dunder:debug27,fig:dunder:debug29,fig:dunder:debug30,fig:dunder:debug31,fig:dunder:debug32} we work our way through the loop in the same way, by pressing \keys{F8} repeatedly.
1129+
First, \pythonil{9} gets appended to \pythonil{digits}, then \pythonil{a} gets updated to \pythonil{39900}.
1130+
In the following iteration, \pythonil{1} gets appended to \pythonil{digits}, then \pythonil{a} gets updated to \pythonil{199000}.
1131+
Then, \pythonil{9} gets appended to \pythonil{digits} and \pythonil{a} gets updated to \pythonil{190000}.
1132+
1133+
At this stage, \pythonil{digits} has become~\pythonil{[0, 9, 1, 9]}.
1134+
Since \pythonil{max_frac} is \pythonil{3}, \pythonil{len(digits) <= max_frac} is no longer~\pythonil{True}.
1135+
In \cref{fig:dunder:debug32}, we have arrived back at the head of the loop.
1136+
When we hit \keys{F8}, the loop condition is evaluated again, but this time it evaluates to~\pythonil{False}.
1137+
The loop terminates and the cursor is placed on the next line of code after the loop.
1138+
1139+
If we look at what was computed so far, we find that everything is exactly as it should be.
1140+
We want to translate the fraction~$\frac{91995}{100000}$ to a decimal string with three fractional digits.
1141+
So far, we got the digits~0, 9, 1, and~9.
1142+
1143+
The next line of code, \pythonil{if (a // b) >= 5}, is supposed to check whether we should round up the last digit.
1144+
Since \pythonil{a} is \pythonil{190000} and \pythonil{b} is still \pythonil{20000}, \pythonil{a // b} is~\pythonil{9}.
1145+
So the condition should be met.
1146+
In \cref{fig:dunder:debug32}, we press the \pycharmDebuggerStepOver~button to find it out.
1147+
1148+
A look at \cref{fig:dunder:debug35} reveals the bug in our code:
1149+
In order to round up, we incremented the last number in our list~\pythonil{digits}.
1150+
\pythonil{digits} was~\pythonil{[0, 9, 1, 9]}.
1151+
So it is~\pythonil{[0, 9, 1, 10]}.
1152+
1153+
The strange trailing zeros in our output were not separate digits.
1154+
They were the zeros of a ten.
1155+
We did not consider that, when rounding up, we do not just have simple cases like~1.25 which we can round to~1.3 by only incrementing one digit.
1156+
We can have cases like~0.9999, which rounds up to~1, even if we want three fractional digits of precision.
1157+
We can stop the debugging here and go back to our code.
1158+
10911159
\gitPython{\programmingWithPythonCodeRepo}{09_dunder/fraction.py}{--args format --labels part_6}{dunder:fraction:part_6}{%
10921160
The repaired Part~6 of the \pythonil{Fraction} class: A correct \pythonil{decimal_str} method.}%
1093-
%
1161+
1162+
In \cref{dunder:fraction:part_6}, we revisit the \pythonil{decimal_str} method of our class~\pythonil{Fraction}.
1163+
Our rounding-up code becomes more complex:
1164+
First, we need to loop over all fractional digits, from the end of the list \pythonil{digits} forward:
1165+
\pythonil{for i in range(len(digits) - 1, 0, -1)} does this.
1166+
If \pythonil{len(digits) == 5}, then \pythonil{range(len(digits) - 1, 0, -1)} iterates over the numbers 4, 3, 2, and~1.
1167+
We increment the digit at index~\pythonil{i} by~1.
1168+
If it does not become~10, then we can stop the loop via~\pythonilIdx{break}.
1169+
If it did become~10, then we set it to zero and continue the loop.
1170+
This will increment the next digit, and so on.
1171+
If we arrive at index~1 and still need to continue, the regular loop execution ends anyway.
1172+
1173+
Then, the \pythonil{else} statement is hit.
1174+
Recall from \cref{sec:loopElse} that the \pythonil{else}\pythonIdx{for!else} statement at the bottom of a loop is \emph{only} executed if the loop finished regularly, i.e., if no \pythonil{break} statement was executed.
1175+
Therefore, if and only if the digit at index~1 also became~10 and was then set to~0, we increment the digit at index~0.
1176+
This digit represents the integer part of our fraction.
1177+
Here, it is totally OK to round a~9 to a~10.
1178+
For example, 9.999 can be rounded to 10, and 1239.9 can be rounded to 1240.
1179+
1180+
This new code for rounding numbers may introduce zeros at the end of our string.
1181+
We just gobble them up with an additional \pythonil{while} loop directly after the rounding.
1182+
And with this, we are done.
1183+
We have working code converting fractional numbers to decimal strings.
1184+
All the \pglspl{doctest} now pass.
1185+
10941186
\gitPython{\programmingWithPythonCodeRepo}{09_dunder/fraction_sqrt.py}{--args format}{dunder:fraction_sqrt}{%
10951187
Using the \pythonil{Fraction} class to compute square roots.}%
10961188
%

text/main/controlFlow/loops/loops.tex

+1
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@
434434
%
435435
%
436436
\hsection{The \texttt{else} Statement at the Bottom of Loops}%
437+
\label{sec:loopElse}%
437438
%
438439
Now, you have learned before that we can leave a loop body immediately by calling the~\pythonilIdx{break} statement.
439440
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.

0 commit comments

Comments
 (0)