Perl, Haskell, stuff
a list of numbers like 10,25,50,100,250,500,1000,etc without tracking any other state than the number itselfNope, this isn't A020179 but the much more prosaic A112024. Debolaz wanted this sequence, or something similar to it, for the same reason the US used it for currency - the numbers are human meaningful, and are useful as the numbers get exponentially bigger: in his case, for the labels for a chart. This may not be the most interesting of sequences, but, as autarch pointed out, there are many algorithms that work, and exploring some of them is fun and/or instructive.
sub seq_1 {
my $n = shift;
my $val = 10;
my @ret;
for (1..$n) {
push @ret, $val;
$val *= $val =~ /^1/ ?
2.5
: 2;
}
return @ret;
}
say for seq_1(10);
autarch suggested dividing by 10 until you get to 1 (or not) as an alternative.
So we were back to a state variable like the position of the element in the sequence.
map { 10 * 10**(int $_/3) * [1,2.5,5]->[$_ % 3] } 0..10
That is to say
10 10 10 100 100 100
* 1 2.5 5 1 2.5 5
= 10 25 50 100 250 500
i.e
10 * 10 ^
0 0 0 1 1 1
*
1 2.5 5 1 2.5 5
Not much else to say really, though I think indexing into [1,2.5,5] with the modded value
is cute.
awwaid suggested an improvement:
map { 10**(int $_ / 3 +2) * 2**($_ % 3 -2) } -1..9
Which hurt my head for a while: it comes from the realization that the sequence is actually
10 100 100 100 1000 1000 / 1 4 2 1 4 2
sub your_seq {
my $n = shift;
return 10 unless $n;
my @prev = your_seq($n-1);
return (@prev, $prev[-1] * ($n % 3 == 1 ? 2.5 : 2));
}
That has a rather odd recursion style: turning it into tail-recursive style, and then
eliminating the recursion using the techniques from
Higher Order Perl
sub cycle {
my @list = @_;
my @curr = @list;
return sub {
@curr = @list unless @curr;
return shift @curr;
};
}
Now we can create a list of functions that multiply by 2.5, 2, 2:
cycle([ map { times($_) } @multipliers ])
Where "times" is a function that given one multiplier, returns a function that
multiplies by that number. That is to say, it's the * operator "curried".
And by great coincidence, I just uploaded
Sub::Curried to CPAN, so we'll
use that:
curry times ($x,$y) { $x * $y }
We'll use the "curry" declarator for the rest of this example, largely because of the
automatic argument unpacking magic (I love Matt Trout's
Devel::Declare).
Now that we have a sequence, we need to write something similar to Haskell's scanr,
that repeatedly applies a transformation, returning a list of the intermediate values.
curry scan_iterator ($it, $start) {
my $next = $start;
return sub {
my $ret = $next;
$next = $it->()->($next); # prepare next value;
return $ret;
};
};
And as Perl doesn't like infinite calculations, we may as well transform a slice of this
sequences into an array:
curry iterator_to_array ($it, $count) {
return map { $it->() } 1..$count;
};
At which point we can put the whole thing together like so:
say for iterator_to_array(
scan_iterator(
mk_seq( [2.5, 2, 2] )
)->(10) # start value
)->(12); # number of iterations
I've uploaded the final source code as
an example for Sub::Curried.
Elegant? Overcomplicated? You decide!
Update:
Notice how (times) with no arguments is equivalent to a function reference, due to the autocurrying!say for take 12 => scanl(times)->(10 => cycle [2.5, 2, 2] );
Joeri Samson
July 27th, 2008 at 4:21 pm
should be: if a number is a power of 10, multiply by 2.5
augustss
July 27th, 2008 at 5:41 pm