FizzBuzz in the tidyverse

FizzBuzz in the tidyverse

FizzBuzz is and old kids games

Not that popular where I am from Brazil, Fizz Buzz has a simple set of rules

You start counting from 1 (obviously) and when a number is a multiple of 3 you say Fizz,
if the number is a multiple of 5 you say Buzz,
and if the number is a multiple of both you shout FizzBuzz, And for every other case you can say the number itself, simple right?

I watched this really cool video on Tom Scott channel and realized that I have never attempted this problem as a programmer

This is an blog post full of tricks I will try to point them all out.

Scott’s video

Naive FizzBuzz

Naive FizzBuzz

for (i in 1:15){
  
  if(i%%3 == 0 & i%%5 == 0) {
    print('FizzBuzz')
  }
  else if(i%%3 == 0) {
    print('Fizz')
  }
  else if (i%%5 == 0){
    print('Buzz')
  }
  else {
    print(i)
  }
  
}
## [1] 1
## [1] 2
## [1] "Fizz"
## [1] 4
## [1] "Buzz"
## [1] "Fizz"
## [1] 7
## [1] 8
## [1] "Fizz"
## [1] "Buzz"
## [1] 11
## [1] "Fizz"
## [1] 13
## [1] 14
## [1] "FizzBuzz"
  1. Simple flow control with if, else statements
  2. Some basic operators ($, ==)

Extending the loop approach

Using Scott’s approach we can improve a bit on the logic

for (i in 1:15){
  current_out <- ''
  if(i%%3 == 0) {
    current_out <- paste0(current_out,'Fizz')
  }
  if (i%%5 == 0){
    current_out <- paste0(current_out,'Buzz')
  }
  if (current_out == ''){
    print(i)
  }
  else print(current_out)
}
## [1] 1
## [1] 2
## [1] "Fizz"
## [1] 4
## [1] "Buzz"
## [1] "Fizz"
## [1] 7
## [1] 8
## [1] "Fizz"
## [1] "Buzz"
## [1] 11
## [1] "Fizz"
## [1] 13
## [1] 14
## [1] "FizzBuzz"

While it is possible to improve open this loop, I think it already is close to the limits of what I would call a very simple example

Functional approach

Thanks Functional FizzBuzz

divisor <-
  function(number, string) {
    function(d) {
      if (d %% number == 0) string else ""
    }
  }

mod3er <- divisor(3, "Fizz")
mod5er <- divisor(5, "Buzz")

fizzbuzz <- 
  function(i) {
    res <- paste0(mod3er(i), mod5er(i))
    ifelse(res == "", i, res)
  }

sapply(1:15, fizzbuzz)
##  [1] "1"        "2"        "Fizz"     "4"        "Buzz"     "Fizz"    
##  [7] "7"        "8"        "Fizz"     "Buzz"     "11"       "Fizz"    
## [13] "13"       "14"       "FizzBuzz"

So enumerating the new concepts here:

  1. Functions that create functions (mod3er,mod5er)
  2. Functions that create functions that create functions (divisor)
  3. Applying functions (sapply)
  4. Functional if else (I prefer it)

All of which seen pretty complicated at first but will pay off big time latter.

My approach (tidyverse)

The Basics

Loading the tidyverse

library(tidyverse)
## -- Attaching packages ---------------------------------------------------------------------------------------------------------------------------------------------- tidyverse 1.3.0 --
## <U+2713> ggplot2 3.2.1     <U+2713> purrr   0.3.3
## <U+2713> tibble  2.1.3     <U+2713> dplyr   0.8.3
## <U+2713> tidyr   1.0.0     <U+2713> stringr 1.4.0
## <U+2713> readr   1.3.1     <U+2713> forcats 0.4.0
## -- Conflicts ------------------------------------------------------------------------------------------------------------------------------------------------- tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
divisor <- function(number, string) {
    function(input) {
      if_else(condition = input %% number == 0,
              true = string,
              false = "")
    }
}

mod3 <- divisor(3, "Fizz")
mod5 <- divisor(5, "Buzz")

list_functions <- list(mod3,mod5)

mapper_list <- function(i,list_functions) map(list_functions, exec,i)

map(1:15,mapper_list,list_functions) %>% 
  map(reduce,str_c)
## [[1]]
## [1] ""
## 
## [[2]]
## [1] ""
## 
## [[3]]
## [1] "Fizz"
## 
## [[4]]
## [1] ""
## 
## [[5]]
## [1] "Buzz"
## 
## [[6]]
## [1] "Fizz"
## 
## [[7]]
## [1] ""
## 
## [[8]]
## [1] ""
## 
## [[9]]
## [1] "Fizz"
## 
## [[10]]
## [1] "Buzz"
## 
## [[11]]
## [1] ""
## 
## [[12]]
## [1] "Fizz"
## 
## [[13]]
## [1] ""
## 
## [[14]]
## [1] ""
## 
## [[15]]
## [1] "FizzBuzz"

We learned two new tricks:

  1. Executing a list of functions using exec
  2. reducing an list

Making just one call

fancy <- function(i,...) {
  list_functions <- list(...)
  mapper_list <- function(i,list_functions) map(list_functions, exec,i)
  map(i,mapper_list,list_functions) %>%
  map(reduce,str_c)
}

fancy(1:15,mod3,mod5)
## [[1]]
## [1] ""
## 
## [[2]]
## [1] ""
## 
## [[3]]
## [1] "Fizz"
## 
## [[4]]
## [1] ""
## 
## [[5]]
## [1] "Buzz"
## 
## [[6]]
## [1] "Fizz"
## 
## [[7]]
## [1] ""
## 
## [[8]]
## [1] ""
## 
## [[9]]
## [1] "Fizz"
## 
## [[10]]
## [1] "Buzz"
## 
## [[11]]
## [1] ""
## 
## [[12]]
## [1] "Fizz"
## 
## [[13]]
## [1] ""
## 
## [[14]]
## [1] ""
## 
## [[15]]
## [1] "FizzBuzz"

One new trick using ellipsis

Or preparing for an api

api_less_fancy <- function(i,list_functions) {
  mapper_list <- function(i,list_functions) map(list_functions, exec,i)
  map(i,mapper_list,list_functions) %>%
  map(reduce,str_c)
}

api_less_fancy(1:15,list(mod3,mod5))
## [[1]]
## [1] ""
## 
## [[2]]
## [1] ""
## 
## [[3]]
## [1] "Fizz"
## 
## [[4]]
## [1] ""
## 
## [[5]]
## [1] "Buzz"
## 
## [[6]]
## [1] "Fizz"
## 
## [[7]]
## [1] ""
## 
## [[8]]
## [1] ""
## 
## [[9]]
## [1] "Fizz"
## 
## [[10]]
## [1] "Buzz"
## 
## [[11]]
## [1] ""
## 
## [[12]]
## [1] "Fizz"
## 
## [[13]]
## [1] ""
## 
## [[14]]
## [1] ""
## 
## [[15]]
## [1] "FizzBuzz"

Extending FizzBuzz

Let’s see how easy it is too make the game more difficult:

Changing names

mod3n <- divisor(3, "Buzz")
mod5n <- divisor(5,'Fizz')
fancy(1:15,mod3n,mod5n)
## [[1]]
## [1] ""
## 
## [[2]]
## [1] ""
## 
## [[3]]
## [1] "Buzz"
## 
## [[4]]
## [1] ""
## 
## [[5]]
## [1] "Fizz"
## 
## [[6]]
## [1] "Buzz"
## 
## [[7]]
## [1] ""
## 
## [[8]]
## [1] ""
## 
## [[9]]
## [1] "Buzz"
## 
## [[10]]
## [1] "Fizz"
## 
## [[11]]
## [1] ""
## 
## [[12]]
## [1] "Buzz"
## 
## [[13]]
## [1] ""
## 
## [[14]]
## [1] ""
## 
## [[15]]
## [1] "BuzzFizz"

Adding divisors

mod2 <- divisor(2, "Deuce")
fancy(1:30,mod2,mod3,mod5)
## [[1]]
## [1] ""
## 
## [[2]]
## [1] "Deuce"
## 
## [[3]]
## [1] "Fizz"
## 
## [[4]]
## [1] "Deuce"
## 
## [[5]]
## [1] "Buzz"
## 
## [[6]]
## [1] "DeuceFizz"
## 
## [[7]]
## [1] ""
## 
## [[8]]
## [1] "Deuce"
## 
## [[9]]
## [1] "Fizz"
## 
## [[10]]
## [1] "DeuceBuzz"
## 
## [[11]]
## [1] ""
## 
## [[12]]
## [1] "DeuceFizz"
## 
## [[13]]
## [1] ""
## 
## [[14]]
## [1] "Deuce"
## 
## [[15]]
## [1] "FizzBuzz"
## 
## [[16]]
## [1] "Deuce"
## 
## [[17]]
## [1] ""
## 
## [[18]]
## [1] "DeuceFizz"
## 
## [[19]]
## [1] ""
## 
## [[20]]
## [1] "DeuceBuzz"
## 
## [[21]]
## [1] "Fizz"
## 
## [[22]]
## [1] "Deuce"
## 
## [[23]]
## [1] ""
## 
## [[24]]
## [1] "DeuceFizz"
## 
## [[25]]
## [1] "Buzz"
## 
## [[26]]
## [1] "Deuce"
## 
## [[27]]
## [1] "Fizz"
## 
## [[28]]
## [1] "Deuce"
## 
## [[29]]
## [1] ""
## 
## [[30]]
## [1] "DeuceFizzBuzz"

Adding new rules

less <- function(number, string) {
    function(input) {
      if_else(condition = input < number,
              true = string,
              false = "")
    }
}
less10 <- less(10,"Small")
fancy(1:15,less10,mod3,mod5)
## [[1]]
## [1] "Small"
## 
## [[2]]
## [1] "Small"
## 
## [[3]]
## [1] "SmallFizz"
## 
## [[4]]
## [1] "Small"
## 
## [[5]]
## [1] "SmallBuzz"
## 
## [[6]]
## [1] "SmallFizz"
## 
## [[7]]
## [1] "Small"
## 
## [[8]]
## [1] "Small"
## 
## [[9]]
## [1] "SmallFizz"
## 
## [[10]]
## [1] "Buzz"
## 
## [[11]]
## [1] ""
## 
## [[12]]
## [1] "Fizz"
## 
## [[13]]
## [1] ""
## 
## [[14]]
## [1] ""
## 
## [[15]]
## [1] "FizzBuzz"

That is it have a great day.

Avatar
Bruno Carlin
Data Scientist - Specialist

Data Scientist

Related

Next
Previous
comments powered by Disqus