From 082082fcb94bdf57494dbd6ea81f40d1840af6ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20Bj=C3=B6rnstam?= Date: Thu, 4 Jun 2020 14:41:57 +0200 Subject: [PATCH] Initial commit. fizzin' and buzzin'. --- LICENCE | 6 +++++ README.md | 52 ++++++++++++++++++++++++++++++++++++++++ example.scm | 43 +++++++++++++++++++++++++++++++++ wheel-utils/wheel.scm | 56 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 157 insertions(+) create mode 100644 LICENCE create mode 100644 README.md create mode 100644 example.scm create mode 100644 wheel-utils/wheel.scm diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..c32cb80 --- /dev/null +++ b/LICENCE @@ -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. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4dac28b --- /dev/null +++ b/README.md @@ -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. diff --git a/example.scm b/example.scm new file mode 100644 index 0000000..3b6b2c1 --- /dev/null +++ b/example.scm @@ -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))))))) diff --git a/wheel-utils/wheel.scm b/wheel-utils/wheel.scm new file mode 100644 index 0000000..9ab57a2 --- /dev/null +++ b/wheel-utils/wheel.scm @@ -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)))))) + + +