天天看點

Chapter 3– Control Flow of TCPL (Part 9)

Chapter 3 – Control Flow

Exercise 3-1

Our binary search makes two tests inside the loop, when one would suffice (at the price of more tests outside). Write a version with only one test inside the loop and measure the difference in run-time.

#include <stdio.h>

#include <time.h>

#define MAX_ELEMENT 20000

int binsearch(int x, int v[], int n);

int binsearch2(int x, int v[], int n);   

int main(void) {

    int testdata[MAX_ELEMENT];

    int index;           

    int n = -1;          

    int i;

    clock_t time_taken;

    for ( i = 0; i < MAX_ELEMENT; ++i )

        testdata[i] = i;

    for ( i = 0, time_taken = clock(); i < 100000; ++i ) {

        index = binsearch(n, testdata, MAX_ELEMENT);

    }

    time_taken = clock() - time_taken;

    if ( index < 0 )

        printf("Element %d not found./n", n);

    else

        printf("Element %d found at index %d./n", n, index);

    printf("binsearch() took %lu clocks (%lu seconds)/n",

        (unsigned long) time_taken,

        (unsigned long) time_taken / CLOCKS_PER_SEC);

    for ( i = 0, time_taken = clock(); i < 100000; ++i ) {

        index = binsearch2(n, testdata, MAX_ELEMENT);

    }

    time_taken = clock() - time_taken;

    if ( index < 0 )

        printf("Element %d not found./n", n);

    else

        printf("Element %d found at index %d./n", n, index);

    printf("binsearch2() took %lu clocks (%lu seconds)/n",

        (unsigned long) time_taken,

        (unsigned long) time_taken / CLOCKS_PER_SEC);

    return 0;

}

int binsearch(int x, int v[], int n) {

    int low, mid, high;

    low = 0;

    high = n - 1;

    while ( low <= high ) {

        mid = (low+high) / 2;

        if ( x < v[mid] )

            high = mid - 1;

        else if ( x > v[mid] )

            low = mid + 1;

        else

            return mid;

    }

    return -1;

}

int binsearch2(int x, int v[], int n) {

    int low, high, mid;

    low = 0;

    high = n - 1;

    mid = (low+high) / 2;

    while ( low <= high && x != v[mid] ) {

        if ( x < v[mid] )

            high = mid - 1;

        else

            low = mid + 1;

        mid = (low+high) / 2;

    }

    if ( x == v[mid] )

        return mid;

    else

        return -1;

}

Exercise 3-2

Write a function escape(s,t) that converts characters like newline and tab into visible escape sequences like /n and /t as it copies the string t to s . Use a switch. Write a function for the other direction as well, converting escape sequences into the real characters.

#include <stdio.h>

#define MAX_LEN 1024

#define SLASH   1

#define TAB     2

#define NEWLINE 3

#define OTHER   4

void escape(char s[], char t[]);

void deescape(char t[], char s[]);

int main() {

    char str[] = "This is a test/n. This is   another example./n";

    char t[MAX_LEN];

    char str2[MAX_LEN];

    escape(str, t);

    printf("%s/n", str);

    printf("%s/n", t);

    deescape(t, str2);

    printf("%s/n", str2);

    return 0;

}

void escape(char s[], char t[]) {

    int i, j;

    for (i = 0, j = 0; s[i] != '/0'; i++) {

        if (s[i] == '/t' ) {

            t[j++] = '//';

            t[j++] = 't';

        } else if (s[i] == '/n') {

            t[j++] = '//';

            t[j++] = 'n';

        }

        else {

            t[j++] = s[i];

        }

    }

    t[j] = '/0';

}

void deescape(char t[], char s[]) {

    int state = OTHER, i, j;

    for (i = 0, j = 0; t[i] != '/0'; i++) {

        switch(state) {

        case OTHER:

            if (t[i] == '//') {

                state = SLASH;

            } else {

                s[j++] = t[i];

            }

            break;

        case SLASH:

            if (t[i] == 't') {

                state = TAB;

            } else if (t[i] == 'n') {

                state = NEWLINE;

            } else if(t[i] != '//') {

                state = OTHER;

            }

            break;

        case TAB:

            s[j++] = '/t';

            state = OTHER;

            break;

        case NEWLINE:

            s[j++] = '/n';

            state = OTHER;

            break;

        default: break;

        }

    }

    s[j] = '/0';

}

Exercise 3-3

Write a function expand(s1,s2) that expands shorthand notations like a-z in the string s1 into the equivalent complete list abc...xyz in s2 . Allow for letters of either case and digits, and be prepared to handle cases like a-b-c and a-z0-9 and -a-z . Arrange that a leading or trailing - is taken literally.

#include <stdio.h>

#include <string.h>

void expand(char * s1, char * s2);

int main(void) {

    char *s[] = { "a-z-", "z-a-", "-1-6-",

        "a-ee-a", "a-R-L", "1-9-1",

        "5-5", NULL };

    char result[100];

    int i = 0;

    while ( s[i] ) {       

        expand(result, s[i]);

        printf("Unexpanded: %s/n", s[i]);

        printf("Expanded  : %s/n", result);

        ++i;

    }

    return 0;

}

void expand(char * s1, char * s2) {

    static char upper_alph[27] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    static char lower_alph[27] = "abcdefghijklmnopqrstuvwxyz";

    static char digits[11]     = "0123456789";

    char * start, * end, * p;

    int i = 0;

    int j = 0;   

    while ( s2[i] ) {

        switch( s2[i] ) {

        case '-':

            if ( i == 0 || s2[i+1] == '/0' ) {

                s1[j++] = '-';

                ++i;

                break;

            }

            else {

                if ( (start = strchr(upper_alph, s2[i-1])) &&

                    (end = strchr(upper_alph, s2[i+1])) )

                    ;

                else if ( (start = strchr(lower_alph, s2[i-1])) &&

                    (end = strchr(lower_alph, s2[i+1])) )

                    ;

                else if ( (start = strchr(digits, s2[i-1])) &&

                    (end = strchr(digits, s2[i+1])) )

                    ;

                else {

                    fprintf(stderr, "EX3_3: Mismatched operands '%c-%c'/n",

                        s2[i-1], s2[i+1]);

                    s1[j++] = s2[i-1];

                    s1[j++] = s2[i++];

                    break;

                }               

                p = start;

                while ( p != end ) {

                    s1[j++] = *p;

                    if ( end > start )

                        ++p;

                    else

                        --p;

                }

                s1[j++] = *p;

                i += 2;

            }

            break;

        default:

            if ( s2[i+1] == '-' && s2[i+2] != '/0' ) {

                ++i;

            }

            else {               

                s1[j++] = s2[i++];

            }

            break;

        }

    }

    s1[j] = s2[i];   

}

Exercise 3-4

In a two's complement number representation, our version of itoa does not handle the largest negative number, that is, the value of n equal to -(2 to the power (wordsize - 1)) . Explain why not. Modify it to print that value correctly regardless of the machine on which it runs.

#include <stdlib.h>

#include <stdio.h>

#include <string.h>

#include <limits.h>

void itoa2(int n, char s[]);

void reverse(char s[]);

int main(void) {

    char buffer[20];

    printf("INT_MIN: %d/n", INT_MIN);

    itoa2(INT_MIN, buffer);

    printf("Buffer : %s/n", buffer);

    return 0;

}

void itoa2(int n, char s[]) {

    int i, sign;

    sign = n;

    i = 0;

    do {

        s[i++] = abs(n % 10) + '0';

    } while ( n /= 10 );

    if (sign < 0)

        s[i++] = '-';

    s[i] = '/0';

    reverse(s);

}

void reverse(char s[]) {

    int c, i, j;

    for ( i = 0, j = strlen(s)-1; i < j; i++, j--) {

        c = s[i];

        s[i] = s[j];

        s[j] = c;

    }

}

Exercise 3-5

Write the function itob(n,s,b) that converts the integer n into a base b character representation in the string s . In particular, itob(n,s,16) formats n as a hexadecimal integer in s .

#include <stdlib.h>

#include <string.h>

#include <stdio.h>

void itob(int n, char s[], int b);

void reverse(char s[]);

int main(void) {

    char buffer[10];

    int i;

    for ( i = 2; i <= 20; ++i ) {

        itob(255, buffer, i);

        printf("Decimal 255 in base %-2d : %s/n", i, buffer);

    }

    return 0;

}

void itob(int n, char s[], int b) {

    static char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    int i, sign;

    if ( b < 2 || b > 36 ) {

        fprintf(stderr, "EX3_5: Cannot support base %d/n", b);

        exit(EXIT_FAILURE);

    }

    if ((sign = n) < 0)

        n = -n;

    i = 0;

    do {

        s[i++] = digits[abs(n % b)];

    } while ((n /= b));

    if (sign < 0)

        s[i++] = '-';

    s[i] = '/0';

    reverse(s);

}

void reverse(char s[]) {

    int c, i, j;

    for ( i = 0, j = strlen(s)-1; i < j; i++, j--) {

        c = s[i];

        s[i] = s[j];

        s[j] = c;

    }

}

Exercise 3-6

Write a version of itoa that accepts three arguments instead of two. The third argument is a minimum field width; the converted number must be padded with blanks on the left if necessary to make it wide enough.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <limits.h>

#define MAX_LEN 64

void itoa3(int n, char s[], int width);

void reverse(char s[]);

int main(void) {

    char buffer[MAX_LEN];

    itoa3(INT_MIN, buffer,30);

    printf("Buffer:%s/n", buffer);

    return 0;

}

void itoa3(int n, char s[], int width) {

    int i, sign;

    if ((sign = n) < 0)

        n = -n;

    i = 0;

    do {

        s[i++] = abs(n % 10) + '0';

    } while ((n /= 10));

    if (sign < 0)

        s[i++] = '-';

    while (i < width )   

        s[i++] = ' ';

    s[i] = '/0';

    reverse(s);

}

void reverse(char s[]) {

    int c, i, j;

    for ( i = 0, j = strlen(s)-1; i < j; i++, j--) {

        c = s[i];

        s[i] = s[j];

        s[j] = c;

    }

}