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.

+3


source to share


2 answers


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

      

0


source


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;

      

+1


source







All Articles