Intersecting Number Wheels
October 1, 2019
Our implementation follows the description exactly:
(define-generator (wheel xs)
(let loop ((ws (cycle xs)))
(if (integer? (car ws))
(yield (car ws))
(yield ((eval (car ws)))))
(loop (cdr ws))))
Here are the examples from the Rosetta Code description:
> (define a (wheel '(1 2 3))) > (take-gen 20 a) (1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2) > (define a (wheel '(1 b 2))) > (define b (wheel '(3 4))) > (take-gen 20 a) (1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3) > (define a (wheel '(1 d d))) > (define d (wheel '(6 7 8))) > (take-gen 20 a) (1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6) > (define a (wheel '(1 b c))) > (define b (wheel '(3 4))) > (define c (wheel '(5 b))) > (take-gen 20 a) (1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4)
You can see the assembled program at https://ideone.com/lpMU0a, but it doesn’t run, I don’t know why, something to do with the define-generator macro. I can assure you it runs fine in Chez Scheme on my systems at home and work.
Here is a very simple solution using only R7RS Scheme plus
circular-list from SRFI 1 (and unfold for a demo). It is avoids eval
but uses destructive updates. It also requires the wheels to be
defined without forward references, which could be considered a
feature that disallows wheels like A = (B) with B = (A).
(import (scheme base) (scheme write) (only (srfi 1) circular-list unfold)) (define (number-wheel . vals) (let ((clst (apply circular-list vals))) (lambda () (let ((v (car clst))) (set! clst (cdr clst)) (if (procedure? v) (v) v))))) (define (demo) (define B (number-wheel 3 4)) (define A (number-wheel 1 B 2)) (display (unfold (lambda (s) (>= s 20)) (lambda (s) (A)) (lambda (s) (+ s 1)) 0)) (newline)) (demo)Output:
Here’s a C++ solution, read the wheels from the command line, 1st arg is for A, 2nd for B etc. Store in array and use second array to store current offset for each wheel:
#include <iostream> #include <vector> #include <string> int main(int argc, char *argv[]) { std::vector<std::string> wheels(argv+1,argv+argc); std::vector<int> state(wheels.size()); for (int i = 0; i < 20; i++) { int n = 0; while(true) { char c = wheels[n][state[n]]; state[n] = (state[n]+1)%wheels[n].size(); if (isdigit(c)) { std::cout << c; break; } n = c-'A'; } } std::cout << "\n"; }A better version:
#include <iostream> #include <vector> #include <string> class Wheels { public: template <typename IT> Wheels(IT start, IT end) : wheels(start,end), state(wheels.size()) { } int operator()() { int n = 0; while (true) { char c = wheels[n][state[n]]; state[n] = (state[n]+1)%wheels[n].size(); if (isdigit(c)) return c-'0'; else n = c-'A'; } } private: std::vector<std::string> wheels; std::vector<int> state; }; int main(int argc, char *argv[]) { Wheels wheels(argv+1,argv+argc); for (int i = 0; i < 20; i++) { std::cout << wheels(); } std::cout << "\n"; }Here’s a solution in Python.
from collections import deque, OrderedDict def gen(wheels): wheel = first = wheels[next(iter(wheels))] while True: value = wheel[0] wheel.rotate(-1) if isinstance(value, int): wheel = first yield value else: wheel = wheels[value] wheels = OrderedDict(( ('A', deque([1, 'B', 2])), ('B', deque([3, 4])), )) for x in gen(wheels): print(x)Output: