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.
$ crystal init app fizzbuzz
Let’s write our first failing test. Open up
require "./spec_helper"describe Fizzbuzz doit "shouldn't divide 1 by 3" dodiv_by_three(1).should eq(false)endend
And run it:
$ crystal specError in ./spec/fizzbuzz_spec.cr:7: undefined method 'div_by_three'div_by_three(1).should eq(false)
This makes sense: We haven’t defined any method yet. Let’s define one:
require "./fizzbuzz/*"def div_by_three(n)falseend
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:
$ crystal spec.Finished in 0.82 milliseconds1 examples, 0 failures, 0 errors, 0 pending
Awesome! We pass! Let’s write another test, and see what happens:
require "./spec_helper"describe Fizzbuzz doit "shouldn't divide 1 by 3" dodiv_by_three(1).should eq(false)endit "should divide 3 by 3" dodiv_by_three(3).should eq(true)endend
$ crystal spec.FFailures:1) Fizzbuzz should divide 3 by 3Failure/Error: div_by_three(3).should eq(true)expected: truegot: false# ./spec/fizzbuzz_spec.cr:9Finished in 0.83 milliseconds2 examples, 1 failures, 0 errors, 0 pendingFailed examples:crystal spec ./spec/fizzbuzz_spec.cr:8 # Fizzbuzz should divide 3 by 3
We have 1 failure. Let’s make this pass.
require "./fizzbuzz/*"def div_by_three(n)if n % 3 == 0trueelsefalseendend
$ crystal spec..Finished in 0.61 milliseconds2 examples, 0 failures, 0 errors, 0 pending
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:
def div_by_three(n)n % 3 == 0end
Remember, the value of the last expression gets returned.
Okay, now try to TDD out the
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:
$ crystal spec -vFizzbuzzshouldn't divide 1 by 3should divide 3 by 3shouldn't divide 8 by 5should divide 5 by 5shouldn't divide 13 by 15should divide 15 by 15Finished in 0.61 milliseconds6 examples, 0 failures, 0 errors, 0 pending
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!
100.times do |num|puts numend
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:
100.times do |num|answer = ""if div_by_fifteen numanswer = "FizzBuzz"elsif div_by_three numanswer = "Fizz"elsif div_by_five numanswer = "Buzz"elseanswer = numendputs answerend
if returns a value, we could also do something like this:
(1..100).each do |num|answer = if div_by_fifteen num"FizzBuzz"elsif div_by_three num"Fizz"elsif div_by_five num"Buzz"elsenumendputs answerend
Notice that we also changed
(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.