2015-05-29 3 views
3

Мне нравится парадигма функционального программирования, которую List::Gen привносит в Perl. Написание Collatz sequence с ним должно быть выполнимым, хотя и немного сложным, так как длина списка неизвестна априори.Как написать последовательность Collatz с List :: Gen?

я пропускаю окончательный 1 в конце последовательности со следующим кодом:

use List::Gen '*'; 
iterate{ $_%2 ? 3*$_+1 : $_/2 }->from(23)->while('!=1')->say; 

который печатает:

23 70 35 106 53 160 80 40 20 10 5 16 8 4 2 

То, что я в основном нужно с этим подходом является do-while. В документации упоминается while_, который является «перспективной» версией while, но интерпретатор не может найти такой метод.

ответ

0

Вот обходной путь, который проверяет элемент для defined -ness, чтобы решить, когда конец списка. Это требует изменения определения итератора немедленно населён undef элемент после того, как он встречает в цепи с 1:

iterate{ $_ == 1 ? undef : $_%2 ? 3*$_+1 : $_/2 }->from(23)->while('defined')->say; 

, который печатает

23 70 35 106 53 160 80 40 20 10 5 16 8 4 2 1 
1

Это работает (как начало):

use List::Gen '*'; 
iterate{$_%2 ? 3*$_+1 : $_/2}->from(23)->until(sub{$_ == 1 ? (($delay = 1), 0) : $delay})->say(); 

Позвольте мне увидеть, если я могу сделать функцию из этого и сделать $delay безопасным ...

Это должно работать, но не потому что функция передается until вызывается дважды (за исключением первого значения):

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; 

Это работает для вызова двойной функции:

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; 

По-прежнему пытается понять, почему функция until вызывается дважды после первого вызова.

Работает только для until, а не while.

Приведенный выше код работает только со строковыми аргументами; это один работает со ссылками функции:

#!/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; 
+0

Хм ... Если бы хорошую функцию, но обнаружил, что после того, как первый вызов 'while' или' until', последующие вызовы 'while' или' until' выполняются дважды для одного и того же значения: 23, 70, 70, 35, 35, 106, 106, ..., 4, 4, 2, 2, 1! Смотрите: 'iterate {$ _% 2? 3 * $ _ + 1: $ _/2} -> из (23) -> while (sub {print "_ = $ _ \ n"; $ _! = 1}) -> say; ' – kjpires

+0

Я понял [обходной путь] (http://stackoverflow.com/a/30664416/133939), который чувствует себя чище, что связано с добавлением 'undef' после того, как встречается первый« 1 », и тестирование для' defined'-ness – Zaid

+0

Если это wasn ' t для двойного вызова (который все еще вызывает у меня недоумение), я мог бы заставить эту функцию задержки возвращать дополнительные последовательности N, которые могли бы быть полезны для несконтерминирующих последовательностей. – kjpires

Смежные вопросы