Intersecting Number Wheels
October 1, 2019
Today’s exercise is a new task from Rosetta Code:
A number wheel has a name which is an uppercase letter and a set of ordered values which are either numbers or names. A number is generated from a named wheel by starting at the first value of the names wheel and advancing through subesquent values then wrapping around to the first value to form a “wheel”, according to the following rules: If the value is a number, yield it; if the value is a name, yield the next value from the named wheel; advance the position of the wheel. For instance, given two wheels A: 1 B 2 and B: 3 4, the sequence generated for wheel A is 1 3 2 1 4 2 1 3 2 1 4 2 ….
Your task is to write a program to calculate the sequences generated by intersecting number wheels. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.
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: