Intersecting number wheels: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎{{header|Haskell}}: Slight disaggregation)
 
(40 intermediate revisions by 17 users not shown)
Line 61: Line 61:


<br>
<br>

=={{header|11l}}==
{{trans|Python}}

<syntaxhighlight lang="11l">F nextfrom(&w, =name)
L
V nxt = w[name][0]
w[name] = w[name][1..] + w[name][0.<1]
I nxt[0] C ‘0’..‘9’
R nxt
name = nxt

L(group) |‘A: 1 2 3
A: 1 B 2; B: 3 4
A: 1 D D; D: 6 7 8
A: 1 B C; B: 3 4; C: 5 B’.split("\n")
print("Intersecting Number Wheel group:\n "group)
[String = [String]] wheel
V first = ‘’
L(w) group.split(‘;’)
V s = w.trim(‘ ’).split(‘ ’)
V name = s[0]
wheel[name[0 .< (len)-1]] = s[1..]
first = I first == ‘’ {name[0 .< (len)-1]} E first
V gen = (0.<20).map(i -> nextfrom(&@wheel, @first)).join(‘ ’)
print(" Generates:\n "gen" ...\n")</syntaxhighlight>

{{out}}
<pre>
Intersecting Number Wheel group:
A: 1 2 3
Generates:
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 ...

Intersecting Number Wheel group:
A: 1 B 2; B: 3 4
Generates:
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3 ...

Intersecting Number Wheel group:
A: 1 D D; D: 6 7 8
Generates:
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6 ...

Intersecting Number Wheel group:
A: 1 B C; B: 3 4; C: 5 B
Generates:
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4 ...
</pre>

=={{header|ALGOL 68}}==
=={{header|ALGOL 68}}==
<lang algol68>BEGIN
<syntaxhighlight lang="algol68">BEGIN
# a number wheel element #
# a number wheel element #
MODE NWELEMENT = UNION( CHAR # wheel name #, INT # wheel value # );
MODE NWELEMENT = UNION( CHAR # wheel name #, INT # wheel value # );
Line 117: Line 167:
, NW( "B", LOC INT := 1, ( 3, 4 ) )
, NW( "B", LOC INT := 1, ( 3, 4 ) )
, NW( "C", LOC INT := 1, ( 5, "B" ) ) ) )
, NW( "C", LOC INT := 1, ( 5, "B" ) ) ) )
END</lang>
END</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Line 141: Line 191:


</pre>
</pre>

=={{header|AutoHotkey}}==
<syntaxhighlight lang="autohotkey">obj1 := {"A":[1, 2, 3]}
obj2 := {"A":[1, "B", 2] , "B":[3, 4]}
obj3 := {"A":[1, "D", "D"] , "D":[6, 7, 8]}
obj4 := {"A":[1, "B", "C"] , "B":[3, 4] , "C":[5, "B"]}

loop 4
{
str := ""
for k, v in obj%A_Index% {
str .= "{" k " : "
for i, t in v
str .= t ","
str := Trim(str, ",") "}, "
}
str := Trim(str, ", ")
x := INW(obj%A_Index%)
result .= str "`n" x.1 "`n" x.2 "`n------`n"
}
MsgBox % result
return

INW(obj, num:=20){
sets := [], ptr := []
for k, v in obj {
if A_Index=1
s := k, s1 := k
%k% := v, sets.Push(k), ptr[k] := 0
}
loop % num {
ptr[s]++
while !((v := %s%[ptr[s]]) ~= "\d") {
s := %s%[ptr[s]]
ptr[s]++
}
key .= s "." ptr[s] ", "
result .= %s%[ptr[s]] " "
s := s1
for i, set in sets
ptr[set] := ptr[set] = %set%.count() ? 0 : ptr[set]
}
return [key, result]
}</syntaxhighlight>
{{out}}
<pre>{A : 1,2,3}
A.1, A.2, A.3, A.1, A.2, A.3, A.1, A.2, A.3, A.1, A.2, A.3, A.1, A.2, A.3, A.1, A.2, A.3, A.1, A.2,
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2
------
{A : 1,B,2}, {B : 3,4}
A.1, B.1, A.3, A.1, B.2, A.3, A.1, B.1, A.3, A.1, B.2, A.3, A.1, B.1, A.3, A.1, B.2, A.3, A.1, B.1,
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3
------
{A : 1,D,D}, {D : 6,7,8}
A.1, D.1, D.2, A.1, D.3, D.1, A.1, D.2, D.3, A.1, D.1, D.2, A.1, D.3, D.1, A.1, D.2, D.3, A.1, D.1,
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6
------
{A : 1,B,C}, {B : 3,4}, {C : 5,B}
A.1, B.1, C.1, A.1, B.2, B.1, A.1, B.2, C.1, A.1, B.1, B.2, A.1, B.1, C.1, A.1, B.2, B.1, A.1, B.2,
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4
------</pre>

=={{header|C}}==
<syntaxhighlight lang="c">#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Wheel {
char *seq;
int len;
int pos;
};

struct Wheel *create(char *seq) {
struct Wheel *w = malloc(sizeof(struct Wheel));
if (w == NULL) {
return NULL;
}

w->seq = seq;
w->len = strlen(seq);
w->pos = 0;

return w;
}

char cycle(struct Wheel *w) {
char c = w->seq[w->pos];
w->pos = (w->pos + 1) % w->len;
return c;
}

struct Map {
struct Wheel *v;
struct Map *next;
char k;
};

struct Map *insert(char k, struct Wheel *v, struct Map *head) {
struct Map *m = malloc(sizeof(struct Map));
if (m == NULL) {
return NULL;
}

m->k = k;
m->v = v;
m->next = head;

return m;
}

struct Wheel *find(char k, struct Map *m) {
struct Map *ptr = m;

while (ptr != NULL) {
if (ptr->k == k) {
return ptr->v;
}
ptr = ptr->next;
}

return NULL;
}

void printOne(char k, struct Map *m) {
struct Wheel *w = find(k, m);
char c;

if (w == NULL) {
printf("Missing the wheel for: %c\n", k);
exit(1);
}

c = cycle(w);
if ('0' <= c && c <= '9') {
printf(" %c", c);
} else {
printOne(c, m);
}
}

void exec(char start, struct Map *m) {
struct Wheel *w;
int i;

if (m == NULL) {
printf("Unable to proceed.");
return;
}

for (i = 0; i < 20; i++) {
printOne(start, m);
}
printf("\n");
}

void group1() {
struct Wheel *a = create("123");

struct Map *m = insert('A', a, NULL);

exec('A', m);
}

void group2() {
struct Wheel *a = create("1B2");
struct Wheel *b = create("34");

struct Map *m = insert('A', a, NULL);
m = insert('B', b, m);

exec('A', m);
}

void group3() {
struct Wheel *a = create("1DD");
struct Wheel *d = create("678");

struct Map *m = insert('A', a, NULL);
m = insert('D', d, m);

exec('A', m);
}

void group4() {
struct Wheel *a = create("1BC");
struct Wheel *b = create("34");
struct Wheel *c = create("5B");

struct Map *m = insert('A', a, NULL);
m = insert('B', b, m);
m = insert('C', c, m);

exec('A', m);
}

int main() {
group1();
group2();
group3();
group4();

return 0;
}</syntaxhighlight>
{{out}}
<pre> 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4</pre>


=={{header|C sharp}}==
=={{header|C sharp}}==
<lang csharp>using System;
<syntaxhighlight lang="csharp">using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Linq;
Line 177: Line 436:


static void Print(this IEnumerable<char> sequence) => Console.WriteLine(string.Join(" ", sequence));
static void Print(this IEnumerable<char> sequence) => Console.WriteLine(string.Join(" ", sequence));
}</lang>
}</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Line 184: Line 443:
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4</pre>
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4</pre>

=={{header|C++}}==
{{trans|D}}
<syntaxhighlight lang="cpp">#include <initializer_list>
#include <iostream>
#include <map>
#include <vector>

struct Wheel {
private:
std::vector<char> values;
size_t index;

public:
Wheel() : index(0) {
// empty
}

Wheel(std::initializer_list<char> data) : values(data), index(0) {
//values.assign(data);
if (values.size() < 1) {
throw new std::runtime_error("Not enough elements");
}
}

char front() {
return values[index];
}

void popFront() {
index = (index + 1) % values.size();
}
};

struct NamedWheel {
private:
std::map<char, Wheel> wheels;

public:
void put(char c, Wheel w) {
wheels[c] = w;
}

char front(char c) {
char v = wheels[c].front();
while ('A' <= v && v <= 'Z') {
v = wheels[v].front();
}
return v;
}

void popFront(char c) {
auto v = wheels[c].front();
wheels[c].popFront();

while ('A' <= v && v <= 'Z') {
auto d = wheels[v].front();
wheels[v].popFront();
v = d;
}
}
};

void group1() {
Wheel w({ '1', '2', '3' });
for (size_t i = 0; i < 20; i++) {
std::cout << ' ' << w.front();
w.popFront();
}
std::cout << '\n';
}

void group2() {
Wheel a({ '1', 'B', '2' });
Wheel b({ '3', '4' });

NamedWheel n;
n.put('A', a);
n.put('B', b);

for (size_t i = 0; i < 20; i++) {
std::cout << ' ' << n.front('A');
n.popFront('A');
}
std::cout << '\n';
}

void group3() {
Wheel a({ '1', 'D', 'D' });
Wheel d({ '6', '7', '8' });

NamedWheel n;
n.put('A', a);
n.put('D', d);

for (size_t i = 0; i < 20; i++) {
std::cout << ' ' << n.front('A');
n.popFront('A');
}
std::cout << '\n';
}

void group4() {
Wheel a({ '1', 'B', 'C' });
Wheel b({ '3', '4' });
Wheel c({ '5', 'B' });

NamedWheel n;
n.put('A', a);
n.put('B', b);
n.put('C', c);

for (size_t i = 0; i < 20; i++) {
std::cout << ' ' << n.front('A');
n.popFront('A');
}
std::cout << '\n';
}

int main() {
group1();
group2();
group3();
group4();

return 0;
}</syntaxhighlight>
{{out}}
<pre> 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4</pre>

=={{header|D}}==
<syntaxhighlight lang="d">import std.exception;
import std.range;
import std.stdio;

struct Wheel {
private string[] values;
private uint index;

invariant {
enforce(index < values.length, "index out of range");
}

this(string[] value...) in {
enforce(value.length > 0, "Cannot create a wheel with no elements");
} body {
values = value;
}

enum empty = false;

auto front() {
return values[index];
}

void popFront() {
index = (index + 1) % values.length;
}
}

struct NamedWheel {
private Wheel[char] wheels;
char m;

this(char c, Wheel w) {
add(c, w);
m = c;
}

void add(char c, Wheel w) {
wheels[c] = w;
}

enum empty = false;

auto front() {
auto v = wheels[m].front;
char c = v[0];
while ('A' <= c && c <= 'Z') {
v = wheels[c].front;
c = v[0];
}
return v;
}

void popFront() {
auto v = wheels[m].front;
wheels[m].popFront;

char c = v[0];
while ('A' <= c && c <= 'Z') {
auto d = wheels[c].front;
wheels[c].popFront;
c = d[0];
}
}
}

void group1() {
auto a = Wheel("1", "2", "3");
a.take(20).writeln;
}

void group2() {
auto a = Wheel("1", "B", "2");
auto b = Wheel("3", "4");

auto n = NamedWheel('A', a);
n.add('B', b);

n.take(20).writeln;
}

void group3() {
auto a = Wheel("1", "D", "D");
auto d = Wheel("6", "7", "8");

auto n = NamedWheel('A', a);
n.add('D', d);

n.take(20).writeln;
}

void group4() {
auto a = Wheel("1", "B", "C");
auto b = Wheel("3", "4");
auto c = Wheel("5", "B");

auto n = NamedWheel('A', a);
n.add('B', b);
n.add('C', c);

n.take(20).writeln;
}

void main() {
group1();
group2();
group3();
group4();
}</syntaxhighlight>
{{out}}
<pre>["1", "2", "3", "1", "2", "3", "1", "2", "3", "1", "2", "3", "1", "2", "3", "1", "2", "3", "1", "2"]
["1", "3", "2", "1", "4", "2", "1", "3", "2", "1", "4", "2", "1", "3", "2", "1", "4", "2", "1", "3"]
["1", "6", "7", "1", "8", "6", "1", "7", "8", "1", "6", "7", "1", "8", "6", "1", "7", "8", "1", "6"]
["1", "3", "5", "1", "4", "3", "1", "4", "5", "1", "3", "4", "1", "3", "5", "1", "4", "3", "1", "4"]</pre>


=={{header|F_Sharp|F#}}==
=={{header|F_Sharp|F#}}==
<lang fsharp>
<syntaxhighlight lang="fsharp">
// Wheels within wheels. Nigel Galloway: September 30th., 2019.
// Wheels within wheels. Nigel Galloway: September 30th., 2019.
let N(n)=fun()->n
let N(n)=fun()->n
Line 206: Line 714:
for n in 0..20 do printf "%d " (A4())
for n in 0..20 do printf "%d " (A4())
printfn ""
printfn ""
</syntaxhighlight>
</lang>
{{out}}
{{out}}
<pre>
<pre>
Line 231: Line 739:
⁠— a dictionary-like structure that is transformed into a lazy list which yields the expected sequence elements.
⁠— a dictionary-like structure that is transformed into a lazy list which yields the expected sequence elements.
{{works with|Factor|0.99 2019-07-10}}
{{works with|Factor|0.99 2019-07-10}}
<lang factor>USING: accessors assocs circular io kernel lists lists.lazy math
<syntaxhighlight lang="factor">USING: accessors assocs circular io kernel lists lists.lazy math
math.parser multiline peg.ebnf prettyprint prettyprint.custom
math.parser multiline peg.ebnf prettyprint prettyprint.custom
sequences strings ;
sequences strings ;
Line 266: Line 774:


: .take ( n group -- )
: .take ( n group -- )
list>> ltake list>array [ pprint bl ] each "..." print ;</lang>
list>> ltake list>array [ pprint bl ] each "..." print ;</syntaxhighlight>
Now the interface defined above may be used:
Now the interface defined above may be used:
<lang factor>USING: generalizations io kernel prettyprint
<syntaxhighlight lang="factor">USING: generalizations io kernel prettyprint
rosetta-code.number-wheels ;
rosetta-code.number-wheels ;


Line 294: Line 802:
"Intersecting number wheel group:" print
"Intersecting number wheel group:" print
[ . ] [ "Generates:" print 20 swap .take nl ] bi
[ . ] [ "Generates:" print 20 swap .take nl ] bi
] 4 napply</lang>
] 4 napply</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Line 323: Line 831:


=={{header|Go}}==
=={{header|Go}}==
<lang go>package main
<syntaxhighlight lang="go">package main


import (
import (
Line 411: Line 919:
generate(wheels, "A", 20)
generate(wheels, "A", 20)
}
}
}</lang>
}</syntaxhighlight>


{{out}}
{{out}}
Line 445: Line 953:
terminating at the first digit found, and printing a map-accumulation of that recursion over a list of given length but arbitrary content.
terminating at the first digit found, and printing a map-accumulation of that recursion over a list of given length but arbitrary content.


<lang haskell>import qualified Data.Map.Strict as M
<syntaxhighlight lang="haskell">import Data.Char (isDigit)
import Data.Maybe (fromMaybe)
import Data.List (mapAccumL)
import Data.List (mapAccumL)
import Data.Char (isDigit)
import qualified Data.Map.Strict as M
import Data.Bool (bool)
import Data.Maybe (fromMaybe)

---------------- INTERSECTING NUMBER WHEELS --------------


clockWorkTick :: M.Map Char String -> (M.Map Char String, Char)
clockWorkTick ::
M.Map Char String ->
(M.Map Char String, Char)
clockWorkTick = flip click 'A'
clockWorkTick = flip click 'A'
where
where
click wheels name =
click wheels name
let wheel = fromMaybe ['?'] (M.lookup name wheels)
| isDigit name = (wheels, name)
v = head wheel
| otherwise =
in bool
( click
click
. flip
(,)
(M.insert name . leftRotate)
(isDigit v || '?' == v)
wheels
(M.insert name (leftRotate wheel) wheels)
<*> head
v
)
$ fromMaybe ['?'] $ M.lookup name wheels


leftRotate :: [a] -> [a]
leftRotate :: [a] -> [a]
leftRotate = take . length <*> (tail . cycle)
leftRotate = take . length <*> (tail . cycle)


--------------------------- TEST -------------------------
main :: IO ()
main :: IO ()
main = do
main = do
let wheelSets =
let wheelSets =
[ [('A', "123")]
[ [('A', "123")],
, [('A', "1B2"), ('B', "34")]
[('A', "1B2"), ('B', "34")],
, [('A', "1DD"), ('D', "678")]
[('A', "1DD"), ('D', "678")],
, [('A', "1BC"), ('B', "34"), ('C', "5B")]
[('A', "1BC"), ('B', "34"), ('C', "5B")]
]
]
putStrLn "State of each wheel-set after 20 clicks:\n"
putStrLn "State of each wheel-set after 20 clicks:\n"
mapM_ print $
mapM_ print $
fmap
fmap
( flip
(flip (mapAccumL (const . clockWorkTick)) (replicate 20 ' ') . M.fromList)
(mapAccumL (const . clockWorkTick))
(replicate 20 undefined)
. M.fromList
)
wheelSets
wheelSets
putStrLn "\nInitial state of the wheel-sets:\n"
putStrLn "\nInitial state of the wheel-sets:\n"
mapM_ print wheelSets</lang>
mapM_ print wheelSets</syntaxhighlight>
{{Out}}
{{Out}}
<pre>State of each wheel-set after 20 clicks:
<pre>State of each wheel-set after 20 clicks:
Line 496: Line 1,013:
[('A',"1DD"),('D',"678")]
[('A',"1DD"),('D',"678")]
[('A',"1BC"),('B',"34"),('C',"5B")]</pre>
[('A',"1BC"),('B',"34"),('C',"5B")]</pre>

=={{header|J}}==
Implementation:
<syntaxhighlight lang="j">
wheelgroup=:{{
yield_wheelgroup_=: {{
i=. wheels i.<;y
j=. i{inds
k=. ".;y
l=. j{k
inds=: ((#k)|1+j) i} inds
if. l e. wheels
do.yield l
else.{.".;l
end.
}}
gen_wheelgroup_=: {{
yield wheel
}}
grp=. cocreate ''
coinsert__grp 'wheelgroup'
specs__grp=: cut each boxopen m
wheel__grp=: ;{.wheels__grp=: {.every specs__grp
init__grp=: {{('inds';wheels)=:(0#~#specs);}.each specs}}
init__grp''
('gen_',(;grp),'_')~
}}
</syntaxhighlight>

Task examples:

<syntaxhighlight lang="j">
task=: {{y wheelgroup^:(1+i.20)_}}
task 'A 1 2 3'
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2
task 'A 1 B 2';'B 3 4'
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3
task 'A 1 D D';'D 6 7 8'
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6
task 'A 1 B C';'B 3 4';'C 5 B'
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4
</syntaxhighlight>

=={{header|Java}}==
<syntaxhighlight lang="java">
package intersectingNumberWheels;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;

public class WheelController {
private static final String IS_NUMBER = "[0-9]";
private static final int TWENTY = 20;
private static Map<String, WheelModel> wheelMap;

public static void advance(String wheel) {
WheelModel w = wheelMap.get(wheel);
if (w.list.get(w.position).matches(IS_NUMBER)) {
w.printThePosition();
w.advanceThePosition();
} else {
String wheelName = w.list.get(w.position);
advance(wheelName);
w.advanceThePosition();
}
}

public static void run() {
System.out.println(wheelMap);
IntStream.rangeClosed(1, TWENTY).forEach(i -> advance("A"));
System.out.println();
wheelMap.clear();
}

public static void main(String[] args) {
wheelMap = new HashMap<>();
wheelMap.put("A", new WheelModel("A", "1", "2", "3"));
run();
wheelMap.put("A", new WheelModel("A", "1", "B", "2"));
wheelMap.put("B", new WheelModel("B", "3", "4"));
run();
wheelMap.put("A", new WheelModel("A", "1", "D", "D"));
wheelMap.put("D", new WheelModel("D", "6", "7", "8"));
run();
wheelMap.put("A", new WheelModel("A", "1", "B", "C"));
wheelMap.put("B", new WheelModel("B", "3", "4"));
wheelMap.put("C", new WheelModel("C", "5", "B"));
run();
}

}

class WheelModel {
String name;
List<String> list;
int position;
int endPosition;
private static final int INITIAL = 0;

public WheelModel(String name, String... values) {
super();

this.name = name.toUpperCase();
this.list = new ArrayList<>();
for (String value : values) {
list.add(value);
}
this.position = INITIAL;
this.endPosition = this.list.size() - 1;
}

@Override
public String toString() {
return list.toString();
}

public void advanceThePosition() {
if (this.position == this.endPosition) {
this.position = INITIAL;// new beginning
} else {
this.position++;// advance position
}
}

public void printThePosition() {
System.out.print(" " + this.list.get(position));
}
}
</syntaxhighlight>
Output:
{A=[1, 2, 3]}
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2
{A=[1, B, 2], B=[3, 4]}
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3
{A=[1, D, D], D=[6, 7, 8]}
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6
{A=[1, B, C], B=[3, 4], C=[5, B]}
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4


=={{header|JavaScript}}==
=={{header|JavaScript}}==
Line 502: Line 1,159:
{{Trans|Haskell}}
{{Trans|Haskell}}
{{Trans|Python}}
{{Trans|Python}}
<lang javascript>(() => {
<syntaxhighlight lang="javascript">(() => {
'use strict';
'use strict';


Line 662: Line 1,319:
// MAIN ---
// MAIN ---
return main();
return main();
})();</lang>
})();</syntaxhighlight>
{{Out}}
{{Out}}
<pre>Series and state of each wheel-set after 20 clicks:
<pre>Series and state of each wheel-set after 20 clicks:
Line 677: Line 1,334:
{"A":"1DD"},{"D":"678"}
{"A":"1DD"},{"D":"678"}
{"A":"1BC"},{"B":"34"},{"C":"5B"}</pre>
{"A":"1BC"},{"B":"34"},{"C":"5B"}</pre>

=={{header|jq}}==
{{works with|jq}}
'''Also works with gojq, the Go implementation of jq'''

In this entry, a single wheel is simply represented by
a JSON object of the form { name: array }

where `name` is its name, and `array` is an array of the values on the wheel in the order
in which they would be read.

A set of of number of wheels can thus be represented simply as the sum of the objects corresponding to each wheel.
Thus the collection of illustrative number wheel groups can be defined as follows:
<syntaxhighlight lang="jq">
def wheels: [
{
"A": [1, 2, 3]
},
{
"A": [1, "B", 2],
"B": [3, 4]
},
{
"A": [1, "D", "D"],
"D": [6, 7, 8]
},
{
"A": [1, "B", "C"],
"B": [3, 4],
"C": [5, "B"]
}
];
</syntaxhighlight>
<syntaxhighlight lang="jq">
# read($wheel)
# where $wheel is the wheel to be read (a string)
# Input: a set of wheels
# Output: an object such that .value is the next value,
# and .state is the updated state of the set of wheels
def read($wheel):

# Input: an array
# Output: the rotated array
def rotate: .[1:] + [.[0]];

.[$wheel][0] as $value
| (.[$wheel] |= rotate) as $state
| if ($value | type) == "number"
then {$value, $state}
else $state | read($value)
end;

# Read wheel $wheel $n times
def multiread($wheel; $n):
if $n <= 0 then empty
else read($wheel)
| .value, (.state | multiread($wheel; $n - 1))
end;

def printWheels:
keys[] as $k
| "\($k): \(.[$k])";

# Spin each group $n times
def spin($n):
wheels[]
| "The number wheel group:",
printWheels,
"generates",
([ multiread("A"; $n) ] | join(" ") + " ..."),
"";

spin(20)
</syntaxhighlight>
'''Invocation'''
<pre>
jq -nr -f intersecting-number-wheels.jq
</pre>

{{output}}
<pre>
The number wheel group:
A: [1,2,3]
generates
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 ...

The number wheel group:
A: [1,"B",2]
B: [3,4]
generates
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3 ...

The number wheel group:
A: [1,"D","D"]
D: [6,7,8]
generates
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6 ...

The number wheel group:
A: [1,"B","C"]
B: [3,4]
C: [5,"B"]
generates
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4 ...
</pre>


=={{header|Julia}}==
=={{header|Julia}}==
<lang julia>const d1 = Dict("A" => [["1", "2", "3"], 1])
<syntaxhighlight lang="julia">const d1 = Dict("A" => [["1", "2", "3"], 1])
const d2 = Dict("A" => [["1", "B", "2"], 1], "B" => [["3", "4"], 1])
const d2 = Dict("A" => [["1", "B", "2"], 1], "B" => [["3", "4"], 1])
const d3 = Dict("A" => [["1", "D", "D"], 1], "D" => [["6", "7", "8"], 1])
const d3 = Dict("A" => [["1", "D", "D"], 1], "D" => [["6", "7", "8"], 1])
Line 709: Line 1,471:


foreach(testwheels, [d1, d2, d3, d4])
foreach(testwheels, [d1, d2, d3, d4])
</lang>{{out}}
</syntaxhighlight>{{out}}
<pre>
<pre>
Number Wheels:
Number Wheels:
Line 732: Line 1,494:
</pre>
</pre>


=={{header|Perl 6}}==
=={{header|Kotlin}}==
{{trans|Java}}
A succinct Perl 6 example using a few additional language features. Wheels are implemented as infinite repeating sequences, allowing a single iterator to keep track of the current position. This means the code contains no position tracking whatsoever.
<syntaxhighlight lang="scala">import java.util.Collections
<lang perl6>
import java.util.stream.IntStream
#| advance rotates a named wheel $n by consuming an item from an infinite sequence. It is called

#| from within a gather block and so can use take in order to construct an infinite, lazy sequence
object WheelController {
#| of result values
private val IS_NUMBER = "[0-9]".toRegex()
sub advance($g, $n) {
private const val TWENTY = 20
given $g{$n}.pull-one {
private var wheelMap = mutableMapOf<String, WheelModel>()
when /\d/ { take $_ }

default { samewith $g, $_ } # samewith re-calls this function with new parameters
private fun advance(wheel: String) {
}
val w = wheelMap[wheel]
if (w!!.list[w.position].matches(IS_NUMBER)) {
w.printThePosition()
} else {
val wheelName = w.list[w.position]
advance(wheelName)
}
w.advanceThePosition()
}

private fun run() {
println(wheelMap)
IntStream.rangeClosed(1, TWENTY)
.forEach { advance("A") }
println()
wheelMap.clear()
}

@JvmStatic
fun main(args: Array<String>) {
wheelMap["A"] = WheelModel("1", "2", "3")
run()
wheelMap["A"] = WheelModel("1", "B", "2")
wheelMap["B"] = WheelModel("3", "4")
run()
wheelMap["A"] = WheelModel("1", "D", "D")
wheelMap["D"] = WheelModel("6", "7", "8")
run()
wheelMap["A"] = WheelModel("1", "B", "C")
wheelMap["B"] = WheelModel("3", "4")
wheelMap["C"] = WheelModel("5", "B")
run()
}
}
}


internal class WheelModel(vararg values: String?) {
#| Input groups are a hash containing each wheel name as the key, and a list of values constructed
var list = mutableListOf<String>()
#| using <> to split on whitespace. They are transformed using xx * to repeat the list infinitely.
var position: Int
#| We then retrieve the underlying iterator in order for wheel position to be persistent. Each group
private var endPosition: Int
#| is then aggregated into a lazy output sequence using an infinite loop inside a gather block.

[
override fun toString(): String {
{A => <1 2 3>},
return list.toString()
{A => <1 B 2>, B => <3 4>},
}
{A => <1 D D>, D => <6 7 8>},

{A => <1 B C>, B => <3 4>, C => <5 B>},
fun advanceThePosition() {
]
if (position == endPosition) {
#| %() converts a list of pairs produced by map into a hash. $^k and $^v are implicit variables.
position = INITIAL // new beginning
#| They are processed in alphabetical order and make the block arity 2, called with two vars.
} else {
#| .kv gets the list of wheel names and wheel values from the input entry
position++ // advance position
==> map({ %(.kv.map: { $^k => (|$^v xx *).iterator }) })
}
#| gather constructs a lazy sequence, in which we infinitely loop advancing wheel A
}
==> map({ gather { loop { advance $_, 'A' }} })

#| state variables are only initialised once, and are kept between invocations.
fun printThePosition() {
==> map({ state $i = 1; say "Group {$i++}, First 20 values: $_[^20]" })
print(" ${list[position]}")
</lang>{{Output}}
}

companion object {
private const val INITIAL = 0
}

init {
Collections.addAll<String>(list, *values)
position = INITIAL
endPosition = list.size - 1
}
}</syntaxhighlight>
{{out}}
<pre>{A=[1, 2, 3]}
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2
{A=[1, B, 2], B=[3, 4]}
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3
{A=[1, D, D], D=[6, 7, 8]}
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6
{A=[1, B, C], B=[3, 4], C=[5, B]}
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4</pre>

=={{header|Maple}}==

<syntaxhighlight lang="maple">
with(ArrayTools):

module Wheel()
option object;
local spokes := Array([1,2,3]);
local currentSpoke := 1;

export currentValue::static := proc(self::Wheel)
local valueOut;
if type(self:-spokes[self:-currentSpoke], integer) then
valueOut := self:-spokes[self:-currentSpoke]:
else
valueOut := currentValue(self:-spokes[self:-currentSpoke]):
end if:
rotate(self):
return valueOut;
end proc:

export rotate::static := proc(self::Wheel)
if self:-currentSpoke = ArrayNumElems(self:-spokes) then self:-currentSpoke := 1:
else self:-currentSpoke += 1: end if:
end proc:

export ModuleApply::static := proc()
Object(Wheel, _passed);
end proc:

export ModuleCopy::static := proc(new::Wheel, proto::Wheel, spo::Array, curr::integer, $)
new:-spokes := spo:
new:-currentSpoke := curr:
end proc:
end module:

A := Wheel(Array([1,2,3]), 1):

seq(currentValue(A), 1..20);

A := Wheel(Array([1,B,2]), 1):
B := Wheel(Array([3,4]), 1):

seq(currentValue(A), 1..20);

A := Wheel(Array([1,d,d]), 1):
d := Wheel(Array([6,7,8]), 1):

seq(currentValue(A), 1..20);

A := Wheel(Array([1,b,C]), 1):
b := Wheel(Array([3,4]), 1):
C := Wheel(Array([5,b]), 1):

seq(currentValue(A), 1..20);
</syntaxhighlight>

{{out}}<pre>
1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2

1, 3, 2, 1, 4, 2, 1, 3, 2, 1, 4, 2, 1, 3, 2, 1, 4, 2, 1, 3

1, 6, 7, 1, 8, 6, 1, 7, 8, 1, 6, 7, 1, 8, 6, 1, 7, 8, 1, 6

1, 3, 5, 1, 4, 3, 1, 4, 5, 1, 3, 4, 1, 3, 5, 1, 4, 3, 1, 4

</pre>

=={{header|Nim}}==
<syntaxhighlight lang="nim">import strutils, tables

type

ElemKind = enum eValue, eWheel

Elem = object
case kind: ElemKind
of eValue:
value: Natural
of eWheel:
name: char

Wheel = ref object
elems: seq[Elem]
index: Natural

Wheels = Table[char, Wheel]

WheelDescription = tuple[name: char; elems: string]


func initWheels(wheels: openArray[WheelDescription]): Wheels =
## Initialize a table of wheels from an array of wheel descriptions.

for (name, elems) in wheels:
let wheel = new(Wheel)
for e in elems.splitWhitespace():
if e[0].isUpperAscii():
wheel.elems.add Elem(kind: eWheel, name: e[0])
else:
wheel.elems.add Elem(kind: eValue, value: e.parseInt())
result[name] = wheel


func next(wheels: Wheels; name: char): Natural =
## Return the next element from a wheel.

let wheel = wheels[name]
let elem = wheel.elems[wheel.index]
wheel.index = (wheel.index + 1) mod wheel.elems.len
result = case elem.kind
of eValue: elem.value
of eWheel: wheels.next(elem.name)


when isMainModule:

proc generate(wheelList: openArray[WheelDescription]; count: Positive) =
## Create the wheels from their description, then display
## the first "count" values generated by wheel 'A'.

let wheels = wheelList.initWheels()
for (name, elems) in wheelList:
echo name, ": ", elems
echo "generates:"
for _ in 1..count:
stdout.write ' ', wheels.next('A')
echo '\n'


{'A': "1 2 3"}.generate(20)
{'A': "1 B 2", 'B': "3 4"}.generate(20)
{'A': "1 D D", 'D': "6 7 8"}.generate(20)
{'A': "1 B C", 'B': "3 4", 'C': "5 B"}.generate(20)</syntaxhighlight>

{{out}}
<pre>A: 1 2 3
generates:
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2

A: 1 B 2
B: 3 4
generates:
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3

A: 1 D D
D: 6 7 8
generates:
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6

A: 1 B C
B: 3 4
C: 5 B
generates:
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4</pre>

=={{header|Perl}}==
{{trans|Julia}}
<syntaxhighlight lang="perl">use strict;
use warnings;
use feature 'say';

sub get_next {
my($w,%wheels) = @_;
my $wh = \@{$wheels{$w}}; # reference, not a copy
my $value = $$wh[0][$$wh[1]];
$$wh[1] = ($$wh[1]+1) % @{$$wh[0]};
defined $wheels{$value} ? get_next($value,%wheels) : $value;
}

sub spin_wheels {
my(%wheels) = @_;
say "$_: " . join ', ', @{${$wheels{$_}}[0]} for sort keys %wheels;
print get_next('A', %wheels) . ' ' for 1..20; print "\n\n";
}

spin_wheels(%$_) for
(
{'A' => [['1', '2', '3'], 0]},
{'A' => [['1', 'B', '2'], 0], 'B' => [['3', '4'], 0]},
{'A' => [['1', 'D', 'D'], 0], 'D' => [['6', '7', '8'], 0]},
{'A' => [['1', 'B', 'C'], 0], 'B' => [['3', '4'], 0], 'C' => [['5', 'B'], 0]},
);</syntaxhighlight>
{{out}}
<pre>A: 1, 2, 3
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2

A: 1, B, 2
B: 3, 4
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3

A: 1, D, D
D: 6, 7, 8
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6

A: 1, B, C
B: 3, 4
C: 5, B
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4</pre>

=={{header|Phix}}==
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">terms</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">wheels</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">integer</span> <span style="color: #000000;">n</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">,</span><span style="color: #000000;">n</span><span style="color: #0000FF;">),</span>
<span style="color: #000000;">pos</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">wheels</span><span style="color: #0000FF;">)),</span>
<span style="color: #000000;">wvs</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">vslice</span><span style="color: #0000FF;">(</span><span style="color: #000000;">wheels</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">wheel</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">rdx</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">while</span> <span style="color: #000000;">rdx</span><span style="color: #0000FF;"><=</span><span style="color: #000000;">n</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">p</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">pos</span><span style="color: #0000FF;">[</span><span style="color: #000000;">wheel</span><span style="color: #0000FF;">],</span>
<span style="color: #000000;">c</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">wheels</span><span style="color: #0000FF;">[</span><span style="color: #000000;">wheel</span><span style="color: #0000FF;">][</span><span style="color: #000000;">p</span><span style="color: #0000FF;">]</span>
<span style="color: #000000;">p</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">p</span><span style="color: #0000FF;">=</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">wheels</span><span style="color: #0000FF;">[</span><span style="color: #000000;">wheel</span><span style="color: #0000FF;">])?</span><span style="color: #000000;">2</span><span style="color: #0000FF;">:</span><span style="color: #000000;">p</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">pos</span><span style="color: #0000FF;">[</span><span style="color: #000000;">wheel</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">p</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">></span><span style="color: #008000;">'9'</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">wheel</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">c</span><span style="color: #0000FF;">,</span><span style="color: #000000;">wvs</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">else</span>
<span style="color: #000000;">res</span><span style="color: #0000FF;">[</span><span style="color: #000000;">rdx</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">c</span>
<span style="color: #000000;">rdx</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
<span style="color: #000000;">wheel</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">res</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">wheels</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{</span><span style="color: #008000;">"A123"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"A1B2"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"B34"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"A1DD"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"D678"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"A1BC"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"B34"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"C5B"</span><span style="color: #0000FF;">}}</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">wheels</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #0000FF;">?</span><span style="color: #000000;">terms</span><span style="color: #0000FF;">(</span><span style="color: #000000;">wheels</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #000000;">20</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<!--</syntaxhighlight>-->
{{out}}
<pre>
<pre>
"12312312312312312312"
Group 1, First 20 values: 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2
"13214213214213214213"
Group 2, First 20 values: 1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3
"16718617816718617816"
Group 3, First 20 values: 1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6
"13514314513413514314"
Group 4, First 20 values: 1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4
</pre>
</pre>


=={{header|Python}}==
=={{header|Python}}==
===Python: Original class and generator based===
===Python: Original class and generator based===
<lang python>from itertools import islice
<syntaxhighlight lang="python">from itertools import islice


class INW():
class INW():
Line 826: Line 1,877:
]:
]:
w = INW(**group)
w = INW(**group)
print(f"{w}\n Generates:\n {first(w, 20)} ...\n")</lang>
print(f"{w}\n Generates:\n {first(w, 20)} ...\n")</syntaxhighlight>


{{out}}
{{out}}
Line 854: Line 1,905:


===Python: Simplified procedural===
===Python: Simplified procedural===
<lang python>def nextfrom(w, name):
<syntaxhighlight lang="python">def nextfrom(w, name):
while True:
while True:
nxt, w[name] = w[name][0], w[name][1:] + w[name][:1]
nxt, w[name] = w[name][0], w[name][1:] + w[name][:1]
Line 874: Line 1,925:
first = name[:-1] if first is None else first
first = name[:-1] if first is None else first
gen = ' '.join(nextfrom(wheel, first) for i in range(20))
gen = ' '.join(nextfrom(wheel, first) for i in range(20))
print(f" Generates:\n {gen} ...\n")</lang>
print(f" Generates:\n {gen} ...\n")</syntaxhighlight>


{{out}}
{{out}}
Line 902: Line 1,953:
Input is just a list of Python dicts, and depends on c-python dicts being odered by key insertion order.
Input is just a list of Python dicts, and depends on c-python dicts being odered by key insertion order.


<lang python>def nextfromr(w, name):
<syntaxhighlight lang="python">def nextfromr(w, name):
nxt, w[name] = w[name][0], w[name][1:] + w[name][:1]
nxt, w[name] = w[name][0], w[name][1:] + w[name][:1]
return nxt if '0' <= nxt[0] <= '9' else nextfromr(w, nxt)
return nxt if '0' <= nxt[0] <= '9' else nextfromr(w, nxt)
Line 914: Line 1,965:
first = next(group.__iter__())
first = next(group.__iter__())
gen = ' '.join(nextfromr(group, first) for i in range(20))
gen = ' '.join(nextfromr(group, first) for i in range(20))
print(f" Generates:\n {gen} ...\n")</lang>
print(f" Generates:\n {gen} ...\n")</syntaxhighlight>


{{out}}
{{out}}
Line 943: Line 1,994:
{{Trans|Haskell}}
{{Trans|Haskell}}
{{Works with|Python|3.7}}
{{Works with|Python|3.7}}
<lang python>'''Intersecting number wheels'''
<syntaxhighlight lang="python">'''Intersecting number wheels'''


from functools import reduce
from itertools import cycle, islice
from itertools import cycle, islice
from functools import reduce




Line 954: Line 2,005:
digit found by recursive descent from a single
digit found by recursive descent from a single
click of the first wheel.'''
click of the first wheel.'''
def click(wheels, name):
def click(wheels):
wheel = wheels.get(name, ['?'])
def go(wheelName):
v = wheel[0]
wheel = wheels.get(wheelName, ['?'])
return (Tuple if isDigit(v) or '?' == v else curry(click))(
v = wheel[0]
insertDict(name)(
return (Tuple if v.isdigit() or '?' == v else click)(
leftRotate(wheel)
insertDict(wheelName)(leftRotate(wheel))(wheels)
)(wheels)
)(v)
)(v)
return go
return click(wheelMap, 'A')
return click(wheelMap)('A')




Line 973: Line 2,024:




# TEST ----------------------------------------------------
# ------------------------- TEST -------------------------
# main :: IO ()
# main :: IO ()
def main():
def main():
Line 997: Line 2,048:




# GENERIC -------------------------------------------------
# ----------------------- GENERIC ------------------------


# Tuple (,) :: a -> b -> (a, b)
# Tuple (,) :: a -> b -> (a, b)
Line 1,021: Line 2,072:
'''
'''
return lambda _: k
return lambda _: k


# curry :: ((a, b) -> c) -> a -> b -> c
def curry(f):
'''A curried function derived
from an uncurried function.
'''
return lambda x: lambda y: f(x, y)




# insertDict :: String -> a -> Dict -> Dict
# insertDict :: String -> a -> Dict -> Dict
def insertDict(k):
def insertDict(k):
'''A dictionary updated with a (k, v) pair.'''
'''A new dictionary updated with a (k, v) pair.'''
def go(v, dct):
def go(v, dct):
dup = dict(dct)
return dict(dct, **{k: v})
dup.update({k: v})
return dup
return lambda v: lambda dct: go(v, dct)
return lambda v: lambda dct: go(v, dct)


# isDigit :: Char -> Bool
def isDigit(c):
'''True if the character c is a digit.'''
return c.isdigit() and (1 == len(c))




# mapAccumL :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])
# mapAccumL :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])
def mapAccumL(f):
def mapAccumL(f):
'''A tuple of an accumulation and a list derived by a
'''A tuple of an accumulation and a map
combined map and fold,
with accumulation from left to right.
with accumulation from left to right.
'''
'''
def go(a, x):
def nxt(a, x):
tpl = f(a[0])(x)
tpl = f(a[0])(x)
return (tpl[0], a[1] + [tpl[1]])
return tpl[0], a[1] + [tpl[1]]

return lambda acc: lambda xs: (
reduce(go, xs, (acc, []))
def go(acc):
)
def g(xs):
return reduce(nxt, xs, (acc, []))
return g
return go




# MAIN ---
# MAIN ---
if __name__ == '__main__':
if __name__ == '__main__':
main()</lang>
main()</syntaxhighlight>
{{Out}}
{{Out}}
<pre>New state of wheel sets, after 20 clicks of each:
<pre>New state of wheel sets, after 20 clicks of each:
Line 1,077: Line 2,114:
{'A': '1DD', 'D': '678'}
{'A': '1DD', 'D': '678'}
{'A': '1BC', 'B': '34', 'C': '5B'}</pre>
{'A': '1BC', 'B': '34', 'C': '5B'}</pre>

=={{header|Quackery}}==

As the contents of a wheel (e.g. <code>[ 1 B 2 ]</code>) is just Quackery code, wheels can be extended in interesting ways.

They could, for example, contain a nest that randomly selects a wheel to advance; <code>[ 1 [ 2 random table [ B C ] ] 2 ]</code> would do the same as <code>[ 1 B 2 ]</code>, except that on the second click of the wheel, instead of always advancing wheel <code>B</code>, <code>[ 2 random table [ B C ] ]</code> would be evaluated, causing either wheel <code>B</code> or wheel <code>C</code> to advance arbitrarily.

<syntaxhighlight lang="quackery"> [ ]this[ ]done[
dup take behead
dup dip
[ nested join
swap put ]
do ] is wheel ( --> n )

[ ]'[
]'[ nested
' [ wheel ]
swap join
swap replace ] is newwheel ( --> )
forward is A forward is B forward is C
forward is D ( and so on, as required )
[ wheel [ 1 2 3 ] ] resolves A ( --> n )

[ wheel [ 3 4 ] ] resolves B ( --> n )

[ wheel [ 5 B ] ] resolves C ( --> n )

[ wheel [ 6 7 8 ] ] resolves D ( --> n )


20 times [ A echo sp ] cr
newwheel A [ 1 B 2 ]
20 times [ A echo sp ] cr
newwheel A [ 1 D D ]
20 times [ A echo sp ] cr
newwheel A [ 1 B C ]
newwheel B [ 3 4 ] ( As B has been used already )
( it's state may be [ 4 3 ]. )
( So we reset it to [ 3 4 ]. )

20 times [ A echo sp ] cr</syntaxhighlight>

{{out}}

<pre>1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4 </pre>

=={{header|Raku}}==
(formerly Perl 6)
A succinct Raku example using a few additional language features. Wheels are implemented as infinite repeating sequences, allowing a single iterator to keep track of the current position. This means the code contains no position tracking whatsoever.
<syntaxhighlight lang="raku" line>
#| advance rotates a named wheel $n by consuming an item from an infinite sequence. It is called
#| from within a gather block and so can use take in order to construct an infinite, lazy sequence
#| of result values
sub advance($g, $n) {
given $g{$n}.pull-one {
when /\d/ { take $_ }
default { samewith $g, $_ } # samewith re-calls this function with new parameters
}
}

#| Input groups are a hash containing each wheel name as the key, and a list of values constructed
#| using <> to split on whitespace. They are transformed using xx * to repeat the list infinitely.
#| We then retrieve the underlying iterator in order for wheel position to be persistent. Each group
#| is then aggregated into a lazy output sequence using an infinite loop inside a gather block.
[
{A => <1 2 3>},
{A => <1 B 2>, B => <3 4>},
{A => <1 D D>, D => <6 7 8>},
{A => <1 B C>, B => <3 4>, C => <5 B>},
]
#| %() converts a list of pairs produced by map into a hash. $^k and $^v are implicit variables.
#| They are processed in alphabetical order and make the block arity 2, called with two vars.
#| .kv gets the list of wheel names and wheel values from the input entry
==> map({ %(.kv.map: { $^k => (|$^v xx *).iterator }) })
#| gather constructs a lazy sequence, in which we infinitely loop advancing wheel A
==> map({ gather { loop { advance $_, 'A' }} })
#| state variables are only initialised once, and are kept between invocations.
==> map({ state $i = 1; say "Group {$i++}, First 20 values: $_[^20]" })
</syntaxhighlight>{{Output}}
<pre>
Group 1, First 20 values: 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2
Group 2, First 20 values: 1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3
Group 3, First 20 values: 1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6
Group 4, First 20 values: 1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4
</pre>


=={{header|REXX}}==
=={{header|REXX}}==
Line 1,083: Line 2,213:


This REXX program uses &nbsp; ''numbers'' &nbsp; (any form), &nbsp; not &nbsp; ''digits'' &nbsp; (for the values on the wheels).
This REXX program uses &nbsp; ''numbers'' &nbsp; (any form), &nbsp; not &nbsp; ''digits'' &nbsp; (for the values on the wheels).
<lang rexx>/*REXX program expresses numbers from intersecting number wheels (or wheel sets). */
<syntaxhighlight lang="rexx">/*REXX program expresses numbers from intersecting number wheels (or wheel sets). */
@.= /*initialize array to hold the wheels. */
@.= /*initialize array to hold the wheels. */
parse arg lim @.1 /*obtain optional arguments from the CL*/
parse arg lim @.1 /*obtain optional arguments from the CL*/
Line 1,092: Line 2,222:
@.4= ' A: 1 B C, B: 3 4, C: 5 B '
@.4= ' A: 1 B C, B: 3 4, C: 5 B '
end
end
do i=1 while @.i\=''; call build /*construct the wheel set (gear sets).*/
do i=1 while @.i\=''; call run /*construct wheel set and "execute" it.*/
call run /*execute " " " " " */
end /*i*/
end /*i*/
exit /*stick a fork in it, we're all done. */
exit 0 /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
error: say; say; say '***error***' arg(1); say; say; exit 12
error: procedure; say; say; say '***error***' arg(1); say; say; exit 12
isLet: return datatype( arg(1), 'M') & length( arg(1) )==1
isLet: procedure; parse arg y; return datatype(y, 'M') & length(y)==1 /*is a letter? */
isNum: return datatype( arg(1), 'N')
isNum: procedure; parse arg y; return datatype(y, 'N') /*is a number? */
/*──────────────────────────────────────────────────────────────────────────────────────*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
build: @wn= 'wheel name'; first=; @wnnfbac= 'wheel name not followed by a colon:'
run: @wn= 'wheel name'; first=; @noColon= "wheel name not followed by a colon:"
@gn= 'gear name' ; gear.=; say copies('', 79)
@gn= 'gear name' ; gear.=; say copies("", 79)
say 'building wheel group for: ' @.i; wheels= space(@.i); upper wheels
say 'building wheel group for: ' @.i; wheels= space(@.i); upper wheels
do y=1 while wheels\=''; parse var wheels w gears ',' wheels; L= length(w)
do #=1 while wheels\=''; parse var wheels w gears "," wheels; L= length(w)
if L==2 then do; !.y= left(w, 1) /*obtain the 1-char gear name.*/
if L==2 then do; !.#= left(w, 1) /*obtain the one─character gear name. */
if right(w, 1)\==':' then call error @wnnfbac w
if right(w, 1)\==':' then call error @noColon w
if \isLet(!.y) then call error @wn "not a letter:" w
if \isLet(!.#) then call error @wn "not a letter:" w
end
end
else call error "first token isn't a" @wn':' w
else call error "first token isn't a" @wn':' w
if y==1 then first= !.1 /*Is this is the 1st wheel set? Use it*/
if #==1 then first= !.1 /*Is this is the 1st wheel set? Use it*/
if first=='' then call error "no wheel name was specified."
if first=='' then call error "no wheel name was specified."
n= !.y /*obtain the name of the 1st wheel set.*/
n= !.# /*obtain the name of the 1st wheel set.*/
gear.n.0= 1 /*initialize default 1st gear position.*/
gear.n.0= 1 /*initialize default 1st gear position.*/
say ' setting gear.name:' n ' gears=' gears
say ' setting gear.name:' n " gears=" gears
do g=1 for words(gears); _= word(gears, g)
do g=1 for words(gears); _= word(gears, g)
if isNum(_) | isLet(_) then do; gear.n.g= _; iterate; end
if isNum(_) | isLet(_) then do; gear.n.g= _; iterate; end
call error @gn "isn't a number or a gear name:" _
call error @gn "isn't a number or a gear name:" _
end /*g*/
end /*g*/
end /*y*/; return
end /*#*/
say; say center(' running the wheel named ' first" ", 79, '─'); $=
/*──────────────────────────────────────────────────────────────────────────────────────*/
run: say; say center(' running the wheel named ' first" ", 79, "─"); $=
do dummy=0 by 0 until words($)==lim; n= first
do #=0 by 0 until words($)==lim; n= first
z= gear.n.0; x= gear.n.z; z= z + 1
z= gear.n.0; x= gear.n.z; z= z + 1
gear.n.0= z; if gear.n.z=='' then gear.n.0= 1
gear.n.0= z; if gear.n.z=='' then gear.n.0= 1
if isNum(x) then do; $= $ x; iterate; end /*found a number, use it.*/
if isNum(x) then do; $= $ x; iterate; end /*found a number, use it.*/
xx= x /*different gear, keep switching until #.*/
xx= x /*different gear, keep switching 'til X*/
do forever; nn= xx
do forever; nn= xx
if gear.nn.0=='' then call error "a gear is using an unknown gear name:" x
if gear.nn.0=='' then call error "a gear is using an unknown gear name:" x
zz= gear.nn.0; xx= gear.nn.zz
zz= gear.nn.0; xx= gear.nn.zz
zz= zz + 1; gear.nn.0= zz; if gear.nn.zz=='' then gear.nn.0= 1
zz= zz + 1; gear.nn.0= zz; if gear.nn.zz=='' then gear.nn.0= 1
if isNum(xx) then do; $= $ xx; iterate #; end
if isNum(xx) then do; $= $ xx; iterate dummy; end
end /* [↑] found a number, now use FIRST. */
end /*forever*/ /* [↑] found a number, now use FIRST.*/
end /*dummy*/ /*"DUMMY" is needed for the ITERATE. */
end /*until*/
say '('lim "results): " strip($); say; say; return</lang>
say '('lim "results): " strip($); say; say; return</syntaxhighlight>
{{out|output|text=&nbsp; when using the default inputs:}}
{{out|output|text=&nbsp; when using the default inputs:}}
<pre>
<pre>
Line 1,171: Line 2,299:
───────────────────────── running the wheel named A ──────────────────────────
───────────────────────── running the wheel named A ──────────────────────────
(20 results): 1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4
(20 results): 1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4
</pre>

=={{header|Ruby}}==
<syntaxhighlight lang="ruby">groups = [{A: [1, 2, 3]},
{A: [1, :B, 2], B: [3, 4]},
{A: [1, :D, :D], D: [6, 7, 8]},
{A: [1, :B, :C], B: [3, 4], C: [5, :B]} ]

groups.each do |group|
p group
wheels = group.transform_values(&:cycle)
res = 20.times.map do
el = wheels[:A].next
el = wheels[el].next until el.is_a?(Integer)
el
end
puts res.join(" "),""
end
</syntaxhighlight>
{{out}}
<pre>{:A=>[1, 2, 3]}
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2

{:A=>[1, :B, 2], :B=>[3, 4]}
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3

{:A=>[1, :D, :D], :D=>[6, 7, 8]}
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6

{:A=>[1, :B, :C], :B=>[3, 4], :C=>[5, :B]}
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4
</pre>
=={{header|Visual Basic .NET}}==
{{trans|C#}}
<syntaxhighlight lang="vbnet">Imports System.Runtime.CompilerServices

Module Module1

<Extension()>
Iterator Function Loopy(Of T)(seq As IEnumerable(Of T)) As IEnumerable(Of T)
While True
For Each element In seq
Yield element
Next
End While
End Function

Iterator Function TurnWheels(ParamArray wheels As (name As Char, values As String)()) As IEnumerable(Of Char)
Dim data = wheels.ToDictionary(Function(wheel) wheel.name, Function(wheel) wheel.values.Loopy.GetEnumerator)
Dim primary = data(wheels(0).name)

Dim Turn As Func(Of IEnumerator(Of Char), Char) = Function(sequence As IEnumerator(Of Char))
sequence.MoveNext()
Dim c = sequence.Current
Return If(Char.IsDigit(c), c, Turn(data(c)))
End Function

While True
Yield Turn(primary)
End While
End Function

<Extension()>
Sub Print(sequence As IEnumerable(Of Char))
Console.WriteLine(String.Join(" ", sequence))
End Sub

Sub Main()
TurnWheels(("A", "123")).Take(20).Print()
TurnWheels(("A", "1B2"), ("B", "34")).Take(20).Print()
TurnWheels(("A", "1DD"), ("D", "678")).Take(20).Print()
TurnWheels(("A", "1BC"), ("B", "34"), ("C", "5B")).Take(20).Print()
End Sub

End Module</syntaxhighlight>
{{out}}
<pre>1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4</pre>

=={{header|Wren}}==
{{trans|Go}}
{{libheader|Wren-dynamic}}
{{libheader|Wren-sort}}
{{libheader|Wren-fmt}}
<syntaxhighlight lang="wren">import "./dynamic" for Struct
import "./sort" for Sort
import "./fmt" for Fmt

var Wheel = Struct.create("Wheel", ["next", "values"])

var generate = Fn.new { |wheels, start, maxCount|
var count = 0
var w = wheels[start]
while (true) {
var s = w.values[w.next]
var v = Num.fromString(s)
w.next = (w.next + 1) % w.values.count
wheels[start] = w
if (v) {
System.write("%(v) ")
count = count + 1
if (count == maxCount) {
System.print("...\n")
return
}
} else {
while (true) {
var w2 = wheels[s]
var ss = s
s = w2.values[w2.next]
w2.next = (w2.next + 1) % w2.values.count
wheels[ss] = w2
v = Num.fromString(s)
if (v) {
System.write("%(v) ")
count = count + 1
if (count == maxCount) {
System.print("...\n")
return
}
break
}
}
}
}
}

var printWheels = Fn.new { |wheels|
var names = []
for (name in wheels.keys) names.add(name)
Sort.quick(names)
System.print("Intersecting Number Wheel group:")
for (name in names) {
Fmt.print(" $s: $n", name, wheels[name].values)
}
System.write(" Generates:\n ")
}

var wheelMaps = [
{
"A": Wheel.new(0, ["1", "2", "3"])
},
{
"A": Wheel.new(0, ["1", "B", "2"]),
"B": Wheel.new(0, ["3", "4"])
},
{
"A": Wheel.new(0, ["1", "D", "D"]),
"D": Wheel.new(0, ["6", "7", "8"])
},
{
"A": Wheel.new(0, ["1", "B", "C"]),
"B": Wheel.new(0, ["3", "4"]),
"C": Wheel.new(0, ["5", "B"])
}
]
for (wheels in wheelMaps) {
printWheels.call(wheels)
generate.call(wheels, "A", 20)
}</syntaxhighlight>

{{out}}
<pre>
Intersecting Number Wheel group:
A: [1, 2, 3]
Generates:
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 ...

Intersecting Number Wheel group:
A: [1, B, 2]
B: [3, 4]
Generates:
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3 ...

Intersecting Number Wheel group:
A: [1, D, D]
D: [6, 7, 8]
Generates:
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6 ...

Intersecting Number Wheel group:
A: [1, B, C]
B: [3, 4]
C: [5, B]
Generates:
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4 ...
</pre>
</pre>


=={{header|zkl}}==
=={{header|zkl}}==
<lang zkl>fcn intersectingNumberWheelsW(wheels){ // ("A":(a,b,"C"), "C":(d,e) ...)
<syntaxhighlight lang="zkl">fcn intersectingNumberWheelsW(wheels){ // ("A":(a,b,"C"), "C":(d,e) ...)
ws:=wheels.pump(Dictionary(),fcn([(k,v)]){ return(k,Walker.cycle(v)) }); // new Dictionary
ws:=wheels.pump(Dictionary(),fcn([(k,v)]){ return(k,Walker.cycle(v)) }); // new Dictionary
Walker.zero().tweak(fcn(w,wheels){
Walker.zero().tweak(fcn(w,wheels){
Line 1,182: Line 2,498:
}
}
}.fp("A",ws)) // assume wheel A exists and is always first
}.fp("A",ws)) // assume wheel A exists and is always first
}</lang>
}</syntaxhighlight>
<lang zkl>wheelSets:=T( Dictionary("A",T(1,2,3)),
<syntaxhighlight lang="zkl">wheelSets:=T( Dictionary("A",T(1,2,3)),
Dictionary("A",T(1,"B",2), "B",T(3,4)),
Dictionary("A",T(1,"B",2), "B",T(3,4)),
Dictionary("A",T(1,"D","D"), "D",T(6,7,8)),
Dictionary("A",T(1,"D","D"), "D",T(6,7,8)),
Line 1,191: Line 2,507:
ws.pump(String,fcn([(k,v)]){ " %s: %s\n".fmt(k,v.concat(" ")) }).print();
ws.pump(String,fcn([(k,v)]){ " %s: %s\n".fmt(k,v.concat(" ")) }).print();
println("-->",intersectingNumberWheelsW(ws).walk(20).concat(" "));
println("-->",intersectingNumberWheelsW(ws).walk(20).concat(" "));
}</lang>
}</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>

Latest revision as of 06:17, 7 January 2024

Task
Intersecting number wheels
You are encouraged to solve this task according to the task description, using any language you may know.

A number wheel has:

  • A name which is an uppercase letter.
  • A set of ordered values which are either numbers or names.


A number is generated/yielded from a named wheel by:

1. Starting at the first value of the named wheel and advancing through subsequent values and wrapping around to the first value to form a "wheel":
1.a If the value is a number, yield it.
1.b If the value is a name, yield the next value from the named wheel
1.c Advance the position of this wheel.

Given the wheel

A: 1 2 3

the number 1 is first generated, then 2, then 3, 1, 2, 3, 1, ...

Note: When more than one wheel is defined as a set of intersecting wheels then the first named wheel is assumed to be the one that values are generated from.

Examples

Given the wheels:

   A: 1 B 2
   B: 3 4

The series of numbers generated starts:

   1, 3, 2, 1, 4, 2, 1, 3, 2, 1, 4, 2, 1, 3, 2...

The intersections of number wheels can be more complex, (and might loop forever), and wheels may be multiply connected.

Note: If a named wheel is referenced more than once by one or many other wheels, then there is only one position of the wheel that is advanced by each and all references to it.

E.g.

 A:  1 D D
 D:  6 7 8
 Generates:
   1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6 ...    
Task

Generate and show the first twenty terms of the sequence of numbers generated from these groups:

   Intersecting Number Wheel group:
     A:  1 2 3
   
   Intersecting Number Wheel group:
     A:  1 B 2
     B:  3 4
   
   Intersecting Number Wheel group:
     A:  1 D D
     D:  6 7 8
   
   Intersecting Number Wheel group:
     A:  1 B C
     B:  3 4
     C:  5 B

Show your output here, on this page.


11l

Translation of: Python
F nextfrom(&w, =name)
   L
      V nxt = w[name][0]
      w[name] = w[name][1..] + w[name][0.<1]
      I nxt[0] C ‘0’..‘9’
         R nxt
      name = nxt

L(group) |‘A: 1 2 3
           A: 1 B 2; B: 3 4
           A: 1 D D; D: 6 7 8
           A: 1 B C; B: 3 4; C: 5 B’.split("\n")
   print("Intersecting Number Wheel group:\n  "group)
   [String = [String]] wheel
   V first = ‘’
   L(w) group.split(‘;’)
      V s = w.trim(‘ ’).split(‘ ’)
      V name = s[0]
      wheel[name[0 .< (len)-1]] = s[1..]
      first = I first == ‘’ {name[0 .< (len)-1]} E first
   V gen = (0.<20).map(i -> nextfrom(&@wheel, @first)).join(‘ ’)
   print("  Generates:\n    "gen" ...\n")
Output:
Intersecting Number Wheel group:
  A: 1 2 3
  Generates:
    1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 ...

Intersecting Number Wheel group:
  A: 1 B 2; B: 3 4
  Generates:
    1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3 ...

Intersecting Number Wheel group:
  A: 1 D D; D: 6 7 8
  Generates:
    1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6 ...

Intersecting Number Wheel group:
  A: 1 B C; B: 3 4; C: 5 B
  Generates:
    1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4 ...

ALGOL 68

BEGIN
    # a number wheel element                                                  #
    MODE NWELEMENT = UNION( CHAR # wheel name #, INT # wheel value # );
    # a number wheel                                                          #
    MODE NW = STRUCT( CHAR name, REF INT position, FLEX[ 1 : 0 ]NWELEMENT values );
    # get the next value from a number wheel in an array of number wheels     #
    # note: invalid wheel names will cause subscript range errors             #
    OP   NEXT = ( []NW wheels )INT:
         BEGIN
            INT  result;
            BOOL found := FALSE;
            INT  w     := LWB wheels; # start with the first wheel            #
            WHILE NOT found DO
                IF position OF wheels[ w ] > UPB values OF wheels[ w ] THEN
                    # passed the end of the wheel, go back to the start       #
                    position OF wheels[ w ] := LWB values OF wheels[ w ]
                FI;
                NWELEMENT e = ( values OF wheels[ w ] )[ position OF wheels[ w ] ];
                position OF wheels[ w ] +:= 1;
                CASE e
                  IN ( INT  n ): BEGIN result := n; found := TRUE END
                   , ( CHAR c ): BEGIN
                                     w := LWB wheels;
                                     WHILE name OF wheels[ w ] /= c DO w +:= 1 OD
                                 END
                ESAC
            OD;
            result
         END # NEXT # ;
    # prints the first n values from an array of wheels                       #
    PROC show = ( INT n, []NW wheels )VOID:
         BEGIN
            print( ( "First ", whole( n, 0 ), " values from the Intersecting Number Wheels:" ) );
            FOR i FROM LWB wheels TO UPB wheels DO
                print( ( newline, "    ", name OF wheels[ i ], ":" ) );
                FOR v FROM LWB values OF wheels[ i ] TO UPB values OF wheels[ i ] DO
                    CASE ( values OF wheels[ i ] )[ v ]
                      IN ( INT  n ): print( ( " ", whole( n, 0 ) ) )
                       , ( CHAR c ): print( ( " ", c ) )
                    ESAC
                OD
            OD;
            print( ( newline, "        " ) );
            FOR i TO n DO print( ( " ", whole( NEXT wheels, 0 ) ) ) OD;
            print( ( newline, newline ) )
         END # show # ;
    # show some wheels in action                                              #
    show( 20, ( NW( "A", LOC INT := 1, (  1,   2,   3  ) ) ) );
    show( 20, ( NW( "A", LOC INT := 1, (  1,  "B",  2  ) )
              , NW( "B", LOC INT := 1, (  3,   4       ) ) ) );
    show( 20, ( NW( "A", LOC INT := 1, (  1,  "D", "D" ) )
              , NW( "D", LOC INT := 1, (  6,   7,   8  ) ) ) );
    show( 20, ( NW( "A", LOC INT := 1, (  1,  "B", "C" ) )
              , NW( "B", LOC INT := 1, (  3,   4       ) )
              , NW( "C", LOC INT := 1, (  5,  "B"      ) ) ) )
END
Output:
First 20 values from the Intersecting Number Wheels:
    A: 1 2 3
         1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2

First 20 values from the Intersecting Number Wheels:
    A: 1 B 2
    B: 3 4
         1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3

First 20 values from the Intersecting Number Wheels:
    A: 1 D D
    D: 6 7 8
         1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6

First 20 values from the Intersecting Number Wheels:
    A: 1 B C
    B: 3 4
    C: 5 B
         1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4

AutoHotkey

obj1 := {"A":[1, 2, 3]}
obj2 := {"A":[1, "B", 2]	, "B":[3, 4]}
obj3 := {"A":[1, "D", "D"]	, "D":[6, 7, 8]}
obj4 := {"A":[1, "B", "C"]	, "B":[3, 4]	, "C":[5, "B"]}

loop 4
{
	str := ""
	for k, v in obj%A_Index% {
		str .= "{" k " : " 
		for i, t in v
			str .= t ","
		str := Trim(str, ",") "}, "
	}
	str := Trim(str, ", ")
	x := INW(obj%A_Index%)
	result .= str "`n" x.1 "`n" x.2 "`n------`n"
}
MsgBox % result
return

INW(obj, num:=20){
	sets := [], ptr := []
	for k, v in obj {
		if A_Index=1
			s := k, s1 := k
		%k% := v, sets.Push(k), ptr[k] := 0
	}
	loop % num {
		ptr[s]++				
		while !((v := %s%[ptr[s]]) ~= "\d") {
			s := %s%[ptr[s]]
			ptr[s]++
		}
		key .= s "." ptr[s] ", "
		result .= %s%[ptr[s]] "    "
		s := s1
		for i, set in sets
			ptr[set] := ptr[set] = %set%.count() ? 0 : ptr[set]
	}
	return [key, result]
}
Output:
{A : 1,2,3}
A.1, A.2, A.3, A.1, A.2, A.3, A.1, A.2, A.3, A.1, A.2, A.3, A.1, A.2, A.3, A.1, A.2, A.3, A.1, A.2, 
1    2    3    1    2    3    1    2    3    1    2    3    1    2    3    1    2    3    1    2    
------
{A : 1,B,2}, {B : 3,4}
A.1, B.1, A.3, A.1, B.2, A.3, A.1, B.1, A.3, A.1, B.2, A.3, A.1, B.1, A.3, A.1, B.2, A.3, A.1, B.1, 
1    3    2    1    4    2    1    3    2    1    4    2    1    3    2    1    4    2    1    3    
------
{A : 1,D,D}, {D : 6,7,8}
A.1, D.1, D.2, A.1, D.3, D.1, A.1, D.2, D.3, A.1, D.1, D.2, A.1, D.3, D.1, A.1, D.2, D.3, A.1, D.1, 
1    6    7    1    8    6    1    7    8    1    6    7    1    8    6    1    7    8    1    6    
------
{A : 1,B,C}, {B : 3,4}, {C : 5,B}
A.1, B.1, C.1, A.1, B.2, B.1, A.1, B.2, C.1, A.1, B.1, B.2, A.1, B.1, C.1, A.1, B.2, B.1, A.1, B.2, 
1    3    5    1    4    3    1    4    5    1    3    4    1    3    5    1    4    3    1    4    
------

C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Wheel {
    char *seq;
    int len;
    int pos;
};

struct Wheel *create(char *seq) {
    struct Wheel *w = malloc(sizeof(struct Wheel));
    if (w == NULL) {
        return NULL;
    }

    w->seq = seq;
    w->len = strlen(seq);
    w->pos = 0;

    return w;
}

char cycle(struct Wheel *w) {
    char c = w->seq[w->pos];
    w->pos = (w->pos + 1) % w->len;
    return c;
}

struct Map {
    struct Wheel *v;
    struct Map *next;
    char k;
};

struct Map *insert(char k, struct Wheel *v, struct Map *head) {
    struct Map *m = malloc(sizeof(struct Map));
    if (m == NULL) {
        return NULL;
    }

    m->k = k;
    m->v = v;
    m->next = head;

    return m;
}

struct Wheel *find(char k, struct Map *m) {
    struct Map *ptr = m;

    while (ptr != NULL) {
        if (ptr->k == k) {
            return ptr->v;
        }
        ptr = ptr->next;
    }

    return NULL;
}

void printOne(char k, struct Map *m) {
    struct Wheel *w = find(k, m);
    char c;

    if (w == NULL) {
        printf("Missing the wheel for: %c\n", k);
        exit(1);
    }

    c = cycle(w);
    if ('0' <= c && c <= '9') {
        printf(" %c", c);
    } else {
        printOne(c, m);
    }
}

void exec(char start, struct Map *m) {
    struct Wheel *w;
    int i;

    if (m == NULL) {
        printf("Unable to proceed.");
        return;
    }

    for (i = 0; i < 20; i++) {
        printOne(start, m);
    }
    printf("\n");
}

void group1() {
    struct Wheel *a = create("123");

    struct Map *m = insert('A', a, NULL);

    exec('A', m);
}

void group2() {
    struct Wheel *a = create("1B2");
    struct Wheel *b = create("34");

    struct Map *m = insert('A', a, NULL);
    m = insert('B', b, m);

    exec('A', m);
}

void group3() {
    struct Wheel *a = create("1DD");
    struct Wheel *d = create("678");

    struct Map *m = insert('A', a, NULL);
    m = insert('D', d, m);

    exec('A', m);
}

void group4() {
    struct Wheel *a = create("1BC");
    struct Wheel *b = create("34");
    struct Wheel *c = create("5B");

    struct Map *m = insert('A', a, NULL);
    m = insert('B', b, m);
    m = insert('C', c, m);

    exec('A', m);
}

int main() {
    group1();
    group2();
    group3();
    group4();

    return 0;
}
Output:
 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2
 1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3
 1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6
 1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4

C#

using System;
using System.Collections.Generic;
using System.Linq;

public static class IntersectingNumberWheels
{
    public static void Main() {
        TurnWheels(('A', "123")).Take(20).Print();
        TurnWheels(('A', "1B2"), ('B', "34")).Take(20).Print();
        TurnWheels(('A', "1DD"), ('D', "678")).Take(20).Print();
        TurnWheels(('A', "1BC"), ('B', "34"), ('C', "5B")).Take(20).Print();
    }

    static IEnumerable<char> TurnWheels(params (char name, string values)[] wheels) {
        var data = wheels.ToDictionary(wheel => wheel.name, wheel => wheel.values.Loop().GetEnumerator());
        var primary = data[wheels[0].name];
        while (true) {
            yield return Turn(primary);
        }

        char Turn(IEnumerator<char> sequence) {
            sequence.MoveNext();
            char c = sequence.Current;
            return char.IsDigit(c) ? c : Turn(data[c]);
        }
    }

    static IEnumerable<T> Loop<T>(this IEnumerable<T> seq) {
        while (true) {
            foreach (T element in seq) yield return element;
        }
    }

    static void Print(this IEnumerable<char> sequence) => Console.WriteLine(string.Join(" ", sequence));
}
Output:
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4

C++

Translation of: D
#include <initializer_list>
#include <iostream>
#include <map>
#include <vector>

struct Wheel {
private:
    std::vector<char> values;
    size_t index;

public:
    Wheel() : index(0) {
        // empty
    }

    Wheel(std::initializer_list<char> data) : values(data), index(0) {
        //values.assign(data);
        if (values.size() < 1) {
            throw new std::runtime_error("Not enough elements");
        }
    }

    char front() {
        return values[index];
    }

    void popFront() {
        index = (index + 1) % values.size();
    }
};

struct NamedWheel {
private:
    std::map<char, Wheel> wheels;

public:
    void put(char c, Wheel w) {
        wheels[c] = w;
    }

    char front(char c) {
        char v = wheels[c].front();
        while ('A' <= v && v <= 'Z') {
            v = wheels[v].front();
        }
        return v;
    }

    void popFront(char c) {
        auto v = wheels[c].front();
        wheels[c].popFront();

        while ('A' <= v && v <= 'Z') {
            auto d = wheels[v].front();
            wheels[v].popFront();
            v = d;
        }
    }
};

void group1() {
    Wheel w({ '1', '2', '3' });
    for (size_t i = 0; i < 20; i++) {
        std::cout << ' ' << w.front();
        w.popFront();
    }
    std::cout << '\n';
}

void group2() {
    Wheel a({ '1', 'B', '2' });
    Wheel b({ '3', '4' });

    NamedWheel n;
    n.put('A', a);
    n.put('B', b);

    for (size_t i = 0; i < 20; i++) {
        std::cout << ' ' << n.front('A');
        n.popFront('A');
    }
    std::cout << '\n';
}

void group3() {
    Wheel a({ '1', 'D', 'D' });
    Wheel d({ '6', '7', '8' });

    NamedWheel n;
    n.put('A', a);
    n.put('D', d);

    for (size_t i = 0; i < 20; i++) {
        std::cout << ' ' << n.front('A');
        n.popFront('A');
    }
    std::cout << '\n';
}

void group4() {
    Wheel a({ '1', 'B', 'C' });
    Wheel b({ '3', '4' });
    Wheel c({ '5', 'B' });

    NamedWheel n;
    n.put('A', a);
    n.put('B', b);
    n.put('C', c);

    for (size_t i = 0; i < 20; i++) {
        std::cout << ' ' << n.front('A');
        n.popFront('A');
    }
    std::cout << '\n';
}

int main() {
    group1();
    group2();
    group3();
    group4();

    return 0;
}
Output:
 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2
 1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3
 1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6
 1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4

D

import std.exception;
import std.range;
import std.stdio;

struct Wheel {
    private string[] values;
    private uint index;

    invariant {
        enforce(index < values.length, "index out of range");
    }

    this(string[] value...) in {
        enforce(value.length > 0, "Cannot create a wheel with no elements");
    } body {
        values = value;
    }

    enum empty = false;

    auto front() {
        return values[index];
    }

    void popFront() {
        index = (index + 1) % values.length;
    }
}

struct NamedWheel {
    private Wheel[char] wheels;
    char m;

    this(char c, Wheel w) {
        add(c, w);
        m = c;
    }

    void add(char c, Wheel w) {
        wheels[c] = w;
    }

    enum empty = false;

    auto front() {
        auto v = wheels[m].front;
        char c = v[0];
        while ('A' <= c && c <= 'Z') {
            v = wheels[c].front;
            c = v[0];
        }
        return v;
    }

    void popFront() {
        auto v = wheels[m].front;
        wheels[m].popFront;

        char c = v[0];
        while ('A' <= c && c <= 'Z') {
            auto d = wheels[c].front;
            wheels[c].popFront;
            c = d[0];
        }
    }
}

void group1() {
    auto a = Wheel("1", "2", "3");
    a.take(20).writeln;
}

void group2() {
    auto a = Wheel("1", "B", "2");
    auto b = Wheel("3", "4");

    auto n = NamedWheel('A', a);
    n.add('B', b);

    n.take(20).writeln;
}

void group3() {
    auto a = Wheel("1", "D", "D");
    auto d = Wheel("6", "7", "8");

    auto n = NamedWheel('A', a);
    n.add('D', d);

    n.take(20).writeln;
}

void group4() {
    auto a = Wheel("1", "B", "C");
    auto b = Wheel("3", "4");
    auto c = Wheel("5", "B");

    auto n = NamedWheel('A', a);
    n.add('B', b);
    n.add('C', c);

    n.take(20).writeln;
}

void main() {
    group1();
    group2();
    group3();
    group4();
}
Output:
["1", "2", "3", "1", "2", "3", "1", "2", "3", "1", "2", "3", "1", "2", "3", "1", "2", "3", "1", "2"]
["1", "3", "2", "1", "4", "2", "1", "3", "2", "1", "4", "2", "1", "3", "2", "1", "4", "2", "1", "3"]
["1", "6", "7", "1", "8", "6", "1", "7", "8", "1", "6", "7", "1", "8", "6", "1", "7", "8", "1", "6"]
["1", "3", "5", "1", "4", "3", "1", "4", "5", "1", "3", "4", "1", "3", "5", "1", "4", "3", "1", "4"]

F#

// Wheels within wheels. Nigel Galloway: September 30th., 2019. 
let N(n)=fun()->n
let wheel(n:(unit->int)[])=let mutable g= -1 in (fun()->g<-(g+1)%n.Length; n.[g]())
let A1=wheel[|N(1);N(2);N(3)|]
for n in 0..20 do printf "%d " (A1())
printfn ""
let B2=wheel[|N(3);N(4)|]
let A2=wheel[|N(1);B2;N(2)|]
for n in 0..20 do printf "%d " (A2())
printfn ""
let D3=wheel[|N(6);N(7);N(8)|]
let A3=wheel[|N(1);D3;D3|]
for n in 0..20 do printf "%d " (A3())
printfn ""
let B4=wheel[|N(3);N(4)|]
let C4=wheel[|N(5);B4|]
let A4=wheel[|N(1);B4;C4|]
for n in 0..20 do printf "%d " (A4())
printfn ""
Output:
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3 2
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6 7
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4 5

Factor

An attempt has been made to simplify the interface as much as possible by creating a natural literal syntax for number wheel groups. This should be useful for exploring these types of sequences in the future. nw-parser is an EBNF grammar that turns

"A: 1 B C\nB: 3 4\nC: 5 B"

into

{
    { "A" T{ number-wheel { seq T{ circular { seq { 1 "B" "C" } } } } { i 0 } } }
    { "B" T{ number-wheel { seq T{ circular { seq { 3 4 } } } } { i 0 } } }
    { "C" T{ number-wheel { seq T{ circular { seq { 5 "B" } } } } { i 0 } } }
}

⁠— a dictionary-like structure that is transformed into a lazy list which yields the expected sequence elements.

Works with: Factor version 0.99 2019-07-10
USING: accessors assocs circular io kernel lists lists.lazy math
math.parser multiline peg.ebnf prettyprint prettyprint.custom
sequences strings ;
IN: rosetta-code.number-wheels

TUPLE: group pretty list ;

C: <group> group

M: group pprint* pretty>> write ;

TUPLE: number-wheel seq i ;

: <number-wheel> ( seq -- number-wheel )
    <circular> 0 number-wheel boa ;

: yield ( assoc -- n )
    dup first first [ dup integer? ]
    [ dupd of [ i>> ] [ [ 1 + ] change-i seq>> nth ] bi ] until
    nip ;

: number-wheel>lazy ( assoc -- list )
    0 lfrom swap [ yield nip ] curry lmap-lazy ;

EBNF: nw-parser [=[
    num   = [0-9]+ => [[ >string string>number ]]
    name  = [a-zA-Z]+ => [[ >string ]]
    wheel = (" "~ (num | name))+ "\n"?
          => [[ but-last first <number-wheel> ]]
    group = (name ":"~ wheel)+ => [[ number-wheel>lazy ]]
]=]

SYNTAX: NUMBER-WHEELS: parse-here dup nw-parser <group> suffix! ;

: .take ( n group -- )
    list>> ltake list>array [ pprint bl ] each "..." print ;

Now the interface defined above may be used:

USING: generalizations io kernel prettyprint
rosetta-code.number-wheels ;

NUMBER-WHEELS:
A: 1 2 3
;

NUMBER-WHEELS:
A: 1 B 2
B: 3 4
;

NUMBER-WHEELS:
A: 1 D D
D: 6 7 8
;

NUMBER-WHEELS:
A: 1 B C
B: 3 4
C: 5 B
;

[ 
    "Intersecting number wheel group:" print
    [ . ] [ "Generates:" print 20 swap .take nl ] bi
] 4 napply
Output:
Intersecting number wheel group:
A: 1 2 3
Generates:
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 ...

Intersecting number wheel group:
A: 1 B 2
B: 3 4
Generates:
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3 ...

Intersecting number wheel group:
A: 1 D D
D: 6 7 8
Generates:
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6 ...

Intersecting number wheel group:
A: 1 B C
B: 3 4
C: 5 B
Generates:
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4 ...

Go

package main

import (
    "fmt"
    "sort"
    "strconv"
)

type wheel struct {
    next   int
    values []string
}

type wheelMap = map[string]wheel

func generate(wheels wheelMap, start string, maxCount int) {
    count := 0
    w := wheels[start]
    for {
        s := w.values[w.next]
        v, err := strconv.Atoi(s)
        w.next = (w.next + 1) % len(w.values)
        wheels[start] = w
        if err == nil {
            fmt.Printf("%d ", v)
            count++
            if count == maxCount {
                fmt.Println("...\n")
                return
            }
        } else {
            for {
                w2 := wheels[s]
                ss := s
                s = w2.values[w2.next]
                w2.next = (w2.next + 1) % len(w2.values)
                wheels[ss] = w2
                v, err = strconv.Atoi(s)
                if err == nil {
                    fmt.Printf("%d ", v)
                    count++
                    if count == maxCount {
                        fmt.Println("...\n")
                        return
                    }
                    break
                }
            }
        }
    }
}

func printWheels(wheels wheelMap) {
    var names []string
    for name := range wheels {
        names = append(names, name)
    }
    sort.Strings(names)
    fmt.Println("Intersecting Number Wheel group:")
    for _, name := range names {
        fmt.Printf("  %s: %v\n", name, wheels[name].values)
    }
    fmt.Print("  Generates:\n    ")
}

func main() {
    wheelMaps := []wheelMap{
        {
            "A": {0, []string{"1", "2", "3"}},
        },
        {
            "A": {0, []string{"1", "B", "2"}},
            "B": {0, []string{"3", "4"}},
        },
        {
            "A": {0, []string{"1", "D", "D"}},
            "D": {0, []string{"6", "7", "8"}},
        },
        {
            "A": {0, []string{"1", "B", "C"}},
            "B": {0, []string{"3", "4"}},
            "C": {0, []string{"5", "B"}},
        },
    }
    for _, wheels := range wheelMaps {
        printWheels(wheels)
        generate(wheels, "A", 20)
    }
}
Output:
Intersecting Number Wheel group:
  A: [1 2 3]
  Generates:
    1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 ...

Intersecting Number Wheel group:
  A: [1 B 2]
  B: [3 4]
  Generates:
    1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3 ...

Intersecting Number Wheel group:
  A: [1 D D]
  D: [6 7 8]
  Generates:
    1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6 ...

Intersecting Number Wheel group:
  A: [1 B C]
  B: [3 4]
  C: [5 B]
  Generates:
    1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4 ...

Haskell

Defining a unit movement of the interlocking wheels as a recursive descent, terminating at the first digit found, and printing a map-accumulation of that recursion over a list of given length but arbitrary content.

import Data.Char (isDigit)
import Data.List (mapAccumL)
import qualified Data.Map.Strict as M
import Data.Maybe (fromMaybe)

---------------- INTERSECTING NUMBER WHEELS --------------

clockWorkTick ::
  M.Map Char String ->
  (M.Map Char String, Char)
clockWorkTick = flip click 'A'
  where
    click wheels name
      | isDigit name = (wheels, name)
      | otherwise =
        ( click
            . flip
              (M.insert name . leftRotate)
              wheels
            <*> head
        )
          $ fromMaybe ['?'] $ M.lookup name wheels

leftRotate :: [a] -> [a]
leftRotate = take . length <*> (tail . cycle)

--------------------------- TEST -------------------------
main :: IO ()
main = do
  let wheelSets =
        [ [('A', "123")],
          [('A', "1B2"), ('B', "34")],
          [('A', "1DD"), ('D', "678")],
          [('A', "1BC"), ('B', "34"), ('C', "5B")]
        ]
  putStrLn "State of each wheel-set after 20 clicks:\n"
  mapM_ print $
    fmap
      ( flip
          (mapAccumL (const . clockWorkTick))
          (replicate 20 undefined)
          . M.fromList
      )
      wheelSets
  putStrLn "\nInitial state of the wheel-sets:\n"
  mapM_ print wheelSets
Output:
State of each wheel-set after 20 clicks:

(fromList [('A',"312")],"12312312312312312312")
(fromList [('A',"21B"),('B',"43")],"13214213214213214213")
(fromList [('A',"D1D"),('D',"786")],"16718617816718617816")
(fromList [('A',"C1B"),('B',"34"),('C',"5B")],"13514314513413514314")

Initial state of the wheel-sets:

[('A',"123")]
[('A',"1B2"),('B',"34")]
[('A',"1DD"),('D',"678")]
[('A',"1BC"),('B',"34"),('C',"5B")]

J

Implementation:

wheelgroup=:{{
  yield_wheelgroup_=: {{
    i=. wheels i.<;y
    j=. i{inds
    k=. ".;y
    l=. j{k
    inds=: ((#k)|1+j) i} inds
    if. l e. wheels
    do.yield l
    else.{.".;l
    end.
  }}
  gen_wheelgroup_=: {{
    yield wheel
  }}
  grp=. cocreate ''
  coinsert__grp 'wheelgroup'
  specs__grp=: cut each boxopen m
  wheel__grp=: ;{.wheels__grp=: {.every specs__grp
  init__grp=: {{('inds';wheels)=:(0#~#specs);}.each specs}}
  init__grp''
  ('gen_',(;grp),'_')~
}}

Task examples:

   task=: {{y wheelgroup^:(1+i.20)_}}
   task 'A 1 2 3'
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2
   task 'A 1 B 2';'B 3 4'
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3
   task 'A 1 D D';'D 6 7 8'
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6
   task 'A 1 B C';'B 3 4';'C 5 B'
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4

Java

package intersectingNumberWheels;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;

public class WheelController {
	private static final String IS_NUMBER = "[0-9]";
	private static final int TWENTY = 20;
	private static Map<String, WheelModel> wheelMap;

	public static void advance(String wheel) {
		WheelModel w = wheelMap.get(wheel);
		if (w.list.get(w.position).matches(IS_NUMBER)) {
			w.printThePosition();
			w.advanceThePosition();
		} else {
			String wheelName = w.list.get(w.position);
			advance(wheelName);
			w.advanceThePosition();
		}
	}

	public static void run() {
		System.out.println(wheelMap);
		IntStream.rangeClosed(1, TWENTY).forEach(i -> advance("A"));
		System.out.println();
		wheelMap.clear();
	}

	public static void main(String[] args) {
		wheelMap = new HashMap<>();
		wheelMap.put("A", new WheelModel("A", "1", "2", "3"));
		run();
		wheelMap.put("A", new WheelModel("A", "1", "B", "2"));
		wheelMap.put("B", new WheelModel("B", "3", "4"));
		run();
		wheelMap.put("A", new WheelModel("A", "1", "D", "D"));
		wheelMap.put("D", new WheelModel("D", "6", "7", "8"));
		run();
		wheelMap.put("A", new WheelModel("A", "1", "B", "C"));
		wheelMap.put("B", new WheelModel("B", "3", "4"));
		wheelMap.put("C", new WheelModel("C", "5", "B"));
		run();
	}

}

class WheelModel {
	String name;
	List<String> list;
	int position;
	int endPosition;
	private static final int INITIAL = 0;

	public WheelModel(String name, String... values) {
		super();

		this.name = name.toUpperCase();
		this.list = new ArrayList<>();
		for (String value : values) {
			list.add(value);
		}
		this.position = INITIAL;
		this.endPosition = this.list.size() - 1;
	}

	@Override
	public String toString() {
		return list.toString();
	}

	public void advanceThePosition() {
		if (this.position == this.endPosition) {
			this.position = INITIAL;// new beginning
		} else {
			this.position++;// advance position
		}
	}

	public void printThePosition() {
		System.out.print(" " + this.list.get(position));
	}
}

Output: {A=[1, 2, 3]}

1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2

{A=[1, B, 2], B=[3, 4]}

1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3

{A=[1, D, D], D=[6, 7, 8]}

1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6

{A=[1, B, C], B=[3, 4], C=[5, B]}

1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4

JavaScript

Map-accumulation of a recursive digit-search, over an array of given length and arbitrary contents.

Translation of: Haskell
Translation of: Python
(() => {
    'use strict';

    // main :: IO ()
    const main = () => {

        // clockWorkTick :: Dict -> (Dict, Char)
        const clockWorkTick = wheelMap => {
            // The new configuration of the wheels, tupled with
            // a digit found by recursive descent from a single
            // click of the first wheel.
            const click = wheels => wheelName => {
                const
                    wheel = wheels[wheelName] || ['?'],
                    v = wheel[0];
                return bool(click)(Tuple)(isDigit(v) || '?' === v)(
                    insertDict(wheelName)(
                        leftRotate(wheel)
                    )(wheels)
                )(v);
            };
            return click(wheelMap)('A');
        };

        // leftRotate ::[a] -> [a]
        const leftRotate = xs =>
            // The head of the list appended
            // to the tail of of the list.
            0 < xs.length ? (
                xs.slice(1).concat(xs[0])
            ) : [];


        // TEST -------------------------------------------
        // State of each wheel-set after 20 clicks,
        // paired with the resulting series of characters.

        const tuple = uncurry(Tuple);
        const wheelLists = [
            [tuple('A', '123')],
            [tuple('A', '1B2'), tuple('B', '34')],
            [tuple('A', '1DD'), tuple('D', '678')],
            [tuple('A', '1BC'), tuple('B', '34'), tuple('C', '5B')]
        ];

        console.log([
            'Series and state of each wheel-set after 20 clicks:\n',
            unlines(
                map(tuples => showWheels(
                    mapAccumL(
                        compose(constant, clockWorkTick)
                    )(dictFromList(tuples))(replicate(20)(''))
                ))(wheelLists)
            ),
            '\nInitial state of each wheel-set:\n',
            map(map(compose(
                JSON.stringify,
                dictFromList,
                x => [Array.from(x)]
            )))(wheelLists).join('\n')
        ].join('\n'));
    };

    // DISPLAY FORMATTING ---------------------------------

    // showWheels :: (Dict, [Char]) -> String
    const showWheels = tpl =>
        JSON.stringify(
            Array.from(secondArrow(concat)(tpl))
        );

    // GENERIC FUNCTIONS ----------------------------------

    // Tuple (,) :: a -> b -> (a, b)
    const Tuple = a => b => ({
        type: 'Tuple',
        '0': a,
        '1': b,
        length: 2
    });

    // bool :: a -> a -> Bool -> a
    const bool = f => t => p =>
        p ? t : f;

    // compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
    const compose = (...fs) =>
        x => fs.reduceRight((a, f) => f(a), x);

    // concat :: [[a]] -> [a]
    // concat :: [String] -> String
    const concat = xs =>
        0 < xs.length ? (() => {
            const unit = 'string' !== typeof xs[0] ? (
                []
            ) : '';
            return unit.concat.apply(unit, xs);
        })() : [];

    // constant :: a -> b -> a
    const constant = k => _ => k;

    // dictFromList :: [(k, v)] -> Dict
    const dictFromList = kvs =>
        Object.fromEntries(kvs);

    // secondArrow :: (a -> b) -> ((c, a) -> (c, b))
    const secondArrow = f => xy =>
        // A function over a simple value lifted
        // to a function over a tuple.
        // f (a, b) -> (a, f(b))
        Tuple(xy[0])(
            f(xy[1])
        );

    // insertDict :: String -> a -> Dict -> Dict
    const insertDict = k => v => dct =>
        Object.assign({}, dct, {
            [k]: v
        });

    // isDigit :: Char -> Bool
    const isDigit = c => {
        const n = c.codePointAt(0);
        return 48 <= n && 57 >= n;
    };

    // map :: (a -> b) -> [a] -> [b]
    const map = f => xs =>
        (Array.isArray(xs) ? (
            xs
        ) : xs.split('')).map(f);

    // Map-accumulation is a combination of map and a catamorphism;
    // it applies a function to each element of a list, passing an
    // accumulating parameter from left to right, and returning a final
    // value of this accumulator together with the new list.

    // mapAccumL :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])
    const mapAccumL = f => acc => xs =>
        xs.reduce((a, x) => {
            const pair = f(a[0])(x);
            return Tuple(pair[0])(a[1].concat(pair[1]));
        }, Tuple(acc)([]));

    // replicate :: Int -> a -> [a]
    const replicate = n => x =>
        Array.from({
            length: n
        }, () => x);

    // uncurry :: (a -> b -> c) -> ((a, b) -> c)
    const uncurry = f =>
        (x, y) => f(x)(y);

    // unlines :: [String] -> String
    const unlines = xs => xs.join('\n');

    // MAIN ---
    return main();
})();
Output:
Series and state of each wheel-set after 20 clicks:

[{"A":"312"},"12312312312312312312"]
[{"A":"21B","B":"43"},"13214213214213214213"]
[{"A":"D1D","D":"786"},"16718617816718617816"]
[{"A":"C1B","B":"34","C":"5B"},"13514314513413514314"]

Initial state of each wheel-set:

{"A":"123"}
{"A":"1B2"},{"B":"34"}
{"A":"1DD"},{"D":"678"}
{"A":"1BC"},{"B":"34"},{"C":"5B"}

jq

Works with: jq

Also works with gojq, the Go implementation of jq

In this entry, a single wheel is simply represented by a JSON object of the form { name: array }

where `name` is its name, and `array` is an array of the values on the wheel in the order in which they would be read.

A set of of number of wheels can thus be represented simply as the sum of the objects corresponding to each wheel. Thus the collection of illustrative number wheel groups can be defined as follows:

def wheels: [
    {
        "A": [1, 2, 3]
    },
    {
        "A": [1, "B", 2],
        "B": [3, 4]
    },
    {
        "A": [1, "D", "D"],
        "D": [6, 7, 8]
    },
    {
        "A": [1, "B", "C"],
        "B": [3, 4],
        "C": [5, "B"]
    }
];
# read($wheel)
# where $wheel is the wheel to be read (a string)
# Input: a set of wheels
# Output: an object such that .value is the next value, 
# and .state is the updated state of the set of wheels
def read($wheel):

  # Input: an array
  # Output: the rotated array
  def rotate: .[1:] + [.[0]];

  .[$wheel][0] as $value
  | (.[$wheel] |= rotate) as $state
  | if ($value | type) == "number"
    then {$value, $state}
    else $state | read($value)
    end;

# Read wheel $wheel $n times
def multiread($wheel; $n):
  if $n <= 0 then empty
  else read($wheel)
  | .value, (.state | multiread($wheel; $n - 1))
  end;

def printWheels:
  keys[] as $k
  | "\($k): \(.[$k])";

# Spin each group $n times
def spin($n):
  wheels[]
  | "The number wheel group:",
    printWheels,
    "generates",
    ([ multiread("A"; $n)  ] | join(" ") + " ..."),
    "";

spin(20)

Invocation

jq -nr -f intersecting-number-wheels.jq
Output:
The number wheel group:
A: [1,2,3]
generates
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 ...

The number wheel group:
A: [1,"B",2]
B: [3,4]
generates
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3 ...

The number wheel group:
A: [1,"D","D"]
D: [6,7,8]
generates
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6 ...

The number wheel group:
A: [1,"B","C"]
B: [3,4]
C: [5,"B"]
generates
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4 ...

Julia

const d1 = Dict("A" => [["1", "2", "3"], 1])
const d2 = Dict("A" => [["1", "B", "2"], 1], "B" => [["3", "4"], 1])
const d3 = Dict("A" => [["1", "D", "D"], 1], "D" => [["6", "7", "8"], 1])
const d4 = Dict("A" => [["1", "B", "C"], 1], "B" => [["3", "4"], 1],
    "C" => [["5", "B"], 1])

function getvalue!(wheelname, allwheels)
    wheel = allwheels[wheelname]
    s = wheel[1][wheel[2]]
    wheel[2] = mod1(wheel[2] + 1, length(wheel[1]))
    return haskey(allwheels, s) ? getvalue!(s, allwheels) : s
end

function testwheels(wheels, numterms = 20, firstwheel = "A")
    println("\nNumber Wheels:")
    for k in sort(collect(keys(wheels)))
        print("$k: [")
        for s in wheels[k][1]
            print(s, " ")
        end
        println("\b]")
    end
    print("Output: ")
    for _ in 1:numterms
        print(getvalue!(firstwheel, wheels), " ")
    end
    println("...")
end

foreach(testwheels, [d1, d2, d3, d4])
Output:
Number Wheels:
A: [1 2 3]
Output: 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 ...

Number Wheels:
A: [1 B 2]
B: [3 4]
Output: 1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3 ...

Number Wheels:
A: [1 D D]
D: [6 7 8]
Output: 1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6 ...

Number Wheels:
A: [1 B C]
B: [3 4]
C: [5 B]
Output: 1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4 ...

Kotlin

Translation of: Java
import java.util.Collections
import java.util.stream.IntStream

object WheelController {
    private val IS_NUMBER = "[0-9]".toRegex()
    private const val TWENTY = 20
    private var wheelMap = mutableMapOf<String, WheelModel>()

    private fun advance(wheel: String) {
        val w = wheelMap[wheel]
        if (w!!.list[w.position].matches(IS_NUMBER)) {
            w.printThePosition()
        } else {
            val wheelName = w.list[w.position]
            advance(wheelName)
        }
        w.advanceThePosition()
    }

    private fun run() {
        println(wheelMap)
        IntStream.rangeClosed(1, TWENTY)
            .forEach { advance("A") }
        println()
        wheelMap.clear()
    }

    @JvmStatic
    fun main(args: Array<String>) {
        wheelMap["A"] = WheelModel("1", "2", "3")
        run()
        wheelMap["A"] = WheelModel("1", "B", "2")
        wheelMap["B"] = WheelModel("3", "4")
        run()
        wheelMap["A"] = WheelModel("1", "D", "D")
        wheelMap["D"] = WheelModel("6", "7", "8")
        run()
        wheelMap["A"] = WheelModel("1", "B", "C")
        wheelMap["B"] = WheelModel("3", "4")
        wheelMap["C"] = WheelModel("5", "B")
        run()
    }
}

internal class WheelModel(vararg values: String?) {
    var list = mutableListOf<String>()
    var position: Int
    private var endPosition: Int

    override fun toString(): String {
        return list.toString()
    }

    fun advanceThePosition() {
        if (position == endPosition) {
            position = INITIAL // new beginning
        } else {
            position++ // advance position
        }
    }

    fun printThePosition() {
        print(" ${list[position]}")
    }

    companion object {
        private const val INITIAL = 0
    }

    init {
        Collections.addAll<String>(list, *values)
        position = INITIAL
        endPosition = list.size - 1
    }
}
Output:
{A=[1, 2, 3]}
 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2
{A=[1, B, 2], B=[3, 4]}
 1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3
{A=[1, D, D], D=[6, 7, 8]}
 1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6
{A=[1, B, C], B=[3, 4], C=[5, B]}
 1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4

Maple

with(ArrayTools):

module Wheel()
 option object;
 local spokes := Array([1,2,3]);
 local currentSpoke := 1;

 export currentValue::static := proc(self::Wheel)
  local valueOut;
  if type(self:-spokes[self:-currentSpoke], integer) then
   valueOut := self:-spokes[self:-currentSpoke]:
  else
   valueOut := currentValue(self:-spokes[self:-currentSpoke]):
  end if:
  rotate(self):
  return valueOut;
 end proc:

 export rotate::static := proc(self::Wheel)
  if self:-currentSpoke = ArrayNumElems(self:-spokes) then self:-currentSpoke := 1:
  else self:-currentSpoke += 1: end if:
 end proc:

 export ModuleApply::static := proc()
  Object(Wheel, _passed);
 end proc:

 export ModuleCopy::static := proc(new::Wheel, proto::Wheel, spo::Array, curr::integer, $)
  new:-spokes := spo:
  new:-currentSpoke := curr:
 end proc:
end module:

A := Wheel(Array([1,2,3]), 1):

seq(currentValue(A), 1..20);

A := Wheel(Array([1,B,2]), 1):
B := Wheel(Array([3,4]), 1):

seq(currentValue(A), 1..20);

A := Wheel(Array([1,d,d]), 1):
d := Wheel(Array([6,7,8]), 1):

seq(currentValue(A), 1..20);

A := Wheel(Array([1,b,C]), 1):
b := Wheel(Array([3,4]), 1):
C := Wheel(Array([5,b]), 1):

seq(currentValue(A), 1..20);
Output:

         1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2
         1, 3, 2, 1, 4, 2, 1, 3, 2, 1, 4, 2, 1, 3, 2, 1, 4, 2, 1, 3
         1, 6, 7, 1, 8, 6, 1, 7, 8, 1, 6, 7, 1, 8, 6, 1, 7, 8, 1, 6
         1, 3, 5, 1, 4, 3, 1, 4, 5, 1, 3, 4, 1, 3, 5, 1, 4, 3, 1, 4

Nim

import strutils, tables

type

  ElemKind = enum eValue, eWheel

  Elem = object
    case kind: ElemKind
    of eValue:
      value: Natural
    of eWheel:
      name: char

  Wheel = ref object
    elems: seq[Elem]
    index: Natural

  Wheels = Table[char, Wheel]

  WheelDescription = tuple[name: char; elems: string]


func initWheels(wheels: openArray[WheelDescription]): Wheels =
  ## Initialize a table of wheels from an array of wheel descriptions.

  for (name, elems) in wheels:
    let wheel = new(Wheel)
    for e in elems.splitWhitespace():
      if e[0].isUpperAscii():
        wheel.elems.add Elem(kind: eWheel, name: e[0])
      else:
        wheel.elems.add Elem(kind: eValue, value: e.parseInt())
    result[name] = wheel


func next(wheels: Wheels; name: char): Natural =
  ## Return the next element from a wheel.

  let wheel = wheels[name]
  let elem = wheel.elems[wheel.index]
  wheel.index = (wheel.index + 1) mod wheel.elems.len
  result = case elem.kind
           of eValue: elem.value
           of eWheel: wheels.next(elem.name)


when isMainModule:

  proc generate(wheelList: openArray[WheelDescription]; count: Positive) =
    ## Create the wheels from their description, then display
    ## the first "count" values generated by wheel 'A'.

    let wheels = wheelList.initWheels()
    for (name, elems) in wheelList:
      echo name, ": ", elems
    echo "generates:"
    for _ in 1..count:
      stdout.write ' ', wheels.next('A')
    echo '\n'


  {'A': "1 2 3"}.generate(20)
  {'A': "1 B 2", 'B': "3 4"}.generate(20)
  {'A': "1 D D", 'D': "6 7 8"}.generate(20)
  {'A': "1 B C", 'B': "3 4", 'C': "5 B"}.generate(20)
Output:
A: 1 2 3
generates:
 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2

A: 1 B 2
B: 3 4
generates:
 1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3

A: 1 D D
D: 6 7 8
generates:
 1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6

A: 1 B C
B: 3 4
C: 5 B
generates:
 1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4

Perl

Translation of: Julia
use strict;
use warnings;
use feature 'say';

sub get_next {
    my($w,%wheels) = @_;
    my $wh = \@{$wheels{$w}}; # reference, not a copy
    my $value = $$wh[0][$$wh[1]];
    $$wh[1] = ($$wh[1]+1) % @{$$wh[0]};
    defined $wheels{$value} ? get_next($value,%wheels) : $value;
}

sub spin_wheels {
    my(%wheels) = @_;
    say "$_: " . join ', ', @{${$wheels{$_}}[0]} for sort keys %wheels;
    print get_next('A', %wheels) . ' ' for 1..20; print "\n\n";
}

spin_wheels(%$_) for
(
 {'A' => [['1', '2', '3'], 0]},
 {'A' => [['1', 'B', '2'], 0], 'B' => [['3', '4'], 0]},
 {'A' => [['1', 'D', 'D'], 0], 'D' => [['6', '7', '8'], 0]},
 {'A' => [['1', 'B', 'C'], 0], 'B' => [['3', '4'], 0], 'C' => [['5', 'B'], 0]},
);
Output:
A: 1, 2, 3
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2

A: 1, B, 2
B: 3, 4
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3

A: 1, D, D
D: 6, 7, 8
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6

A: 1, B, C
B: 3, 4
C: 5, B
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4

Phix

with javascript_semantics
function terms(sequence wheels, integer n)
    sequence res = repeat(' ',n),
             pos = repeat(2,length(wheels)),
             wvs = vslice(wheels,1)
    integer wheel = 1, rdx = 1
    while rdx<=n do
        integer p = pos[wheel],
                c = wheels[wheel][p]
        p = iff(p=length(wheels[wheel])?2:p+1)
        pos[wheel] = p
        if c>'9' then
            wheel = find(c,wvs)
        else
            res[rdx] = c
            rdx += 1
            wheel = 1
        end if
    end while
    return res
end function
 
constant wheels = {{"A123"},
                   {"A1B2","B34"},
                   {"A1DD","D678"},
                   {"A1BC","B34","C5B"}}
 
for i=1 to length(wheels) do
    ?terms(wheels[i],20)
end for
Output:
"12312312312312312312"
"13214213214213214213"
"16718617816718617816"
"13514314513413514314"

Python

Python: Original class and generator based

from itertools import islice

class INW():
    """
    Intersecting Number Wheels
    represented as a dict mapping
    name to tuple of values.
    """

    def __init__(self, **wheels):
        self._wheels = wheels
        self.isect = {name: self._wstate(name, wheel) 
                      for name, wheel in wheels.items()}
    
    def _wstate(self, name, wheel):
        "Wheel state holder"
        assert all(val in self._wheels for val in wheel if type(val) == str), \
               f"ERROR: Interconnected wheel not found in {name}: {wheel}"
        pos = 0
        ln = len(wheel)
        while True:
            nxt, pos = wheel[pos % ln], pos + 1
            yield next(self.isect[nxt]) if type(nxt) == str else nxt
                
    def __iter__(self):
        base_wheel_name = next(self.isect.__iter__())
        yield from self.isect[base_wheel_name]
        
    def __repr__(self):
        return f"{self.__class__.__name__}({self._wheels})"
    
    def __str__(self):
        txt = "Intersecting Number Wheel group:"
        for name, wheel in self._wheels.items():
            txt += f"\n  {name+':':4}" + ' '.join(str(v) for v in wheel)
        return txt

def first(iter, n):
    "Pretty print first few terms"
    return ' '.join(f"{nxt}" for nxt in islice(iter, n))

if __name__ == '__main__':
    for group in[
      {'A': (1, 2, 3)},
      {'A': (1, 'B', 2),
       'B': (3, 4)},
      {'A': (1, 'D', 'D'),
       'D': (6, 7, 8)},
      {'A': (1, 'B', 'C'),
       'B': (3, 4),
       'C': (5, 'B')}, # 135143145...
     ]:
        w = INW(**group)
        print(f"{w}\n  Generates:\n    {first(w, 20)} ...\n")
Output:
Intersecting Number Wheel group:
  A:  1 2 3
  Generates:
    1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 ...

Intersecting Number Wheel group:
  A:  1 B 2
  B:  3 4
  Generates:
    1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3 ...

Intersecting Number Wheel group:
  A:  1 D D
  D:  6 7 8
  Generates:
    1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6 ...

Intersecting Number Wheel group:
  A:  1 B C
  B:  3 4
  C:  5 B
  Generates:
    1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4 ...

Python: Simplified procedural

def nextfrom(w, name):
    while True:
        nxt, w[name] = w[name][0], w[name][1:] + w[name][:1]
        if '0' <= nxt[0] <= '9':
            return nxt
        name = nxt
            
if __name__ == '__main__':
    for group in '''
A: 1 2 3
A: 1 B 2; B: 3 4
A: 1 D D; D: 6 7 8
A: 1 B C; B: 3 4; C: 5 B'''.strip().split('\n'):
        print(f"Intersecting Number Wheel group:\n  {group}")
        wheel, first = {}, None
        for w in group.strip().split(';'):
            name, *values = w.strip().split()
            wheel[name[:-1]] = values
            first = name[:-1] if first is None else first
        gen = ' '.join(nextfrom(wheel, first) for i in range(20))
        print(f"  Generates:\n    {gen} ...\n")
Output:
Intersecting Number Wheel group:
  A: 1 2 3
  Generates:
    1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 ...

Intersecting Number Wheel group:
  A: 1 B 2; B: 3 4
  Generates:
    1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3 ...

Intersecting Number Wheel group:
  A: 1 D D; D: 6 7 8
  Generates:
    1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6 ...

Intersecting Number Wheel group:
  A: 1 B C; B: 3 4; C: 5 B
  Generates:
    1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4 ...


And Again

This time the nextfromr function is recursive and it will only work for single character names and numbers due to character string rotation being used.
Input is just a list of Python dicts, and depends on c-python dicts being odered by key insertion order.

def nextfromr(w, name):
    nxt, w[name] = w[name][0], w[name][1:] + w[name][:1]
    return nxt if '0' <= nxt[0] <= '9' else nextfromr(w, nxt)
            
if __name__ == '__main__':
    for group in [{'A': '123'},
                  {'A': '1B2', 'B': '34'},
                  {'A': '1DD', 'D': '678'},
                  {'A': '1BC', 'B': '34', 'C': '5B'},]:
        print(f"Intersecting Number Wheel group:\n  {group}")
        first = next(group.__iter__())
        gen = ' '.join(nextfromr(group, first) for i in range(20))
        print(f"  Generates:\n   {gen} ...\n")
Output:
Intersecting Number Wheel group:
  {'A': '123'}
  Generates:
   1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 ...

Intersecting Number Wheel group:
  {'A': '1B2', 'B': '34'}
  Generates:
   1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3 ...

Intersecting Number Wheel group:
  {'A': '1DD', 'D': '678'}
  Generates:
   1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6 ...

Intersecting Number Wheel group:
  {'A': '1BC', 'B': '34', 'C': '5B'}
  Generates:
   1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4 ...

Python: Functional composition

Defining a unit rotation of the wheel-set as a recursive descent, and taking a map-accumulation of this recursion over a list of specific length and arbitrary content.

Translation of: Haskell
Works with: Python version 3.7
'''Intersecting number wheels'''

from itertools import cycle, islice
from functools import reduce


# clockWorkTick :: Dict -> (Dict, Char)
def clockWorkTick(wheelMap):
    '''The new state of the wheels, tupled with a
       digit found by recursive descent from a single
       click of the first wheel.'''
    def click(wheels):
        def go(wheelName):
            wheel = wheels.get(wheelName, ['?'])
            v = wheel[0]
            return (Tuple if v.isdigit() or '?' == v else click)(
                insertDict(wheelName)(leftRotate(wheel))(wheels)
            )(v)
        return go
    return click(wheelMap)('A')


# leftRotate :: [a] -> String
def leftRotate(xs):
    ''' A string shifted cyclically towards
        the left by one position.
    '''
    return ''.join(islice(cycle(xs), 1, 1 + len(xs)))


# ------------------------- TEST -------------------------
# main :: IO ()
def main():
    '''First twenty values from each set of test wheels.'''

    wheelMaps = [dict(kvs) for kvs in [
        [('A', "123")],
        [('A', "1B2"), ('B', "34")],
        [('A', "1DD"), ('D', "678")],
        [('A', "1BC"), ('B', "34"), ('C', "5B")]
    ]]
    print('New state of wheel sets, after 20 clicks of each:\n')
    for wheels, series in [
            mapAccumL(compose(const)(clockWorkTick))(
                dct
            )(' ' * 20) for dct in wheelMaps
    ]:
        print((wheels, ''.join(series)))

    print('\nInital states:')
    for x in wheelMaps:
        print(x)


# ----------------------- GENERIC ------------------------

# Tuple (,) :: a -> b -> (a, b)
def Tuple(x):
    '''Constructor for a pair of values,
       possibly of two different types.
    '''
    return lambda y: (
        x + (y,)
    ) if isinstance(x, tuple) else (x, y)


# compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
def compose(g):
    '''Right to left function composition.'''
    return lambda f: lambda x: g(f(x))


# const :: a -> b -> a
def const(k):
    '''The latter of two arguments,
       with the first discarded.
    '''
    return lambda _: k


# insertDict :: String -> a -> Dict -> Dict
def insertDict(k):
    '''A new dictionary updated with a (k, v) pair.'''
    def go(v, dct):
        return dict(dct, **{k: v})
    return lambda v: lambda dct: go(v, dct)


# mapAccumL :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])
def mapAccumL(f):
    '''A tuple of an accumulation and a map
       with accumulation from left to right.
    '''
    def nxt(a, x):
        tpl = f(a[0])(x)
        return tpl[0], a[1] + [tpl[1]]

    def go(acc):
        def g(xs):
            return reduce(nxt, xs, (acc, []))
        return g
    return go


# MAIN ---
if __name__ == '__main__':
    main()
Output:
New state of wheel sets, after 20 clicks of each:

({'A': '312'}, '12312312312312312312')
({'A': '21B', 'B': '43'}, '13214213214213214213')
({'A': 'D1D', 'D': '786'}, '16718617816718617816')
({'A': 'C1B', 'B': '34', 'C': '5B'}, '13514314513413514314')

Inital states:
{'A': '123'}
{'A': '1B2', 'B': '34'}
{'A': '1DD', 'D': '678'}
{'A': '1BC', 'B': '34', 'C': '5B'}

Quackery

As the contents of a wheel (e.g. [ 1 B 2 ]) is just Quackery code, wheels can be extended in interesting ways.

They could, for example, contain a nest that randomly selects a wheel to advance; [ 1 [ 2 random table [ B C ] ] 2 ] would do the same as [ 1 B 2 ], except that on the second click of the wheel, instead of always advancing wheel B, [ 2 random table [ B C ] ] would be evaluated, causing either wheel B or wheel C to advance arbitrarily.

  [ ]this[ ]done[
    dup take behead 
    dup dip 
      [ nested join 
        swap put ]
    do ]                      is wheel    ( --> n )

  [ ]'[ 
    ]'[ nested 
    ' [ wheel ] 
    swap join
    swap replace ]            is newwheel ( -->   )
    
  forward is A  forward is B  forward is C  
  forward is D  ( and so on, as required )
 
  [ wheel [ 1 2 3 ] ]   resolves A        ( --> n )

  [ wheel [ 3 4 ] ]     resolves B        ( --> n )

  [ wheel [ 5 B ] ]     resolves C        ( --> n )

  [ wheel [ 6 7 8 ] ]   resolves D        ( --> n )


  20 times [ A echo sp ] cr 
 
  newwheel A [ 1 B 2 ] 
  20 times [ A echo sp ] cr 
 
  newwheel A [ 1 D D ] 
  20 times [ A echo sp ] cr 
 
  newwheel A [ 1 B C ]
  newwheel B [ 3 4 ]        ( As B has been used already )
                            ( it's state may be [ 4 3 ]. )
                            ( So we reset it to [ 3 4 ]. )

  20 times [ A echo sp ] cr
Output:
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3 
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6 
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4 

Raku

(formerly Perl 6) A succinct Raku example using a few additional language features. Wheels are implemented as infinite repeating sequences, allowing a single iterator to keep track of the current position. This means the code contains no position tracking whatsoever.

#| advance rotates a named wheel $n by consuming an item from an infinite sequence. It is called
#| from within a gather block and so can use take in order to construct an infinite, lazy sequence
#| of result values
sub advance($g, $n) {
	given $g{$n}.pull-one {
		when /\d/ { take $_ }
		default   { samewith $g, $_ } # samewith re-calls this function with new parameters
	}
}

#| Input groups are a hash containing each wheel name as the key, and a list of values constructed
#| using <> to split on whitespace. They are transformed using xx * to repeat the list infinitely.
#| We then retrieve the underlying iterator in order for wheel position to be persistent. Each group
#| is then aggregated into a lazy output sequence using an infinite loop inside a gather block.
[
	{A => <1 2 3>},
	{A => <1 B 2>, B => <3 4>},
	{A => <1 D D>, D => <6 7 8>},
	{A => <1 B C>, B => <3 4>, C => <5 B>},
]
	#| %() converts a list of pairs produced by map into a hash. $^k and $^v are implicit variables.
	#| They are processed in alphabetical order and make the block arity 2, called with two vars.
	#| .kv gets the list of wheel names and wheel values from the input entry
	==> map({ %(.kv.map: { $^k => (|$^v xx *).iterator }) })
	#| gather constructs a lazy sequence, in which we infinitely loop advancing wheel A
	==> map({ gather { loop { advance $_, 'A' }} })
	#| state variables are only initialised once, and are kept between invocations.
	==> map({ state $i = 1; say "Group {$i++}, First 20 values: $_[^20]" })
Output:
Group 1, First 20 values: 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2
Group 2, First 20 values: 1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3
Group 3, First 20 values: 1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6
Group 4, First 20 values: 1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4

REXX

Quite a bit of the REXX code deals with detecting of errors   (and issuing error messages)   in the specification and
generation/construction of the wheel sets.

This REXX program uses   numbers   (any form),   not   digits   (for the values on the wheels).

/*REXX program  expresses numbers  from  intersecting number wheels  (or wheel sets).   */
@.=                                              /*initialize array to hold the wheels. */
parse arg lim @.1                                /*obtain optional arguments from the CL*/
if lim='' | lim=","  then lim= 20                /*Not specified?  Then use the default.*/
if @.1='' | @.1=","  then do;  @.1= ' A:  1 2 3 '
                               @.2= ' A:  1 B 2,    B:  3 4 '
                               @.3= ' A:  1 D D,    D:  6 7 8 '
                               @.4= ' A:  1 B C,    B:  3 4,    C:  5 B '
                          end
       do i=1  while @.i\='';  call run          /*construct wheel set and "execute" it.*/
       end   /*i*/
exit 0                                           /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
error: procedure; say;  say;    say '***error***'   arg(1);          say;   say;   exit 12
isLet: procedure; parse arg y;  return datatype(y, 'M') & length(y)==1   /*is a letter? */
isNum: procedure; parse arg y;  return datatype(y, 'N')                  /*is a number? */
/*──────────────────────────────────────────────────────────────────────────────────────*/
run: @wn= 'wheel name';     first=;      @noColon= "wheel name not followed by a colon:"
     @gn= 'gear name' ;     gear.=;      say copies("═", 79)
     say 'building wheel group for: '    @.i;    wheels= space(@.i);        upper wheels
        do #=1  while wheels\='';  parse var wheels  w gears "," wheels;    L= length(w)
        if L==2  then do;  !.#= left(w, 1)       /*obtain the one─character gear name.  */
                           if right(w, 1)\==':'  then call error @noColon  w
                           if \isLet(!.#)        then call error @wn "not a letter:"  w
                      end
                 else call error "first token isn't a"   @wn':'     w
        if #==1  then first= !.1                 /*Is this is the 1st wheel set?  Use it*/
        if first==''  then call error "no wheel name was specified."
        n= !.#                                   /*obtain the name of the 1st wheel set.*/
        gear.n.0= 1                              /*initialize default 1st gear position.*/
        say '   setting gear.name:'     n     "    gears=" gears
           do g=1  for words(gears);         _= word(gears, g)
           if isNum(_)  |  isLet(_)  then do;  gear.n.g= _;  iterate;  end
           call error  @gn  "isn't a number or a gear name:"  _
           end   /*g*/
        end      /*#*/
    say;                  say center(' running the wheel named '  first" ", 79, '─');   $=
        do dummy=0  by 0  until words($)==lim;           n= first
        z= gear.n.0;               x= gear.n.z;          z= z + 1
        gear.n.0= z;      if gear.n.z==''  then gear.n.0= 1
        if isNum(x)  then do;     $= $ x;    iterate;    end   /*found a number, use it.*/
        xx= x                                    /*different gear, keep switching 'til X*/
           do forever;            nn= xx
           if gear.nn.0==''  then call error "a gear is using an unknown gear name:"  x
           zz= gear.nn.0;         xx= gear.nn.zz
           zz= zz + 1;   gear.nn.0= zz;   if gear.nn.zz==''  then gear.nn.0= 1
           if isNum(xx)  then do;  $= $ xx;  iterate dummy;  end
           end   /*forever*/                     /* [↑]  found a number,  now use FIRST.*/
        end      /*dummy*/                       /*"DUMMY"  is needed for the  ITERATE. */
     say '('lim "results): "  strip($);      say;          say;          return
output   when using the default inputs:
═══════════════════════════════════════════════════════════════════════════════
building wheel group for:   A:  1 2 3
   setting gear.name: A     gears= 1 2 3

───────────────────────── running the wheel named  A ──────────────────────────
(20 results):  1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2


═══════════════════════════════════════════════════════════════════════════════
building wheel group for:   A:  1 B 2,    B:  3 4
   setting gear.name: A     gears= 1 B 2
   setting gear.name: B     gears= 3 4

───────────────────────── running the wheel named  A ──────────────────────────
(20 results):  1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3


═══════════════════════════════════════════════════════════════════════════════
building wheel group for:   A:  1 D D,    D:  6 7 8
   setting gear.name: A     gears= 1 D D
   setting gear.name: D     gears= 6 7 8

───────────────────────── running the wheel named  A ──────────────────────────
(20 results):  1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6


═══════════════════════════════════════════════════════════════════════════════
building wheel group for:   A:  1 B C,    B:  3 4,    C:  5 B
   setting gear.name: A     gears= 1 B C
   setting gear.name: B     gears= 3 4
   setting gear.name: C     gears= 5 B

───────────────────────── running the wheel named  A ──────────────────────────
(20 results):  1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4

Ruby

groups = [{A: [1, 2, 3]},
          {A: [1, :B, 2], B: [3, 4]},
          {A: [1, :D, :D], D: [6, 7, 8]},
          {A: [1, :B, :C], B: [3, 4], C: [5, :B]} ]

groups.each do |group|
  p group
  wheels = group.transform_values(&:cycle) 
  res = 20.times.map do
    el = wheels[:A].next
    el = wheels[el].next until el.is_a?(Integer)
    el
  end
  puts res.join(" "),""
end
Output:
{:A=>[1, 2, 3]}
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2

{:A=>[1, :B, 2], :B=>[3, 4]}
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3

{:A=>[1, :D, :D], :D=>[6, 7, 8]}
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6

{:A=>[1, :B, :C], :B=>[3, 4], :C=>[5, :B]}
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4

Visual Basic .NET

Translation of: C#
Imports System.Runtime.CompilerServices

Module Module1

    <Extension()>
    Iterator Function Loopy(Of T)(seq As IEnumerable(Of T)) As IEnumerable(Of T)
        While True
            For Each element In seq
                Yield element
            Next
        End While
    End Function

    Iterator Function TurnWheels(ParamArray wheels As (name As Char, values As String)()) As IEnumerable(Of Char)
        Dim data = wheels.ToDictionary(Function(wheel) wheel.name, Function(wheel) wheel.values.Loopy.GetEnumerator)
        Dim primary = data(wheels(0).name)

        Dim Turn As Func(Of IEnumerator(Of Char), Char) = Function(sequence As IEnumerator(Of Char))
                                                              sequence.MoveNext()
                                                              Dim c = sequence.Current
                                                              Return If(Char.IsDigit(c), c, Turn(data(c)))
                                                          End Function

        While True
            Yield Turn(primary)
        End While
    End Function

    <Extension()>
    Sub Print(sequence As IEnumerable(Of Char))
        Console.WriteLine(String.Join(" ", sequence))
    End Sub

    Sub Main()
        TurnWheels(("A", "123")).Take(20).Print()
        TurnWheels(("A", "1B2"), ("B", "34")).Take(20).Print()
        TurnWheels(("A", "1DD"), ("D", "678")).Take(20).Print()
        TurnWheels(("A", "1BC"), ("B", "34"), ("C", "5B")).Take(20).Print()
    End Sub

End Module
Output:
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2
1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3
1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6
1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4

Wren

Translation of: Go
Library: Wren-dynamic
Library: Wren-sort
Library: Wren-fmt
import "./dynamic" for Struct
import "./sort" for Sort
import "./fmt" for Fmt

var Wheel = Struct.create("Wheel", ["next", "values"])

var generate = Fn.new { |wheels, start, maxCount|
    var count = 0
    var w = wheels[start]
    while (true) {
        var s = w.values[w.next]
        var v = Num.fromString(s)
        w.next = (w.next + 1) % w.values.count
        wheels[start] = w
        if (v) {
            System.write("%(v) ")
            count = count + 1
            if (count == maxCount) {
                System.print("...\n")
                return
            }
        } else {
            while (true) {
                var w2 = wheels[s]
                var ss = s
                s = w2.values[w2.next]
                w2.next = (w2.next + 1) % w2.values.count
                wheels[ss] = w2
                v = Num.fromString(s)
                if (v) {
                    System.write("%(v) ")
                    count = count + 1
                    if (count == maxCount) {
                        System.print("...\n")
                        return
                    }
                    break
                }
            }
        }
    }
}

var printWheels = Fn.new { |wheels|
    var names = []
    for (name in wheels.keys) names.add(name)
    Sort.quick(names)
    System.print("Intersecting Number Wheel group:")
    for (name in names) {
        Fmt.print("  $s: $n", name, wheels[name].values)
    }
    System.write("  Generates:\n    ")
}

var wheelMaps = [
    {
        "A": Wheel.new(0, ["1", "2", "3"])
    },
    {
        "A": Wheel.new(0, ["1", "B", "2"]),
        "B": Wheel.new(0, ["3", "4"])
    },
    {
        "A": Wheel.new(0, ["1", "D", "D"]),
        "D": Wheel.new(0, ["6", "7", "8"])
    },
    {
        "A": Wheel.new(0, ["1", "B", "C"]),
        "B": Wheel.new(0, ["3", "4"]),
        "C": Wheel.new(0, ["5", "B"])
    }
]
for (wheels in wheelMaps) {
    printWheels.call(wheels)
    generate.call(wheels, "A", 20)
}
Output:
Intersecting Number Wheel group:
  A: [1, 2, 3]
  Generates:
    1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 ...

Intersecting Number Wheel group:
  A: [1, B, 2]
  B: [3, 4]
  Generates:
    1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3 ...

Intersecting Number Wheel group:
  A: [1, D, D]
  D: [6, 7, 8]
  Generates:
    1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6 ...

Intersecting Number Wheel group:
  A: [1, B, C]
  B: [3, 4]
  C: [5, B]
  Generates:
    1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4 ...

zkl

fcn intersectingNumberWheelsW(wheels){ // ("A":(a,b,"C"), "C":(d,e) ...)
   ws:=wheels.pump(Dictionary(),fcn([(k,v)]){ return(k,Walker.cycle(v)) });  // new Dictionary
   Walker.zero().tweak(fcn(w,wheels){
      while(1){
	 w=wheels[w].next();	// increment wheel w
	 if(Int.isType(w)) return(w);
      }      
   }.fp("A",ws))	// assume wheel A exists and is always first
}
wheelSets:=T( Dictionary("A",T(1,2,3)),
	      Dictionary("A",T(1,"B",2),   "B",T(3,4)),
	      Dictionary("A",T(1,"D","D"), "D",T(6,7,8)),
	      Dictionary("A",T(1,"B","C"), "C",T(5,"B"),  "B",T(3,4)) );
foreach ws in (wheelSets){
   println("Wheel set:");
   ws.pump(String,fcn([(k,v)]){ "  %s: %s\n".fmt(k,v.concat(" ")) }).print();
   println("-->",intersectingNumberWheelsW(ws).walk(20).concat(" "));
}
Output:
Wheel set:
  A: 1 2 3
-->1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2
Wheel set:
  A: 1 B 2
  B: 3 4
-->1 3 2 1 4 2 1 3 2 1 4 2 1 3 2 1 4 2 1 3
Wheel set:
  A: 1 D D
  D: 6 7 8
-->1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6
Wheel set:
  A: 1 B C
  C: 5 B
  B: 3 4
-->1 3 5 1 4 3 1 4 5 1 3 4 1 3 5 1 4 3 1 4