Title: Lecture 5: Repetition
1Lecture 5 Repetition
2The Need for Repetition
- At the compile time, we cannot generally know how
long the program is going to run - If the program reads its input from an arbitrary
data file, we can't know beforehand how long this
file will be - Some interactive game between human and machine
can take arbitrarily many moves, and the human
might want to play the game arbitrarily many
times - Language must have some mechanism to keep doing
something over and over, as many times as it's
needed - Recursion works in principle, but has its
downsides - Explicit looping often simpler and superior
- Three types of loop structures, all equally
powerful in principle, in practice handy in
different situations
3While-Loop
- Of the three different loop structures offered in
C, while-loops are conceptually the simplest - Resembles if-statement, but does not have an else
- Conceptually, keep executing the loop body again
and again while the condition is true - Loop terminates when the loop condition becomes
false - Note that the truth of the loop condition is not
checked constantly between every statement in the
loop body, but only at the beginning of the loop,
and then again between the repetitions of the
loop body - Condition may be temporarily false during the
loop, but become true again before it is checked
4Countdown With While
- / Outputs a countdown from n to 0. /
- int countdown_with_while(int n)
- int c n / Good style to use a separate
loop counter / - while(c gt 0)
- printf("d... ", c)
- c c - 1
-
- printf("Liftoff!\n")
5Behaviour of While-Loops
- A while-loop will execute its body zero times, if
the condition is false to begin with (for
example, call the countdown function with a
negative n) - Normally, a while-loop will execute its body some
finite number of times and then terminate - It is possible to have an infinite loop that
never terminates (for example, forget to write in
the statement to decrement c in the countdown
function) - Some sort of infinite loops are necessary for
programs that are supposed to run forever (until
they are explicitly terminated by user), but in
this course, infinite loops are logic errors - Of course, the loop itself may be infinite
while(1), but you jump out of it, if some
condition is true
6Do-While Loop
- The second type of loop offered in C is
the do-while - Clearly the least commonly used of the three one
famous study of real world programs revealed that
out of all loops in them, only 1 are do-while - Do-while behaves exactly the same way as the
while-loop, but is guaranteed to execute its body
at least once before it starts looking at the
loop condition - The possibility of executing the loop body zero
times does not exist, even if the condition is
initially false - do-while is most useful in situations where
testing the condition simply does not make any
sense until the loop body has been executed once
7Asking User for Constrained Input
- / Keep asking the user for a number until he
types in a number that is between min and max,
inclusive. / - int ask_for_number(int min, int max)
- int num
- do
- printf("Enter integer between d and d
", min, max) - scanf("d", num)
- while(num lt min num gt max)
- return num
8A Sentinel-Controlled Loop
- int sum_of_inputs(int min, int max)
- int sum 0, curr
- do
- curr ask_for_number(min, max)
- sum curr
- while(curr ! 0) / Stop when input is 0
/ - return sum
9Rationale for For-Loops
- The third type of loop structure in C is suitable
for the common task of iterating through the
values of a range of integers, one value at the
time - For example, go through the numbers from 1 to 100
- The for-loop header defines this range, and the
loop body contains the statements you execute for
each value - To define an integer range, you need to define
three things where it begins (1), where it ends
(100), and the step size between the consecutive
values of the range (1) - It is not a coincidence that the header of a
for-loop consists of three parts, for the three
things that define a range - We can still use while like in countdown, but
using a for-loop makes it clear that we are going
through a range
10Defining a Range With For-Loop
- Syntactically, a for-loop is of the form for(I
C U) B - The initialization I defines where the range
starts, and is an assignment to the loop counter
variable that keeps track of which value we are
currently at - The condition C defines where the range ends, but
does so in a negated way, be defining where the
range still continues - As long as the condition is true, we are still
inside the range and the loop executes its body B
one more time - The update U is an assignment to the loop counter
that calculates the next value in the range - For example for(c 1 c lt 100 c)
11Some Example Ranges
- Even numbers from -100 to 100
- for(c - 100 c lt 100 c 2) ...
- All numbers from start to end
- for(c start c lt end c) ...
- Powers of two from 1 to 65536 ( 216)
- for(c 1 c lt 65536 c c 2) ...
- Powers of three up to 3n
- for(c 0, p 1 c lt n c, p p 3) ...
- For convenience, the last example uses two
separate loop variables, separated by the comma
operator that can be used to combine two
expressions into one, used where the language
syntax requires one expression
12Example Countdown With For
- / Outputs a countdown from n to 0. Note that
using a for loop, the loop body does not need to
talk about the logic of iterating through the
values, but can just contain the statements that
you want to execute for each value. / - void countdown_with_for(int n)
- int c / Still have to declare the loop
counter variable / - for(c n c gt 0 c--)
- printf("d... ", c)
-
- printf("Liftoff!\n")
13Example Output a List of Numbers
- / Output the numbers from start to end separated
by commas, taking care that you don't write a
trailing comma after the last number in the
series. / - void output_numbers(int start, int end)
- int c
- for(c start c lt end c)
- printf("d", c)
- if(c lt end) printf(", ")
-
14Example Rolling Multiple Dice
- / Roll the dice given number of times and return
total. / - int roll_dice(int n_dice, int sides)
- int total 0
- int roll
- for(roll 1 roll lt n_dice roll)
- total 1 rand() sides
-
- return total
15Example Harmonic Sum
- / H_n 1 1/2 1/3 ... 1/n /
- double harmonic(int n)
- double result 0.0
- int i
- for(i 1 i lt n i)
- result 1.0 / i
-
- return result
16Example Primality Testing
- int is_prime(int n)
- int c
- if(n lt 2) return 0
- if(n 2) return 1
- if(n 2 0) return 0
- / Iterate through odd numbers from 3 to
sqrt(n) / - for(c 3 c c lt n c 2)
- / For each such number, check if it
divides n. / - if(n c 0) return 0
-
- return 1 / Only after we checked all
divisors... /
17Example Goldbach Conjecture
- / Check if the positive even number n gt 4 can be
expressed as a sum of two prime numbers. / - int verify_goldbach(int n)
- int a
- for(a 3 a lt n / 2 a 2)
- if(is_prime(a) is_prime(n - a))
return 1 -
- / This should not happen, assuming valid n.
/ - return 0
18Example Fibonacci Numbers
- / 1, 1, 2, 3, 5, 8, 13, 21, 34, ... /
- int fib(int n)
- int a 1, b 1, i, c
- for(i 2 i lt n i)
- c a b / Next number in series /
- a b / Shift the current two
numbers / - b c
-
- return b
19Example Debt Calculator
- int debt(double debt, double interest, double
payment) - int years 0
- double old_debt
- while(debt gt 0)
- old_debt debt
- debt debt interest / 100.0 -
payment - if(old_debt lt debt) return -1
- years
-
- return years
20Example Binary Power
- double power(double base, int exp)
- double result 1.0
- if(exp lt 0) return 1.0 / power(base, -exp)
- while(exp gt 0)
- if(exp 2 1) result result
base - base base base
- exp exp / 2
-
- return result
21Example Solving a Root by Halving
- / Assume F is a continuous function of doubles
defined as a macro, and F(a) and F(b) initially
have different signs. / - double solve(double a, double b)
- double mid, fmid
- while(b - a gt 0.000001) / desired
precision / - mid (a b) / 2 fmid F(mid)
- if(fmid gt 0 F(a) gt 0) a mid
- else if(fmid lt 0 F(a) lt 0) a mid
- else b mid
-
- return (a b) / 2
22Example Hailstone Numbers
- int count_hailstone(int n)
- int steps 0
- while(n gt 1)
- steps
- if(n 2 0) n n / 2
- else n 3 n 1
-
- return steps
-
- / The famous open Collatz conjecture says that
for any starting value n, this process will reach
1 in finite time. /
23Nested Loops
- Since the body of any looping statement can be
any series of statements, these statements can be
conditions and even other inner loops nested
inside the outer loop - It might be first difficult to intuit the
behaviour of nested loops, but this becomes
easier when you just think of how an ordinary
clock works - To count through one day, the hour counter goes
through the numbers from 0 to 23 - For each hour, the minute counter goes through
the numbers from 0 to 59, starting again the next
hour - For each minute, the second counter goes through
the numbers from 0 to 59, starting again the next
minute - Implemented next as three nested loops
24Counting Time With Nested Loops
- void count_time_through_day()
- int hour, minute, second
- for(hour 0 hour lt 24 hour)
- for(minute 0 minute lt 60 minute)
- for(second 0 second lt 60
second) - / Whatever you do with the
current second... / -
-
-
25As Long As The Total Is Correct...
- To count to five, we humans count "one, two,
three, four, five", with or without using our
fingers to keep track - If something is supposed to happen a total of
five times, but the actual loop counter values
don't matter, we could just as well count "ten,
eleven, twelve, thirteen, fourteen" and it would
be just as correct - In computing, it is often convenient to start
counting from zero, instead of one - The canonical loop to do something exactly n
times goes - for(c 0 c lt n c) ...
26Off By One
- Writing loop logic, off by one is a common type
of logic error in which the loop runs one round
too many or too few - Do not try to fix this by mechanistically
converting the terminating condition from lt to
lt, or vice versa, but reason for what values the
loop is supposed to run - Common subtype is fencepost error if there is a
fencepost every 10 meters, and the fence is 100
meters long, how many fenceposts are there? - How many integers are there from 10 to 20?
- How about from -10 to 10?
- Fencepost errors can be avoided by using
zero-based indexing, and expressing ranges with
an exclusive end
27Example Output A Rectangle
- / Output a filled rectangle of given character
using nested loops, outer loop counting the rows,
and for each row, the inner loop counting the
columns. / - void output_rectangle(int rows, int cols, char
ch) - int r, c
- for(r 0 r lt rows r)
- for(c 0 c lt cols c)
- printf("c", ch)
-
- printf("\n") / Newline after each row
/ -
28Example Output a Deck of Cards
- void output_deck()
- int suit, rank
- for(suit 0 suit lt 4 suit)
- for(rank 1 rank lt 13 rank)
- switch(rank)
- case 11 printf("jack of ")
break - case 12 printf("queen of ")
break - case 13 printf("king of ")
break - case 1 printf("ace of ") break
- default printf("d of ", rank)
break -
- switch(suit)
- case 0 printf("clubs\n") break
- case 1 printf("diamonds\n")
break - case 2 printf("hearts\n")
break - case 3 printf("spades\n")
break -
-
-
29Example Output Prime Factors
- void output_prime_factors(int n)
- int c 2 / First potential factor /
- while(n gt c)
- if(n c 0)
- printf("d ", c) / Eliminate one
factor / - n n / c
-
- else / Move on to try the next
potential divisor / - c (c 2)? 1 2
-
-
- printf("\n")
30Functions Simulating Nested Loops
- void output_one_row(int cols, char ch)
- int c
- for(c 0 c lt cols c)
- printf("c", ch)
-
- printf("\n")
-
- void output_rectangle(int rows, int cols, char
ch) - int r
- for(r 0 r lt rows r)
- output_one_row(cols, ch)
-
-
31Example Radix Conversions
- / First, a utility function to return the digit
character for the given digit. Assumes that digit
is at most 36. / - char get_digit(int d)
- if(d lt 10) return '0' d
- else return 'A' (d - 10)
-
- / The character '0' is not the same as zero.
Since characters are really integers, it is okay
to perform integer arithmetic on them. The
function assumes that characters '0', ..., '9'
are consecutive in the encoding table, as are
also 'A', ..., 'Z'./
32Radix Conversion Using While
- void output_int(int n, int base)
- int p 1
- while(p base lt n) p p base
- / p is now the highest power of base that is
lt n. / - while(p gt 0) / Descend through the powers
of base - printf("c", get_digit(n / p) )
- n n p
- p p / base
-
33Radix Conversion With Recursion
- / In this particular problem, recursion is so
much simpler that I just have to show it. The
loop was complicated because we have to output
digits starting with the most significant one,
which is harder to find than the least
significant digit. With recursion, this order
doesn't matter. / - void output_int(int n, int base)
- / Output the digits before the last one. /
- if(n gt base) output_int(n / base)
- / Output the last digit. /
- printf("c", get_digit(n base))
-
34Terminating Loops Prematurely
- Inside a loop, a break statement causes the
execution to immediately jump out of that loop,
and move to the statement following the loop - If used inside nested loops, break jumps out of
the innermost loop that contains this statement - If you want to jump out of an outer loop, you
need to first give this loop a label, an
identifier followed by a colon placed before the
loop, and use this label in the break statement
to specify which loop you want to terminate - In principle, any loop that uses break can be
rewritten as an equivalent loop that does not use
that statement - Jumping around tends to bring back the bad old
days of unstructured spaghetti code and makes the
program logic harder to follow
35Continue
- continue is a statement similar to break, but
instead of jumping out of the loop altogether, it
jumps to the next round of the loop, skipping the
rest of the body - Somewhat confusingly named, since this statement
would have been better titled maybe skip or next - Most commonly used to skip some of the values
inside the range for which the loop doesn't need
to do anything - Just like with break, you can use a label to
determine which loop you want to move to the next
round - In modern C, did you know you can put URL's in
code? - http//www.ryerson.ca
36Files With Unknown Amount of Data
- When reading data from a file so that the file
doesn't in its beginning tell us how many items
of data it contains, we need a way to tell that
we have reached the end of file - Function feof(f) tells you this for the file f
- Use something like while(!feof(f)) ...
- Alternatively, can use the fact that fscanf, in
addition to assigning the values, returns the
constant EOF if you have already reached the end
of file - Use something like while(fscanf(...) ! EOF)
... - Better when there is extra whitespace at end of
file - Also works with scanf just as well as with fscanf
37Example Variance from File
- double variance(FILE f)
- double sum 0, sum_sq 0, x, avg_sq, avg
- int total 0
- while(fscanf(f, "f", x) ! EOF)
- sum x sum_sq x x
- total
-
- avg sum / total
- avg_sq sum_sq / total
- return avg_sq - avg avg
-
- / It is the caller's responsibility to open and
close the file. /
38Raw Text, The Universal Data Format
- Storing numbers as text takes more space than
storing them as bytes, since you effectively use
only ten of the 256 possible values of each byte
(plus whatever whitespace, comma or other
separator characters you use) - Application-specific binary data formats can take
a lot less space compared to human-readable text - However, text can be easily edited and
transformed, and it does not impose hard
constraints on the maximum values of each data
field when stored in a fixed space - Prefer text files over custom binary formats,
unless there really is a large amount of data
(sound, pictures, video) - Can always compress and decompress text anyway
with standard compression algorithms
39 - "When I am working on a problem, I never think
about beauty. I think only of how to solve the
problem. But when I have finished, if the
solution is not beautiful, I know it is wrong." - Buckminster Fuller
- "John von Neumann draws attention to what seemed
to him a contrast. He remarked that for simple
mechanisms, it is often easier to describe how
they work than what they do, while for more
complicated mechanisms, it is usually the other
way around." - Edsger W. Dijkstra