Perl
我决定有点反竞争,展示通常如何在Perl中编写这样的问题。
最后还有一个46个字符的代码高尔夫入口。
这前三个例子都从这个标题开始。
use Modern::Perl;
while( <> ){
chomp;
last unless $_;
Collatz( $_ );
}
use Sub::Call::Recur;
sub Collatz{
my( $n ) = @_;
$n += 0;
die 'invalid value' unless $n > 0;
die 'Integer values only' unless $n == int $n;
say $n;
given( $n ){
when( 1 ){}
when( $_ % 2 != 0 ){
recur( 3 * $n + 1 );
}
default{
recur( $n / 2 );
}
}
}
简单的迭代版本
sub Collatz{
my( $n ) = @_;
$n += 0;
die 'invalid value' unless $n > 0;
die 'Integer values only' unless $n == int $n;
say $n;
while( $n > 1 ){
if( $n % 2 ){
$n = 3 * $n + 1;
} else {
$n = $n / 2;
}
say $n;
}
}
优化后的迭代版本
sub Collatz{
my( $n ) = @_;
$n += 0;
die 'invalid value' unless $n > 0;
die 'Integer values only' unless $n == int $n;
state @next;
$next[1] //= 0;
until( defined $next[$n] ){
say $n;
if( $n % 2 ){
$next[$n] = 3 * $n + 1;
} else {
$next[$n] = $n / 2;
}
$n = $next[$n];
}
say $n;
say $n while $n = $next[$n];
}
现在我将展示如何使用 Perl v5.10.0 之前的版本完成上一个示例。
use strict;
use warnings;
while( <> ){
chomp;
last unless $_;
Collatz( $_ );
}
{
my @next = (0,0);
sub Collatz{
my( $n ) = @_;
$n += 0;
die 'invalid value' unless $n > 0;
until( $n == 1 or defined $next[$n] ){
print $n, "\n";
if( $n % 2 ){
$next[$n] = 3 * $n + 1;
} else {
$next[$n] = $n / 2;
}
$n = $next[$n];
}
print $n, "\n";
print $n, "\n" while $n = $next[$n];
}
}
性能测试
首先,IO(输入输出)始终是速度较慢的部分。因此,如果您按原样对它们进行基准测试,则应该获得每个函数相同的速度。
为了测试这些函数,我打开了一个与/dev/null
($null
)有关的文件句柄,并将每个say $n
编辑为say {$null} $n
。这是为了减少对IO的依赖。
use Modern::Perl;
use autodie;
open our $null, '>', '/dev/null';
use Benchmark qw':all';
cmpthese( -10,
{
Recursive => sub{ Collatz_r( 31 ) },
Iterative => sub{ Collatz_i( 31 ) },
Optimized => sub{ Collatz_o( 31 ) },
});
sub Collatz_r{
...
say {$null} $n;
...
}
sub Collatz_i{
...
say {$null} $n;
...
}
sub Collatz_o{
...
say {$null} $n;
...
}
运行了10次后,这是代表性的输出示例:
Rate Recursive Iterative Optimized
Recursive 1715/s -- -27% -46%
Iterative 2336/s 36% -- -27%
Optimized 3187/s 86% 36% --
最后,一个真正的代码高尔夫入门:
perl -nlE'say;say$_=$_%2?3*$_+1:$_/2while$_>1'
总共46个字符
如果您不需要打印起始值,您可以再删减5个字符。
perl -nE'say$_=$_%2?3*$_+1:$_/2while$_>1'
总共41个字符
实际代码部分为31个字符,但是没有 -n
开关代码将无法运行。因此我在计数时包含了整个示例。