How do I write a Collatz sequence with List :: Gen?
I love the functional programming paradigm that List::Gen
Perl brings. Writing a Collatz sequence with it should be doable, albeit a bit tricky, since the length of the list is unknown a priori.
I am missing the final 1
at the end of the sequence with the following code:
use List::Gen '*';
iterate{ $_%2 ? 3*$_+1 : $_/2 }->from( 23 )->while( '!=1' )->say;
which prints:
23 70 35 106 53 160 80 40 20 10 5 16 8 4 2
What I essentially need with this approach is this do-while
. The documentation mentions while_
which is a "forward looking" version while
, but the interpreter cannot find such a method.
source to share
Here's a workaround that checks the defined
-ness element to decide when to end the list. This requires modifying the definition of an iterator to populate an element undef
immediately after it is encountered 1
in the chain:
iterate{ $_ == 1 ? undef : $_%2 ? 3*$_+1 : $_/2 }->from( 23 )->while( 'defined' )->say;
which prints
23 70 35 106 53 160 80 40 20 10 5 16 8 4 2 1
source to share
This works (like a start):
use List::Gen '*';
iterate{$_%2 ? 3*$_+1 : $_/2}->from(23)->until(sub{$_ == 1 ? (($delay = 1), 0) : $delay})->say();
Let me see if I can make a function out of this and make it $delay
safe ...
This should work, but not because the function passed to until
is called twice (except for the first value):
use List::Gen '*';
sub after { use feature 'state'; $f = shift(); $f = '$_' . $f unless (ref($f)); sub { state $d; $r = $d; $d = eval $f; $r } }
iterate{ $_%2 ? 3*$_+1 : $_/2 }->from( 23 )->until(after('==1'))->say;
This works for a double function call:
use List::Gen '*';
sub after { use feature 'state'; $f = shift(); $f = '$_' . $f unless (ref($f)); sub { state($d1,$d2); $r = $d2; $d2 = $d1; $d1 = eval $f; $r } }
iterate{ $_%2 ? 3*$_+1 : $_/2 }->from( 23 )->until(after('==1'))->say;
Still trying to figure out why the function until
gets called twice after the first call.
It only works for until
, not while
.
The above code only works with string arguments; this works with function references:
#!/usr/bin/perl
use strict;
use List::Gen '*';
sub after {
use feature 'state';
my $f = shift();
my $c = ref($f) eq 'CODE'
? '&$f()'
: '$_' . $f;
sub {
state($d1,$d2);
my $r = $d2;
$d2 = $d1;
$d1 = eval($c);
$f;
$r
}
}
iterate{$_%2 ? 3*$_+1 : $_/2}->from(23)->until(after('==1'))->say;
iterate{$_%2 ? 3*$_+1 : $_/2}->from(23)->until(after(sub{$_ == 1}))->say;
source to share