first commit

This commit is contained in:
Thorn Avery 2020-07-24 17:40:40 +12:00
commit 99a1aa94c5
3 changed files with 252 additions and 0 deletions

BIN
.fizzbuzz.rkt.swp Normal file

Binary file not shown.

203
README.md Normal file
View File

@ -0,0 +1,203 @@
# 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

49
fizzbuzz.rkt Normal file
View File

@ -0,0 +1,49 @@
#lang racket
(define (skip s)
s)
(define (halt s)
"")
(define (myprint s x)
(string-append s x))
(define (base n)
(lambda (k)
(lambda (s)
(k (myprint (number->string n) s)))))
(define (mult n)
(lambda (k) (= 0 (modulo k n))))
(require (for-syntax syntax/parse racket/base))
(begin-for-syntax
(define-syntax-class fbline
#:description "predicate string pair"
(pattern (p:expr m:string))))
(define-syntax (create-trigger stx)
(syntax-parse stx
[(_ p:expr m:string)
#'(lambda (n)
(lambda (k)
(lambda (s)
(if (p n)
(myprint m (k (halt s)))
(k s)))))]))
(define-syntax (define-fizzbuzz stx)
(syntax-parse stx
[(_ name:identifier l:fbline ...)
#'(define (name n)
(((compose (base n)
((create-trigger l.p l.m) n) ...)
skip) ""))]))
(define-fizzbuzz fizzbuzz
[(mult 3) "Fizz"]
[(mult 5) "Buzz"])
(provide mult define-fizzbuzz fizzbuzz)