Series, Memoization, and Limits

Out on Reddit, my last post got some interesting comments. In particular, one commenter creates a finite series of squares, and then asks “That’s all well and good, but what would I have to do to say ‘up to 900’ instead of saying ‘up to 30^2’?” That’s a great question, and I’d like to explore the answer to it, though I’m going to stick with the Fibonacci series for my examples.

First, there’s a very quick and dirty answer to this. If you modify the original series, it is easy to add a limit term:

That gives you all the Fibonacci numbers less than or equal to 900. But the list is no longer expandable; with this one, if you say @Fibonacci[100], rather than calculating the 100th value of the series, it will just act like it’s a normal out-of-bounds array access.

In an ideal world, we’d have an easy way of looking at just part of an infinite series. What tools do we have in our toolbox? Well, there’s grep:

…and that last one is where the trouble starts, because it never comes back. By 20, we’re already well over 900. But grep’s been fed an infinite list, and it doesn’t know anything about it, so it just keeps on looking forever. So that won’t do.

As you can see, first is great at finding the first Fibonacci number greater than 900. It’s pretty useless for finding all the numbers less then 900, though.

So, the bad news here is that, as I write this, Perl 6 does not have a method which can give us a finite portion of an infinite list based on some test, other than “give us the first N elements”. The good news is that the function we want is dead simple to write:

This is actually a straightforward modification of the normal grep code. $test is defined as Mu so it can be anything that can be smartmatched against, including junctions. Then we scan the list, exiting if a smartmatch against the current element fails, and otherwise returning the element in our output list.

So here we have it: we’ve still got the efficiency of the de facto memoized solution, but we can also carve off portions of the list based on some criteria. I threw in the last one there just to show it didn’t have to be a Code block passed in; in this case, it shows us how far we get down the series with Int values before our current limited integer math throws in the towel and gives you back a Num instead. (Note that once we get arbitrarily large Ints in Rakudo, that line will no longer work, because take-while(@Fibonacci, Int) will return an infinite list!)

And now for the even slightly better news: as of today, take-while is available in the List::Utils module.