FizzBuzz Macro
Go to file
2020-07-24 17:43:22 +12:00
.gitignore fuark me forgot to exclude swp files 2020-07-24 17:43:22 +12:00
fizzbuzz.rkt first commit 2020-07-24 17:40:40 +12:00
README.md first commit 2020-07-24 17:40:40 +12:00

FizzBuzz Macro

a small macro for defining FizzBuzz-like functions easily. uses continuation passing to ensure each predicate is only checked once per number.

usage

import the file into your #lang racket project

(require "fizzbuzz.rkt")

from there the functions mult, fizzbuzz and define-fizzbuzz will be avaliable to you.

mult

(mult x) creates a predicate that answers whether a number is a multiple of x

((mult 5) 15) => #t
((mult 5) 16) => #f

fizzbuzz

(fizzbuzz x) is an implementation of fizzbuzz using this program. it returns a string which will be one of:

  • the string representation of x
  • the string "Fizz"
  • the string "Buzz"
  • the string "FizzBuzz"

it is defined using define-fizzbuzz and mult as follows:

(define-fizzbuzz fizzbuzz
   [(mult 3) "Fizz"]
   [(mult 5) "Buzz"])

define-fizzbuzz

define-fizzbuzz takes a name, and a list of predicate, string pairs, and creates a function that acts as fizzbuzz would for any number of triggers.

the order of the pairs will determine the ordering of the strings should more than one be true for a given number, ie. the difference between "FizzBuzz" and "BuzzFizz" is the ordering of the pairs.

example

in order to define the function bashforkfoobar which has the following semantics:

  • if x is odd, "Bash"
  • if x is a multiple of 7, "Fork"
  • if x - 2 is a multiple of 3, "Foo"
  • if x is 69, "Bar"

we can use the following;

(define-fizzbuzz bashforkfoobar
   [odd? "Bash"]
   [(mult 7) "Fork"]
   [(lambda (n) ((mult 3) (- n 2))) "Foo"]
   [(lambda (n) (= n 69) "Bar"])

which when mapped over a range of numbers using (map bashforfoobar (range 1 101);

'("Bash"
  "Foo"
  "Bash"
  "4"
  "BashFoo"
  "6"
  "BashFork"
  "Foo"
  "Bash"
  "10"
  "BashFoo"
  "12"
  "Bash"
  "ForkFoo"
  "Bash"
  "16"
  "BashFoo"
  "18"
  "Bash"
  "Foo"
  "BashFork"
  "22"
  "BashFoo"
  "24"
  "Bash"
  "Foo"
  "Bash"
  "Fork"
  "BashFoo"
  "30"
  "Bash"
  "Foo"
  "Bash"
  "34"
  "BashForkFoo"
  "36"
  "Bash"
  "Foo"
  "Bash"
  "40"
  "BashFoo"
  "Fork"
  "Bash"
  "Foo"
  "Bash"
  "46"
  "BashFoo"
  "48"
  "BashFork"
  "Foo"
  "Bash"
  "52"
  "BashFoo"
  "54"
  "Bash"
  "ForkFoo"
  "Bash"
  "58"
  "BashFoo"
  "60"
  "Bash"
  "Foo"
  "BashFork"
  "64"
  "BashFoo"
  "66"
  "Bash"
  "Foo"
  "BashBar"
  "Fork"
  "BashFoo"
  "72"
  "Bash"
  "Foo"
  "Bash"
  "76"
  "BashForkFoo"
  "78"
  "Bash"
  "Foo"
  "Bash"
  "82"
  "BashFoo"
  "Fork"
  "Bash"
  "Foo"
  "Bash"
  "88"
  "BashFoo"
  "90"
  "BashFork"
  "Foo"
  "Bash"
  "94"
  "BashFoo"
  "96"
  "Bash"
  "ForkFoo"
  "Bash"
  "100")

explanation

the macro creates a composition of continuation style passing functions, which will build a string and halt production in order to prevent a number being printed if an identifier has been.

the string building functions myprint, skip and halt are quite simple.

skip and halt both take a string and return a string, with skip acting as the identity function, and halt intead throwing away its argument and returning the empty string (the unit value for this composition).

myprint takes two strings and returns a string. the first is the message to add to the end of the string being built, and the second (curried) argument works as in skip and halt.

base exists as a CPS function that creates (once passed a continuation) a function from string to string, simply applying its continuation to the result of printing the number passed to base. it is the first rule in any fizzbuzz function to facilitate the default behaviour.

the macro then constructs an additional trigger for each predicate / string pair, which when given a number, returns a CPS function that when given a continuation will take a string, and either pass it unchanged to the continuation, or sandwich the continuation between a myprint and a halt, such that the halt will prevent the default behaviour.

after macro transformations, a trigger with the message message and the predicate predicate will look as follows:

(lambda (n)
   (lambda (k) 
      (lambda (s) 
         (if (p n)
             (myprint m (k (halt s)))
             (k s)))))

here, n is the number passed to the fizzbuzz function, k is the continuation function, and s is the string for the final string builder.

these are composed starting with base, and then each trigger in order to create the final fizzbuzz function.

author

  • tA