如何使用 List::Gen 编写 Collatz 序列?
How can I write a Collatz sequence with List::Gen?
我喜欢 List::Gen
brings to Perl. Writing a Collatz sequence 函数式编程范例,它应该是可行的,尽管有点挑战,因为列表的长度不是先验的。
我在序列末尾缺少最后的 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
的 'look-ahead' 版本,但解释器找不到这样的方法。
这有效(作为开始):
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;
这里有一个解决方法,可以测试元素的 defined
-ness 来决定何时结束列表。它需要修改迭代器定义以在链中遇到 1
后立即填充 undef
元素:
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
我喜欢 List::Gen
brings to Perl. Writing a Collatz sequence 函数式编程范例,它应该是可行的,尽管有点挑战,因为列表的长度不是先验的。
我在序列末尾缺少最后的 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
的 'look-ahead' 版本,但解释器找不到这样的方法。
这有效(作为开始):
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;
这里有一个解决方法,可以测试元素的 defined
-ness 来决定何时结束列表。它需要修改迭代器定义以在链中遇到 1
后立即填充 undef
元素:
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