Problem
Challenge
Given a test case print out a FizzBuzz Series.
Specifications
- The first argument is a path to a file.
- Each line includes a test case.
- 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.
- 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
- The input file is formatted correctly.
- The numbers are valid positive integers.
- X is in range [1, 20]
- Y is in range [1, 20]
- N is in range [21, 100]
- 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
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
, andcount
. -
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
withfizz
andbuzz
, 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()
andfputc()
in the code above instead ofprintf()
. This is because those functions are O(1) operations instead of 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 X, Y, and N 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
oruint_fast8_t
.-
uint_least8_t
: give me the smallest type ofunsigned int
which has at least 8 bits. Optimize for memory consumption. -
uint_fast8_t
: give me anunsigned int
of at least 8 bits. Pick a larger type if it will make my program faster, because of alignment considerations. Optimize for speed.
-