Printing fizzy lines

Posted on

Problem

Challenge

Given a test case print out a FizzBuzz Series.

Specifications

  1. The first argument is a path to a file.
  2. Each line includes a test case.
  3. Each test case is comprised of three spaced delimited integers.
    • The first two integers are the dividers X and Y.
    • The third integer, N is how far to count.
  4. Print out the series 1 through N, replacing numbers divisible by X with F, numbers divisible by Y with B and numbers divisible by both with FB.

Constraints

  1. The input file is formatted correctly.
  2. The numbers are valid positive integers.
  3. X is in range [1, 20]
  4. Y is in range [1, 20]
  5. N is in range [21, 100]
  6. Output should be one line per set with no trailing or empty spaces.

Input Sample

3 5 10
2 7 15

Output Sample

1 2 F 4 B F 7 8 F B
1 F 3 F 5 F B F 9 F 11 F 13 FB 15

Source

My Solution:

#include <stdio.h>

void print_buzzified(int fizz, int buzz, int count) {
    for (int i = 1; i <= count; i++) {
        if (i % fizz == 0 && i % buzz == 0) {
            printf("%s", "FB");
        } else if (i % fizz == 0) {
            printf("%s", "F");
        } else if (i % buzz == 0) {
            printf("%s", "B");
        } else {
            printf("%d", i);
        }

        printf(i < count ? " " : "n");
    }
}

int main(int argc, const char * argv[]) {
    FILE *file;
    if (argc < 2 || !(file = fopen(argv[1], "r"))) {
        puts("No argument provided / File not found.");
        return 1;
    }

    file = fopen(argv[1], "r");
    int fizz;
    int buzz;
    int count;

    while (!feof(file)) {
        fscanf(file, "%d %d %d", &fizz, &buzz, &count);
        print_buzzified(fizz, buzz, count);
    }

    fclose(file);
}

Solution

Design:

  • Good job on setting up everything in main(), then passing off control to another function.

  • Declare and initialize file after checking the command line arguments.

  • Initialize fizz, buzz, and count.

  • Don’t use !feof(file) to control your loop. See this answer for more details.

  • Check the return value of fscanf() to make sure we’re reading good data.

  • Good job remembering to close the file.

Readability & Maintainability/Performance

I’m grouping these two categories together for this review, since they happen to go hand in hand.

  • Your for loop can be refactored down a bit:

    if (i % fizz == 0) fputc('F', stdout);
    if (i % buzz == 0) fputc('B', stdout);
    if (i % fizz && i % buzz)) fprintf(stdout, "%d", i);
    

    Some people may find issue with the duplicated check on i with fizz and buzz, but we cut out some conditional branches doing this and the more branches we cut, the less susceptible to branch prediction we are. There may be a better way to write this, but I can’t think of it right now.

  • Note how I used fputs() and fputc() in the code above instead of printf(). This is because those functions are O(1)O(1) operations instead of O(n)O(n) to loop over the string and format it. This may speed up your program by a hair, if that, but the main reason I recommend this change is to make clear we aren’t modifying any strings.

  • Since the range of XX, YY, and NN is 1-20, it would be good to change the type representing these variables to something a bit more compact in memory.

    Depending on what we want to optimize for (memory or performance), I would recommend either uint_least8_t or uint_fast8_t.

    1. uint_least8_t: give me the smallest type of unsigned int which has at least 8 bits. Optimize for memory consumption.

    2. uint_fast8_t: give me an unsigned int of at least 8 bits. Pick a larger type if it will make my program faster, because of alignment considerations. Optimize for speed.

Leave a Reply

Your email address will not be published. Required fields are marked *