Programmering C: Lösningar till tentamen 2012-05-30

Observera att detta är förslag på lösningar. Det kan finnas andra lösningar som också är korrekta, och det kan hända att en del av lösningarna är mer omfattande än vad som krävs för full poäng på uppgiften. En del av lösningarna är kanske inte fullständiga, utan hänvisar bara till var man kan läsa svaret.

Uppgift 1 (1 p)

a) 2

b) 3

c) 3

d) -2

Uppgift 2 (1 p)

ali = 4, bodil = 4, charlie = 0

Uppgift 3 (3 p)

#include <stdio.h>

int main(void) {
    int fel = 0;

    for (int i = 0; i < 357; ++i) {
        printf("Ange tal nr %d:\n", i + 1);
        int talet;
        scanf("%d", &talet);
        if (talet >= 45)
            fel = 1;
    }
    if (fel)
        printf("För stort\n");
    else
        printf("Ok\n");
    
    return 0;
}

Uppgift 4 (2 p)

int inom_intervallet(double x, double min, double max) {
    return x >= min && x <= max;
}

Uppgift 5 (1 p)

int main(void) {
    double talet, min, max;

    printf("Ange talet: ");
    scanf("%lf", &talet);
    printf("Ange intervallets nedre gräns: ");
    scanf("%lf", &min);
    printf("Ange intervallets övre gräns: ");
    scanf("%lf", &max);

    int resultat = inom_intervallet(talet, min, max);
    printf("Resultat: %d\n", resultat);
    
    return 0;
}

Uppgift 6 (1 p)

struct Interval {
    double min;
    double max;
};

Uppgift 7 (2 p)


int inside_interval(double point, struct Interval *intervalp) {
    return inom_intervallet(point, intervalp->min, intervalp->max);
}

Uppgift 8 (3 p)

Ett förslag, som jag tycker är tydligast och mest läsbart:

int overlapping_intervals(struct Interval *ip1, struct Interval *ip2) {
    if (ip1->max < ip2->min)
        return 0; // ip1 ligger helt till vänster om ip2
    else if (ip1->min > ip2->max)
        return 0; // ip1 ligger helt till höger om ip2
    else
        return 1; // annars är de överlappande!
}

Ett annat förslag:

// Enda sättet som intervallen kan vara något annat än överlappande
// är om det ena intervallet (ip1) ligger antingen helt till vänster
// om det andra (ip2), eller helt till höger om det.
int overlapping_intervals(struct Interval *ip1, struct Interval *ip2) {
    return !(ip1->max < ip2->min || ip1->min > ip2->max);
}

Kommentar: Det kan vara ganska komplicerat att få till villkoret rätt, så att alla olika sätt som de två intervallen kan förhålla sig till varandra på täcks in. Det finns många sätt att skriva funktionen. Ovanstående två lösningar är två varianter av samma sätt att tänka, som utgår från när intervallen inte är överlappande.

En kommentar till: Lösningarna ovan förutsätter att inget av intervallen är tomt, dvs vi antar att den angivna övre gränsen för ett intervall aldrig är mindre dess undre gräns.

Uppgift 9 (3 p)

int inside_how_many_intervals(double point, struct Interval interval_array[], int nr_intervals) {
    int inside_count = 0;
    for (int i = 0; i < nr_intervals; ++i) {
        if (inside_interval(point, &interval_array[i])) {
            ++inside_count;
        }
    }
    return inside_count;
} // inside_how_many_intervals

Uppgift 10 (3 p)

int main(void) {
    struct Interval some_intervals[] = {
        { 1, 2 },
        { -2, -1 },
        { -1, 1 },
        { 0, 2 },
        { 0, 1 }
    };
    int nr_intervals = sizeof(some_intervals) / sizeof(some_intervals[0]);
    if (inside_how_many_intervals(0.5, some_intervals, nr_intervals) != 3)
        printf("Funktionen inside_how_many_intervals gav fel svar!\n");
    return 0;
}

Uppgift 11 (5 p)

#include <stdlib.h>
#include <stdio.h>
#include <float.h>

// Här: Definition av posttypen struct Interval

int main(void) {
    FILE *infile;
    struct Interval this_interval;
    struct Interval widest_interval = { 0, 0 };
    struct Interval narrowest_interval = { 0, DBL_MAX };

    infile = fopen("intervall.txt", "r");
    if (infile == NULL) {
        fprintf(stderr, "Kunde inte öppna filen 'intervall.txt'.\n");
        exit(EXIT_FAILURE);
    }
    while (fscanf(infile, "%lf %lf", &this_interval.min, &this_interval.max) != EOF) {
        double width = this_interval.max - this_interval.min;
        printf("%f\n", width);
        if (width > widest_interval.max - widest_interval.min)
            widest_interval = this_interval;
        if (width < narrowest_interval.max - narrowest_interval.min)
            narrowest_interval = this_interval;
    }
    printf("Smalast: %f - %f\n", narrowest_interval.min, narrowest_interval.max);
    printf("Bredast: %f - %f\n", widest_interval.min, widest_interval.max);

    return EXIT_SUCCESS;
}
En kommentar: Observera att while (!feof(infile)) ... är fel sätt att kontrollera filslut i en läs-loop i C.

Uppgift 12 (6 p)

Den enklaste lösningen är att läsa in alla intervallen till en array, och sen ha två nästlade loopar som jämför intervallen i arrayen med varandra. Här visas som omväxling en lösning med två nästlade läs-loopar, som läser in intervall från filen och jämför dem direkt, utan att lagra dem i någon array.

#include <stdlib.h>
#include <stdio.h>
#include <float.h>

// Här: Definition av posttypen struct Interval, med mera

int main(void) {
    // "inner" och "outer" refererar till looparna, inte till själva intervallen
    FILE *outer_infile, *inner_infile; 
    struct Interval outer_interval, inner_interval;
    int outer_number, inner_number;
    int overlapping_found;

    outer_infile = fopen("intervall.txt", "r");
    if (outer_infile == NULL) {
        fprintf(stderr, "Kunde inte öppna filen 'intervall.txt'.\n");
        exit(EXIT_FAILURE);
    }
    inner_infile = fopen("intervall.txt", "r");
    if (inner_infile == NULL) {
        fprintf(stderr, "Kunde inte öppna filen 'intervall.txt' en gång till.\n");
        exit(EXIT_FAILURE);
    }
    overlapping_found = 0;
    outer_number = 0;
    while (fscanf(outer_infile, "%lf %lf", &outer_interval.min, &outer_interval.max) != EOF && !overlapping_found) {
        ++outer_number;
        rewind(inner_infile); // Onödigt i första varvet
        inner_number = 0;
        while (fscanf(inner_infile, "%lf %lf", &inner_interval.min, &inner_interval.max) != EOF && !overlapping_found) {
            ++inner_number;
            // Vi jämför först intervallnumren, så vi inte jämför ett intervall med sig självt
            if (inner_number != outer_number && overlapping_intervals(&inner_interval, &outer_interval))
                overlapping_found = 1;
        }
    }
    fclose(inner_infile);
    fclose(outer_infile);

    if (overlapping_found)
        printf("Överlappande finns.\n");
    else
        printf("Överlappande finns INTE.\n");

    return EXIT_SUCCESS;
}

Uppgift 13 (5 p)

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

int all_islower(char *string) {
    while (*string != '\0') {
        if (!islower(*string))
            return 0;
        ++string;
    }
    return 1;
}

int part_name_ok(char *name) {
    return isupper(name[0]) && all_islower(&name[1]);
}

int middle_initial_ok(char *middle_initial) {
    return isupper(middle_initial[0]) && middle_initial[1] == '.' && middle_initial[3] == '\0';
}

int main(void) {
    char full_name[1000 + 2]; // +2, eftersom både \n och \0 ska få plats
    char first_name[1000 + 1], middle_initial[1000 + 1], last_name[1000 + 1], illegal_extra_name[1000 + 1];
    printf("Ange amerikanska namn. Avsluta med en tom rad:\n");
    while (fgets(full_name, sizeof full_name, stdin) != NULL && full_name[0] != '\n') {
        if (sscanf(full_name, "%s %s %s %s", first_name, middle_initial, last_name, illegal_extra_name) != 3) {
            printf("Otillåtet namn: Inte tre delar.\n");
        }
        else if (part_name_ok(first_name) == 0) {
            printf("Otillåtet förnamn.\n");
        }
        else if (middle_initial_ok(middle_initial) == 0) {
            printf("Otillåten initial.\n");
        }
        else if (part_name_ok(last_name) == 0) {
            printf("Otillåtet efternamn.\n");
        }
        else {
            printf("Namnet var okej.\n");
        }
    } // while
    return EXIT_SUCCESS;
}


Thomas Padron-McCarthy (thomas.padron-mccarthy@oru.se), 7 juni 2012