From 99a1aa94c5f5ea898356a9c6e42769cf05eb5d66 Mon Sep 17 00:00:00 2001 From: tA Date: Fri, 24 Jul 2020 17:40:40 +1200 Subject: [PATCH] first commit --- .fizzbuzz.rkt.swp | Bin 0 -> 12288 bytes README.md | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ fizzbuzz.rkt | 49 +++++++++++++ 3 files changed, 252 insertions(+) create mode 100644 .fizzbuzz.rkt.swp create mode 100644 README.md create mode 100644 fizzbuzz.rkt diff --git a/.fizzbuzz.rkt.swp b/.fizzbuzz.rkt.swp new file mode 100644 index 0000000000000000000000000000000000000000..2053eec839dad60bce5d77be46c510767a8fd656 GIT binary patch literal 12288 zcmeI2y>HV%7>BQnw513(7LF9D=fO$S3aJW$fdSEhkq@b=$|ZJEBd@4XvNswbijtvL9d}@$F-wI*BK7m@o?zLjtOjb@CLjF&%h(# zfmLt`oCb4Xcb2h_;3aqr?t^P!2{geB_%XxSJMa|jfa~BaXaNqU!8gRQ3(|NFU_NNc zWPl8i0Wv@a$N(8217u*I286)L+S`t0b1&{k+_JllZ=1ylTV6aE_+y##HIX(tWANGq zoWHHD8zmTtv3txJbx;3%Eladx>_>t3fl6hL)b{|5@ zNkq8iM{-~)sWEOC4a!)v3jSBBY)n zDi6Zxa-sHwT-1ub^xBr>A{_5p*0_}#m?}Dy0uxE9We=3*UMoa8{9f8)vX`+8R<#Hv zqsZ1i);%`#D9HJiWrrQ@sK^O?UQ^n(oDQ}Ga*!;7E9vN3BBV$_o~TY=h9S?g$VA)j zIlkEqw3$^iCbWGkb~JX%sFY`wjV#Ieq4Q)TK{=)DvN#$-1&D$zPF8;m2Ov*5>&<1nlK(FR;9AVLwY(W!ljU#r96Rh-@5FDrNf?5BXrv kTJk-V6sHTqOE(%P-&1J?rYj>z-cPIwM}PYgmNOUZ5A0oRQ~&?~ literal 0 HcmV?d00001 diff --git a/README.md b/README.md new file mode 100644 index 0000000..b272d74 --- /dev/null +++ b/README.md @@ -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 diff --git a/fizzbuzz.rkt b/fizzbuzz.rkt new file mode 100644 index 0000000..634a34e --- /dev/null +++ b/fizzbuzz.rkt @@ -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)