Raku: Van Eck Sequence Generator

作者:   發佈於:   #raku

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 數列產生器 之英文版。