Walletfox.com

Fully functional Fizz Buzz with Range-v3


This article presents Fizz Buzz, one of the 27 examples from the book Fully Functional C++ with Range-v3. The example demonstrates a very different approach to problem solving and explores the beauty of functional programming.

Fizz Buzz

Let us repeat the Fizz Buzz problem. We would like to generate a sequence with:

  • "Fizz" if the number is divisible by 3
  • "Buzz" if the number is divisible by 5
  • "FizzBuzz" if it is divisible both by 3 and 5
  • Otherwise print the number itself

1. To accomplish this task, we first need primitive sequences that will represent the properties of Fizz, i.e. divisibility by 3. Analogously for Buzz, divisibility by 5. We can use fixed-size arrays for this.

std::array<std::string,3> fizz {"","","Fizz"};
std::array<std::string,5> buzz {"","","","","Buzz"};

2. Based on the primitive sequences, we create cyclic sequences using views::cycle. You might be already able to spot what we are up to. We are preparing the sequences for 'a merger'.

auto r_fizzes = fizz | views::cycle;
// [ , ,Fizz, , ,Fizz...]
auto r_buzzes = buzz | views::cycle;
// [ , , , ,Buzz, , , , ,Buzz...] 

3. We use views::zip_with in combination with the std::plus{} constrained function object to accomplish this. Notice, how smoothly std::plus{} combines "Fizz" and "Buzz" into "FizzBuzz" at the position 15.

auto r_fizzbuzz = views::zip_with(std::plus{}, r_fizzes, r_buzzes);
/* [ , ,Fizz, ,Buzz,Fizz, , ,Fizz,Buzz, ,Fizz, , ,FizzBuzz, , ,Fizz...] */

4. We have already fulfilled the first three requirements. Nevertheless, we still have to incorporate the numbers into the sequence. For that, we use a hack from Haskell. We generate a sequence of integers 1,2,3.... and convert each of them to a string. Why? So that we can compare them to the fizzbuzz strings. This might sound inefficient, but all we care about here is the abstract level. Patterns are likely to be optimised.

auto r_int_str = views::iota(1) |
                 views::transform([](int x){
                                  return std::to_string(x);}); // [1,2,3...]

5. Last, we perform a merger of the fizzbuzz sequence with number-converted-string sequence. This time we use a lambda using std::max(a,b) as an argument function to views::zip_with. Why does this work? Have a look at the ASCII table. Numbers have higher positions than spaces, hence, when a number is compared to a space, std::max picks the number. Similarly, letters have higher positions than numbers, hence std::max picks the letter-containing string.

auto rng = views::zip_with(
                [](auto a, auto b){return std::max(a,b);}, r_fizzbuzz, r_int_str);
/* [1,2,Fizz,4,Buzz,Fizz,7,8,Fizz,Buzz,11,Fizz,13,14,FizzBuzz,16,17...] */

Intrigued?

Get started

Tagged: Range-v3