Browse Source

first commit

master
Thorn Avery 3 years ago
commit
99a1aa94c5
3 changed files with 252 additions and 0 deletions
  1. BIN
      .fizzbuzz.rkt.swp
  2. +203
    -0
      README.md
  3. +49
    -0
      fizzbuzz.rkt

BIN
.fizzbuzz.rkt.swp View File


+ 203
- 0
README.md 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
- 0
fizzbuzz.rkt 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)

Loading…
Cancel
Save