.gitignore | ||
fizzbuzz.rkt | ||
README.md |
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