Initial commit.

fizzin' and buzzin'.
This commit is contained in:
Linus Björnstam 2020-06-04 14:41:57 +02:00
commit 082082fcb9
4 changed files with 157 additions and 0 deletions

6
LICENCE Normal file
View file

@ -0,0 +1,6 @@
Copyright 2020 Linus Björnstam
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all source copies.
The software is provided "as is", without any express or implied warranties.

52
README.md Normal file
View file

@ -0,0 +1,52 @@
# wheel-utils
I wanted to write a fast, straight-forward, extensible fizzbuzz program. This is a small utility I wrote to do it. It can generate simple wheels that you can use if you have a recurring sequence (like fizzbuzz).
## documentation
## usage
Put it in your site-dir. Then you should be able to:
(use-modules (wheel-utils wheel))
### every-n in value [n]
(every-n-in "fizz" 3) -> '(#f #f "fizz")
(every-n-in "bugz" 2 3) -> (#f "bugz" #f)
This is a procedure used to generate proto-wheels that are merged together into a larger proto-wheel using generate-wheel.
### generate-wheel combine proto-wheels ...
Combines proto-wheels using the combine procedure and returns a new, larger proto-wheel:
(define fizz '(every-n-in "fizz" 3))
(define buzz '(every-n-in "buzz" 5))
(define (combine . args)
(let ((args (filter values args)))
(if (null? args)
#f
(apply string-append args))))
(define proto-wheel (generate-wheel combine fizz buzz))
Proto-wheel can now be converted to a circular list or a vector depending on your needs. Most people will probably convert it to a circular-list, but wheels _can_ grow prohibitively large and using a vector might fit more into the cpu cache. The fizzbuzz wheel is just 15 elements long. A fizz-buzz-bar (where bar is instead of multiples of 7) is 105 elements long. fizz-buzz-bar-quux (quux instead of 11) means 1155 elements.
## How fast was your fizzbuzz
How does our fizzbuzz fare against a naive implementation? Using the code in example.scm (both procedures generate lists of fizzy and buzzy goodness) these are my numbers:
scheme@(guile-user)> ,time (car (regular-fizzbuzz 10000000))
$26 = 1
;; 1.161756s real time, 1.242915s run time. 0.314005s spent in GC.
scheme@(guile-user)> ,time (car (wheel-fizzbuzz 10000000))
$27 = 1
;; 0.528079s real time, 0.614268s run time. 0.312021s spent in GC.
It can of course be done faster, but not necessarily in such a simple way. The wheel version is also trivially extensible if you are unsure about your future fizzbuzzing needs.
## Todo
It would be fun to be able to generate step wheels, like for example a prime wheel where we can just skip large parts of the number line.
## Licence
a simplified ISC. Non of that big-letter stuff matters in my jurisdiction, so I removed it. You may use it under regular ISC if you please.

43
example.scm Normal file
View file

@ -0,0 +1,43 @@
(use-modules (srfi srfi-1)
(wheel-utils wheel))
;;; Example: fizzbuzz.
;; First we generate out sub-proto-wheels.
(define fizz (every-n-in "fizz" 3))
(define buzz (every-n-in "buzz" 5))
;; Then we merge our proto-wheels
(define proto-wheel (generate-wheel
(lambda x
(let ((l (filter values x)))
(if (null? l)
#f
(apply string-append l))))
fizz
buzz))
;; Then we make our wheel circular so that we don't have to check any null.
(define actual-wheel (apply circular-list proto-wheel))
;; Here we loop from 1 to 99 and print every "opening" in the wheel
;; This _should_ be quite a lot faster than a solution that uses
;; division, and it should be quite extensible. We could easily add
;; a baz every 7 numbers.
(define (wheel-fizzbuzz n)
(let loop ((i 1) (wheel actual-wheel))
(if (> i n)
'()
(cons (or (car wheel) i) (loop (+ i 1) (cdr wheel))))))
(define (regular-fizzbuzz n)
(let loop ((i 1))
(cond
((> i n) '())
((eq? 0 (euclidean-remainder i 15))
(cons "fizzbuzz" (loop (+ i 1))))
((eq? 0 (euclidean-remainder i 3))
(cons "fizz" (loop (+ i 1))))
((eq? 0 (euclidean-remainder i 5))
(cons "buzz" (loop (+ i 1))))
(else
(cons i (loop (+ i 1)))))))

56
wheel-utils/wheel.scm Normal file
View file

@ -0,0 +1,56 @@
;; Copyright 2020 Linus Björnstam
;;
;; Permission to use, copy, modify, and/or distribute this software for any
;; purpose with or without fee is hereby granted, provided that the above
;; copyright notice and this permission notice appear in all source copies.
;; The software is provided "as is", without any express or implied warranties.
;; Some code for generating simple wheels.
;; It has had zero optimization work done, meaning that long wheels can take
;; a long time to generate. In the far future, I hope to extend this to
;; do prime wheel distances: I want to be able to feed it a list of primes
;; and that it should generate "steps" to take to jump over multiples of said
;; primes. It doesn't do that right now.
(define-module (wheel-utils wheel)
#:use-module ((srfi srfi-1) #:select (every))
#:export (every-n-in generate-wheel))
(define (next lsts)
(map (lambda (x) (if (null? (cddr x))
(cons (car x) (car x))
(cons (car x) (cddr x))))
lsts))
(define (stop? lsts)
(every (lambda (x) (null? (cddr x))) lsts))
(define every-n-in
(case-lambda
((val n)
(every-n-in val n n))
((val pos out-of)
(let loop ((i 1))
(cond
((= i (+ out-of 1))
'())
((= i pos)
(cons val (loop (+ i 1))))
(else
(cons #f (loop (+ i 1)))))))
((val n i . pos)
(error "not implemented yet"))))
(define (generate-wheel combine . lsts)
;; We need to keep track of the starting point of the lists
;; so we store them in a pair of (start . current-pos)
(let loop ((lsts (map (lambda (x) (cons x x)) lsts)))
(cons
(apply combine (map cadr lsts))
(if (stop? lsts)
'()
(loop (next lsts))))))