One of the tasks from Perl Weekly Challenge #14.1 is to write a program to produce Van Eck sequence. I knew I've heard of it, and yes, it's from a Numberphile video: Numberphile -- Don't Know (the Van Eck Sequence) and OEIS/A181391.
To describe the Van Eck sequence: The first term is 0, and the following terms are defined to be the number of occurrences of the previous term, subtracted by the term index.
a[1] = 0
a[n+1] = 0, if a[n] is absent within a[0..n]
a[n+1] = n - m, Otherwise, where a[m] = a[n] and m < n and m is the largest of such.
By definition, to compute a[n+1]
, we look backward the entire sequence for a[n]
.
~laurent_r offered a loop-based solution:
use v6;
my @a = 0,;
for ^20 -> $n {
my $result = 0;
for reverse ^$n -> $m {
$result = $n - $m and last if @a[$m] == @a[$n];
# Note: $m is always smaller than $n, so $n - $m > 0
}
push @a, $result;
}
say @a;
Given the infinite nature of this sequence, I would like to make an object that represent this infinite sequence and, of course, lazily evaluated. Here's the result:
my $vaneck = Seq.new(
class :: does Iterator {
has %!seen is default(-1);
has Int $!term = -1;
has Int $!pos = 0;
method pull-one {
my $next = %!seen{$!term} == -1 ?? 0 !! $!pos - %!seen{$!term};
%!seen{$!term} = $!pos++;
return $!term = $next;
}
}.new()
);
Seq and Iterator are both builtin Types in Raku. %!seen
is a private variable for recording the position of previous occurrence of numbers.
Here's how you print the beginning 200 terms:
$vaneck[^200].map({ .say });
... or just keep it printing forever:
$vaneck.map({ .say });
本文為 Perl6: Van Eck 數列產生器 之英文版。
Top comments (0)