Chapter 6: FizzBuzz
Of course, the first thing that your job interview for that cushy new Crystal job will task you with is building FizzBuzz. Let’s do it!
If you’re not familiar, FizzBuzz is a simple programming problem:
“Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”.”
This will give us a good excuse to go over some basics of Crystal: Looping, tests, printing to standard output, and a host of other simple things.
First, let’s create our project.
1
$ crystal init app fizzbuzz
Copied!
Let’s write our first failing test. Open up /spec/fizzbuzz_spec.cr
1
require "./spec_helper"
2
3
describe Fizzbuzz do
4
it "shouldn't divide 1 by 3" do
5
div_by_three(1).should eq(false)
6
end
7
end
Copied!
And run it:
1
$ crystal spec
2
Error in ./spec/fizzbuzz_spec.cr:7: undefined method 'div_by_three'
3
4
div_by_three(1).should eq(false)
Copied!
This makes sense: We haven’t defined any method yet. Let’s define one:
1
require "./fizzbuzz/*"
2
3
def div_by_three(n)
4
false
5
end
Copied!
Akin to Ruby, the value of the last expression gets returned.
TDD means do the simplest thing! Now that we’ve defined our method, let’s compile and run our tests:
1
$ crystal spec
2
.
3
4
Finished in 0.82 milliseconds
5
1 examples, 0 failures, 0 errors, 0 pending
Copied!
Awesome! We pass! Let’s write another test, and see what happens:
1
require "./spec_helper"
2
3
describe Fizzbuzz do
4
it "shouldn't divide 1 by 3" do
5
div_by_three(1).should eq(false)
6
end
7
8
it "should divide 3 by 3" do
9
div_by_three(3).should eq(true)
10
end
11
end
Copied!
Run it!
1
$ crystal spec
2
3
.F
4
5
Failures:
6
7
1) Fizzbuzz should divide 3 by 3
8
Failure/Error: div_by_three(3).should eq(true)
9
10
expected: true
11
got: false
12
13
# ./spec/fizzbuzz_spec.cr:9
14
15
Finished in 0.83 milliseconds
16
2 examples, 1 failures, 0 errors, 0 pending
17
18
Failed examples:
19
20
crystal spec ./spec/fizzbuzz_spec.cr:8 # Fizzbuzz should divide 3 by 3
Copied!
We have 1 failure. Let’s make this pass.
1
require "./fizzbuzz/*"
2
3
def div_by_three(n)
4
if n % 3 == 0
5
true
6
else
7
false
8
end
9
end
Copied!
Run it.
1
$ crystal spec
2
3
..
4
5
Finished in 0.61 milliseconds
6
2 examples, 0 failures, 0 errors, 0 pending
Copied!
Awesome! This shows off how else work, as well. It’s probably what you expected. Go ahead and try to refactor this into a one-liner.
Done? How’d you do? Here’s mine:
1
def div_by_three(n)
2
n % 3 == 0
3
end
Copied!
Remember, the value of the last expression gets returned.
Okay, now try to TDD out the div_by_five and div_by_fifteen methods. They should work the same way, but this will let you get practice actually writing it out. Once you see this, you’re ready to advance:
1
$ crystal spec -v
2
3
Fizzbuzz
4
shouldn't divide 1 by 3
5
should divide 3 by 3
6
shouldn't divide 8 by 5
7
should divide 5 by 5
8
shouldn't divide 13 by 15
9
should divide 15 by 15
10
11
Finished in 0.61 milliseconds
12
6 examples, 0 failures, 0 errors, 0 pending
Copied!
Okay! Let’s talk about the main program now. We’ve got the tools to build FizzBuzz, let’s make it work. First thing we need to do is print out all the numbers from one to 100. It’s easy!
1
100.times do |num|
2
puts num
3
end
Copied!
Step one: print something 100 times. If you run this via crystal build src/fizzbuzz.cr && ./fizzbuzz you should see num printed 100 times. Note that our tests didn’t actually run. Not only are they not run, they’re actually not even in the executable:
Now we can put the two together:
1
100.times do |num|
2
answer = ""
3
4
if div_by_fifteen num
5
answer = "FizzBuzz"
6
elsif div_by_three num
7
answer = "Fizz"
8
elsif div_by_five num
9
answer = "Buzz"
10
else
11
answer = num
12
end
13
14
puts answer
15
end
Copied!
Because the if returns a value, we could also do something like this:
1
(1..100).each do |num|
2
answer = if div_by_fifteen num
3
"FizzBuzz"
4
elsif div_by_three num
5
"Fizz"
6
elsif div_by_five num
7
"Buzz"
8
else
9
num
10
end
11
12
puts answer
13
end
Copied!
Notice that we also changed 100.times to (1..100).each, to make num go from 1 to 100 instead of from 0 to 99.
Try running it.
Awesome! We’ve conquered FizzBuzz.
Last modified 2yr ago
Copy link