Perl
Decidí ser un poco anticompetitivo y mostrar cómo codificaría normalmente ese problema en Perl.
También hay una entrada de golf de código de 46 caracteres (total) al final.
Estos tres primeros ejemplos comienzan todos con este encabezado.
#! /usr/bin/env perl
use Modern::Perl;
# which is the same as these three lines:
# use 5.10.0;
# use strict;
# use warnings;
while( <> ){
chomp;
last unless $_;
Collatz( $_ );
}
Versión recursiva simple
use Sub::Call::Recur;
sub Collatz{
my( $n ) = @_;
$n += 0; # ensure that it is numeric
die 'invalid value' unless $n > 0;
die 'Integer values only' unless $n == int $n;
say $n;
given( $n ){
when( 1 ){}
when( $_ % 2 != 0 ){ # odd
recur( 3 * $n + 1 );
}
default{ # even
recur( $n / 2 );
}
}
}
Versión iterativa simple
sub Collatz{
my( $n ) = @_;
$n += 0; # ensure that it is numeric
die 'invalid value' unless $n > 0;
die 'Integer values only' unless $n == int $n;
say $n;
while( $n > 1 ){
if( $n % 2 ){ # odd
$n = 3 * $n + 1;
} else { #even
$n = $n / 2;
}
say $n;
}
}
Versión iterativa optimizada
sub Collatz{
my( $n ) = @_;
$n += 0; # ensure that it is numeric
die 'invalid value' unless $n > 0;
die 'Integer values only' unless $n == int $n;
#
state @next;
$next[1] //= 0; # sets $next[1] to 0 if it is undefined
#
# fill out @next until we get to a value we've already worked on
until( defined $next[$n] ){
say $n;
#
if( $n % 2 ){ # odd
$next[$n] = 3 * $n + 1;
} else { # even
$next[$n] = $n / 2;
}
#
$n = $next[$n];
}
say $n;
# finish running until we get to 1
say $n while $n = $next[$n];
}
Ahora voy a mostrar cómo haría ese último ejemplo con una versión de Perl anterior a la v5.10.0
#! /usr/bin/env perl
use strict;
use warnings;
while( <> ){
chomp;
last unless $_;
Collatz( $_ );
}
{
my @next = (0,0); # essentially the same as a state variable
sub Collatz{
my( $n ) = @_;
$n += 0; # ensure that it is numeric
die 'invalid value' unless $n > 0;
# fill out @next until we get to a value we've already worked on
until( $n == 1 or defined $next[$n] ){
print $n, "\n";
if( $n % 2 ){ # odd
$next[$n] = 3 * $n + 1;
} else { # even
$next[$n] = $n / 2;
}
$n = $next[$n];
}
print $n, "\n";
# finish running until we get to 1
print $n, "\n" while $n = $next[$n];
}
}
Punto de referencia
Primero, el IO siempre será la parte lenta. Entonces, si realmente los comparó como están, debería obtener aproximadamente la misma velocidad de cada uno.
Para probarlos, abrí un identificador de archivo en /dev/null
( $null
) y edité cada say $n
para leer say {$null} $n
. Esto es para reducir la dependencia de IO.
#! /usr/bin/env perl
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;
...
}
Después de ejecutarlo 10 veces, aquí hay una salida de muestra representativa:
Calificar iterativo recursivo optimizado
Recursivo 1715 / s - -27% -46%
Iterativo 2336 / s 36% - -27%
Optimizado 3187 / s 86% 36% -
Finalmente, una entrada de código de golf real:
perl -nlE'say;say$_=$_%2?3*$_+1:$_/2while$_>1'
46 caracteres en total
Si no necesita imprimir el valor inicial, puede eliminar 5 caracteres más.
perl -nE'say$_=$_%2?3*$_+1:$_/2while$_>1'
41 caracteres en total
31 caracteres para la parte del código real, pero el código no funcionará sin el -n
interruptor. Así que incluyo el ejemplo completo en mi recuento.