How can I generate an array in Perl with 100 random values, without using a loop?
I have to avoid all kind of loops, like \"for\", foreach\", while. This is my exerc
map
is used here as a topicalizer over a single value, exempting it from loop status:
my @rand = map&$_($_),sub{@_<=100&&goto&{push@_,rand;$_[0]};shift;@_};
or with two subs:
my @rand = sub{&{$_[0]}}->(sub{@_<=100&&goto&{(@_=(rand,@_))[-1]};pop;@_});
both of these are Y-combinator style self-passed subs that build up the list via iteration but one is clearly faster than the other.
you can fix the inefficiency with s'g[^}]+'goto&{unshift@_,rand;$_[-1]'
but then its getting a bit long.
or to sidestep the call stack:
my @rand = do{local*_=sub{(push@_,rand)<100?goto&_:@_};&_};
or with eval, no variable assignment, no external state, one anon sub:
my @rand = eval'sub{@_<100?eval((caller 1)[6]):@_}->(@_,rand)';
but most concise of all is:
my @rand = map&$_,sub{(100^push@_,rand)?goto&$_:@_};
No perl loops:
#!/usr/bin/perl
use strict;
use warnings;
@ARGV=q!echo 'int rand(void); int printf(const char *format, ...); int main(void) { int i; for(i=0;i<100;++i)printf("%d\\\\n",rand()); return 0; }' | gcc -x c - && ./a.out |!;
chomp(my @array=<>);
Using an anonymous sub and recursion:
use strict;
use warnings;
my @foo;
my $random;
($random = sub {
push @{$_[0]}, rand;
return if @{$_[0]} == $_[1];
goto \&$random;
})->(\@foo, 100);
print "@foo\n";
print scalar @foo, "\n";
Using computed goto, for all the fortran fans:
use strict;
use warnings;
sub randoms {
my $num = shift;
my $foo;
CARRYON:
push @$foo, rand;
# goto $#{$foo} < 99 ? 'CARRYON' : 'STOP';
goto ( ('CARRYON') x 99, 'STOP' )[$#$foo];
STOP:
return @$foo;
}
my @foo = randoms(100);
print "@foo\n";
print scalar(@foo)."\n";
2 anonymous subs and an arrayref:
use strict;
use warnings;
my @bar = sub { return &{$_[0]} }->
(
sub {
push @{$_[1]}, rand;
goto \&{$_[0]}
unless scalar(@{$_[1]}) == $_[2];
return @{$_[1]};
},
[],
100
);
print "@bar\n";
print scalar(@bar), "\n";
#!/usr/bin/perl
use strict;
use warnings;
my $x = 99;
my @rands = (rand,(*x=sub{rand,(map{*x->($x,sub{*x})}($x)x!!--$x)})->($x,*x));
use feature 'say';
say for @rands;
push @array, rand; push @array, rand; # ... repeat 98 more times
Another silly method, how about using a tied array that return a random value ?
use strict;
use warnings;
package Tie::RandArray;
use Tie::Array;
our @ISA = ('Tie::StdArray');
sub FETCH { rand; }
package main;
my @rand;
my $object = tie @rand, 'Tie::RandArray';
$#rand=100;
my @a= @somearray;
warn "@a";
Of course the tied array could cache the values, so that a second array would not be needed to have stable values.