Three new keywords for Spin 1 & 2?
Phil Pilgrim (PhiPi)
Posts: 23,514
I've been working with permutations recently and came across Heap's algorithm for generating all n! permutations of a list of length n. In Spin, the program looks like this:
and produces this output:
What I really want is a routine that simply returns the next permutation in Heap's sequence, not all of them. Of course, there's a lot of state information in that algo that needs to be preserved between calls. The easiest way to do this in Spin is to put generate in its own cog and use flags to stimulate the next value, like this:
It produces the same output as before, but it needs a separate cog to do so.
What is needed here are three new keywords, begin, pause, and resume, that can be used to stop a method, then resume it from where it leaves off, without needing to run it in a separate cog. Of course, the method would still need its own stack. It would be used like this:
begin and resume execute the generate method in the same cog until generate hits a pause, which returns to the calling program. The pause keyword could also return a value, accessible by the resume call.
-Phil
CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 SIZE = 4 VAR byte array[SIZE + 1] OBJ sio: "Parallax Serial Terminal" PUB start | i sio.start(9600) bytemove(@array, string("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), SIZE) generate(@array, SIZE) PUB generate(addr, len) | i if (len == 1) show(addr) else repeat i from 0 to len - 1 generate(addr, len - 1) if (len & 1) swap(addr, 0, len - 1) else swap(addr, i, len - 1) PRI factorial(n) result := 1 repeat n result *= (n--) PRI swap(addr, i, j) | t t := byte[addr][i] byte[addr][i] := byte[addr][j] byte[addr][j] := t PRI show(addr) sio.str(addr) sio.char(13)
and produces this output:
ABCD BACD CABD ACBD BCAD CBAD DBCA BDCA CDBA DCBA BCDA CBDA DACB ADCB CDAB DCAB ACDB CADB DABC ADBC BDAC DBAC ABDC BADC
What I really want is a routine that simply returns the next permutation in Heap's sequence, not all of them. Of course, there's a lot of state information in that algo that needs to be preserved between calls. The easiest way to do this in Spin is to put generate in its own cog and use flags to stimulate the next value, like this:
CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 SIZE = 4 VAR byte array[SIZE + 1], need, ready long stack[SIZE * 10] OBJ sio: "Parallax Serial Terminal" PUB start | i sio.start(9600) bytemove(@array, string("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), SIZE) cognew(generate(@array, SIZE), @stack) repeat factorial(SIZE) need~~ repeat until ready sio.str(@array) sio.char(13) ready~ PUB generate(addr, len) | i repeat if (len == 1) repeat until need need~ ready~~ repeat while ready else repeat i from 0 to len - 1 generate(addr, len - 1) if (len & 1) swap(addr, 0, len - 1) else swap(addr, i, len - 1) PRI factorial(n) result := 1 repeat n result *= (n--) PRI swap(addr, i, j) | t t := byte[addr][i] byte[addr][i] := byte[addr][j] byte[addr][j] := t PRI show(addr) sio.str(addr) sio.char(13)
It produces the same output as before, but it needs a separate cog to do so.
What is needed here are three new keywords, begin, pause, and resume, that can be used to stop a method, then resume it from where it leaves off, without needing to run it in a separate cog. Of course, the method would still need its own stack. It would be used like this:
CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 SIZE = 4 VAR byte array[SIZE + 1] long stack[SIZE * 10] OBJ sio: "Parallax Serial Terminal" PUB start | i sio.start(9600) bytemove(@array, string("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), SIZE) begin(generate(@array, SIZE), @stack) repeat factorial(SIZE) sio.str(@array) sio.char(13) resume(generate) PUB generate(addr, len) | i repeat if (len == 1) pause else repeat i from 0 to len - 1 generate(addr, len - 1) if (len & 1) swap(addr, 0, len - 1) else swap(addr, i, len - 1) PRI factorial(n) result := 1 repeat n result *= (n--) PRI swap(addr, i, j) | t t := byte[addr][i] byte[addr][i] := byte[addr][j] byte[addr][j] := t PRI show(addr) sio.str(addr) sio.char(13)
begin and resume execute the generate method in the same cog until generate hits a pause, which returns to the calling program. The pause keyword could also return a value, accessible by the resume call.
-Phil
Comments
Pause is usually called yield. It should accept an argument that is returned by the calling resume, and resume should take an argument that is returned by the yield that execution resumes at.
EDIT: grammar