[REBOL] Re: On ordinal and cardinal numbers...
From: joel:neely:fedex at: 8-Jul-2001 19:04
Hi, Larry,
Nice variation on the theme!
Larry Palmiter wrote:
> Here is a different approach to circular addressing of a REBOL
> series. The heart of the matter is to be able to skip forward
> or backward by an arbitrary number of steps. Here are two
> functions which serve that purpose...
> Neither of the functions does ANY numeric indexing into the
> original series...
>
Ah, you sneaky fellow! ;-)
> The first function uses iteratation:
>
> ring-skip: func [s [series!] n [integer!]] [
> either positive? n [
> loop n [if tail? s: next s [s: head s]]
> ][
> loop abs n [s: back either head? s [tail s][s]]
> ]
> s
> ]
>
> The second uses recursion:
>
> ring-skip2: func [s [series!] n [integer!]] [
> if n = 0 [return s]
> either positive? n [
> if tail? s: next s [s: head s]
> ring-skip s n - 1
> ][
> s: back either head? s [tail s][s]
> ring-skip s n + 1
> ]
> ]
>
Absent tail-recursion elimination, I'd always use the iterative
version myself (but that's just a personal opinion, and certainly
not something I'd force on anyone else).
I did have a few observations though...
1) There is a peculiar "singularity" that occurs if the series
argument is positioned at the tail, but not at the head.
>> b: "abcde" == "abcde"
>> ring-skip b 5 == "abcde"
>> c: tail b == ""
>> ring-skip c 0 == ""
>> ring-skip c 5 == "e"
>> ring-skip c -1 == "e"
This could be guarded for at the beginning of either function.
2) Explicitly handing N = 0 is the bottoming-out case for the
recursive version. For both recursive and iterative strategies
it's also possible to short-cut on a completely empty series,
as that case requires no action. There's no significant savings
unless one or the other of those cases happens sufficiently
often, but it applies to both versions.
3) The last example you gave
>> ring-skip2 b 344
== [5]
shows that it's possible to burn lots of cycles with a large
enough second argument. Without messing up your policy of
no-numeric-indexing ;-) let me mention that there's no need
to step forward or backward multiple times around the series.
NOTE: I'm assuming here that the series is not totally empty!
In your example, the series had a length of 5, so a forward
ring-skip of 344 is exactly the same as forward ring-skip of
only 4. In addition, a forward or reverse ring-skip of any
exact multiple of the series length is the same as no skipping
at all.
Combining all of these ideas, we could have the following at the
beginning of either the iterative or recursive version:
if tail? s [s: head s]
if any [0 = length? s 0 = n: n // length? s] [return s]
after which the function will never make needless "round trips".
Of course, implementing idea (3) *requires* bailing out on empty
series values to avoid dividing by zero.
There may a clever way of rewriting these tests, or perhaps
even a suggestion that they're not needed most of the time, but
I hope that the benefit of speeding up some cases and removing
a few puzzling results are worthy of consideration.
Thanks for the interesting ideas!
-jn-
---------------------------------------------------------------
There are two types of science: physics and stamp collecting!
-- Sir Arthur Eddington
joel-dot-neely-at-fedex-dot-com