lecture 5: repetition while not learned. the need for repetition at the compile time, we cannot...

39
Lecture 5: Repetition While not learned

Upload: howard-jennings

Post on 11-Jan-2016

213 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Lecture 5: Repetition

While not learned

Page 2: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

The 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

Page 3: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

While-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

Page 4: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Countdown 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 >= 0) {        printf("%d... ", c);        c = c - 1;    }    printf("Liftoff!\n");}

Page 5: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Behaviour 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

Page 6: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Do-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

Page 7: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Asking 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 < min || num > max);    return num;}

Page 8: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

A 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;}

Page 9: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Rationale 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

Page 10: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Defining 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 <= 100; c++)

Page 11: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Some Example Ranges

• Even numbers from -100 to +100:• for(c = - 100; c <= 100; c += 2) { ... }• All numbers from start to end:• for(c = start; c <= end; c++) { ... }• Powers of two from 1 to 65536 ( = 216):• for(c = 1; c <= 65536; c = c * 2) { ... }• Powers of three up to 3n:• for(c = 0, p = 1; c <= 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

Page 12: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Example: 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 >= 0; c--) {        printf("%d... ", c);    }    printf("Liftoff!\n");}

Page 13: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Example: 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 <= end; c++) {        printf("%d", c);        if(c < end) { printf(", "); }    }}

Page 14: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Example: 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 <= n_dice; roll++) {        total += 1 + rand() % sides;    }    return total;}

Page 15: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Example: 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 <= n; i++) {        result += 1.0 / i;    }    return result;}

Page 16: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Example: Primality Testing

int is_prime(int n) {    int c;    if(n < 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 <= 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... */}

Page 17: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Example: Goldbach Conjecture

/* Check if the positive even number n > 4 can be expressed as a sum of two prime numbers. */

int verify_goldbach(int n) {    int a;    for(a = 3; a <= n / 2; a += 2) {        if(is_prime(a) && is_prime(n - a)) { return 1; }    }    /* This should not happen, assuming valid n. */    return 0;}

Page 18: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Example: 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 <= n; i++) {        c = a + b; /* Next number in series */        a = b;      /* Shift the current two numbers */        b = c;    }    return b;}

Page 19: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Example: Debt Calculator

int debt(double debt, double interest, double payment) {    int years = 0;    double old_debt;    while(debt > 0) {        old_debt = debt;        debt += debt * interest / 100.0 - payment;        if(old_debt <= debt) { return -1; }        years++;    }    return years;}

Page 20: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Example: Binary Power

double power(double base, int exp) {    double result = 1.0;    if(exp < 0) { return 1.0 / power(base, -exp); }    while(exp > 0) {        if(exp % 2 == 1) { result = result * base; }        base = base * base;        exp = exp / 2;    }    return result;}

Page 21: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Example: 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 > 0.000001) { /* desired precision */        mid = (a + b) / 2; fmid = F(mid);        if(fmid > 0 && F(a) > 0) { a = mid; }        else if(fmid < 0 && F(a) < 0) { a = mid; }        else b = mid;    }    return (a + b) / 2;}

Page 22: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Example: Hailstone Numbers

int count_hailstone(int n) {    int steps = 0;    while(n > 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. */

Page 23: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Nested 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

Page 24: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Counting Time With Nested Loops

void count_time_through_day() {    int hour, minute, second;

    for(hour = 0; hour < 24; hour++) {        for(minute = 0; minute < 60; minute++) {            for(second = 0; second < 60; second++) {                /* Whatever you do with the current second... */            }        }    }}

Page 25: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

As 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 < n; c++) { ... }

Page 26: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Off 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 < to <=, 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

Page 27: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Example: 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 < rows; r++) {        for(c = 0; c < cols; c++) {            printf("%c", ch);        }        printf("\n"); /* Newline after each row */    }}

Page 28: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Example: Output a Deck of Cardsvoid output_deck() {    int suit, rank;    for(suit = 0; suit < 4; suit++) {        for(rank = 1; rank <= 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;            }        }    } }

Page 29: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Example: Output Prime Factors

void output_prime_factors(int n) {    int c = 2; /* First potential factor */    while(n >= 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");}

Page 30: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Functions Simulating Nested Loops

void output_one_row(int cols, char ch) {    int c;    for(c = 0; c < cols; c++) {        printf("%c", ch);    }    printf("\n");}

void output_rectangle(int rows, int cols, char ch) {    int r;    for(r = 0; r < rows; r++) {        output_one_row(cols, ch);    }}

Page 31: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Example: 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 < 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'.*/

Page 32: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Radix Conversion Using While

void output_int(int n, int base) {    int p = 1;    while(p * base <= n) { p = p * base; }    /* p is now the highest power of base that is <= n. */    while(p > 0) { /* Descend through the powers of base        printf("%c", get_digit(n / p) );        n = n % p;        p = p / base;    }}

Page 33: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Radix 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 >= base) { output_int(n / base); }    /* Output the last digit. */    printf("%c", get_digit(n % base));}

Page 34: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Terminating 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

Page 35: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Continue

• 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

Page 36: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Files 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

Page 37: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Example: 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. */

Page 38: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

Raw 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

Page 39: Lecture 5: Repetition While not learned. The Need for Repetition At the compile time, we cannot generally know how long the program is going to run If

 

"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