In C track Isogram and Pangram are broken

I solved both exercises, but Exercism’s tests think otherwise. So I copied the code into CLion to debug it and surprisingly was unable to reproduce test results. I tried Visual Studio and Repl.it, but the problem persists: running the code on Exercism results in errors, running elsewhere results in no errors. I therefore conclude, that it’s a bug and not my mistake.

Here are the .c files (.h files are untouched) with my solution and tests that work wrong at the end.

PANGRAM:

// PANGRAM EXERCISE
#include "pangram.h"
#include <stddef.h>

struct letter {
    char lowercase;
    char uppercase;
    int occurrences;
};

struct letter alphabet[] = {
        {'a', 'A', 0},
        {'b', 'B', 0},
        {'c', 'C', 0},
        {'d', 'D', 0},
        {'e', 'E', 0},
        {'f', 'F', 0},
        {'g', 'G', 0},
        {'h', 'H', 0},
        {'i', 'I', 0},
        {'j', 'J', 0},
        {'k', 'K', 0},
        {'l', 'L', 0},
        {'m', 'M', 0},
        {'n', 'N', 0},
        {'o', 'O', 0},
        {'p', 'P', 0},
        {'q', 'Q', 0},
        {'r', 'R', 0},
        {'s', 'S', 0},
        {'t', 'T', 0},
        {'u', 'U', 0},
        {'v', 'V', 0},
        {'w', 'W', 0},
        {'x', 'X', 0},
        {'y', 'Y', 0},
        {'z', 'Z', 0},
};

bool is_pangram(const char *sentence){
    if (!sentence) return false;
    int i;
    char character;
    for (i = 0; sentence[i] != '\0'; i++){
        character = sentence[i];
        if (alphabet[i].lowercase == character || alphabet[i].uppercase == character){
            alphabet[i].occurrences += 1;
        } else {
        continue;
        }
    }
    for (i = 0; i < 26; i++){
        if (alphabet[i].occurrences == 0){
            return false;
        }
    }
    return true;
}

// UNREPRODUCABLE TESTS THAT RESULT IN "Expected FALSE was TRUE"
// a quick movement of the enemy will jeopardize five gunboats
// five boxing wizards jump quickly at it
// 7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog
// abcdefghijklm ABCDEFGHIJKLM

ISOGRAM:

// ISOGRAM EXERCISE
#include "isogram.h"
#include <stddef.h>

struct letter {
    char lowercase;
    char uppercase;
    int occurrences;
};

struct letter alphabet[] = {
        {'a', 'A', 0},
        {'b', 'B', 0},
        {'c', 'C', 0},
        {'d', 'D', 0},
        {'e', 'E', 0},
        {'f', 'F', 0},
        {'g', 'G', 0},
        {'h', 'H', 0},
        {'i', 'I', 0},
        {'j', 'J', 0},
        {'k', 'K', 0},
        {'l', 'L', 0},
        {'m', 'M', 0},
        {'n', 'N', 0},
        {'o', 'O', 0},
        {'p', 'P', 0},
        {'q', 'Q', 0},
        {'r', 'R', 0},
        {'s', 'S', 0},
        {'t', 'T', 0},
        {'u', 'U', 0},
        {'v', 'V', 0},
        {'w', 'W', 0},
        {'x', 'X', 0},
        {'y', 'Y', 0},
        {'z', 'Z', 0},
};

bool is_isogram(const char phrase[]){
    if (!phrase) return false;
    int i, j;
    char character;
    for (i = 0; phrase[i] != '\0'; i++){
        character = phrase[i];
        for (j = 0; j < 26; j++){
            if (alphabet[j].lowercase == character || alphabet[j].uppercase == character) {
                alphabet[j].occurrences += 1;
                if (alphabet[j].occurrences > 1) {
                    return false;
                }
            } else {
            continue;
            }
        }
    }
    return true;
}

// UNREPRODUCABLE TESTS THAT RESULT IN "Expected TRUE was FALSE"
// subdermatoglyphic
// thumbscrew-japingly
// six-year-old
// Emily Jung Schwartzkopf

Hi shrvtv, welcome aboard!

Both solutions define the global variable alphabet and initialize it once. But each execution of is_pangram() or is_isogram() modifies the occurrences which affects subsequent tests.

Also, is_pangram() accesses alphabet[i]. But i is the index of the current character in the sentence, not an index into alphabet. I think there’s a nested loop missing, take a look at is_isogram() for comparison.


Did you run all the tests in CLion? You can install the exercism CLI to download the whole exercise, including the tests, to compile and run the tests with make. That should give you the same test results as in the online editor (at least for these kinds of issues.)


And finally: Please consider getting mentored. A mentor could point out a few things like defining the alphabet as static as a local variable inside the function , omitting the else branch with the continue, or they could suggest shorter alternatives like calling tolower() or calculating the index of the occurrence counter directly instead of using a loop.
And if you use the exercism CLI you can even submit incomplete or failing solutions and ask for help or clarification.

2 Likes

Thanks for replying so fast! I installed CLI and configured remote development in CLion. Now the errors are reproducable! Will look into that tomorrow.

Both solutions define the global variable alphabet and initialize it once . But each execution of is_pangram() or is_isogram() modifies the occurrences which affects subsequent tests.

Are you saying that I shouldn’t use the same variable name in 2 different exercises, because they are the same thing? I don’t get it, how is it possible, that once a program ends and all data is erased, some variables remain?

The exercises are independent and unrelated. But each exercise has multiple tests. All the tests within one exercise can call the function multiple times with different values. The global state is reused across multiple tests within the exercise.

As @siebenschlaefer said, the best approach is to request mentoring, though. That way someone familiar with the exercise and language can review your actual code and the actual error and provide targeted, high value feedback.