27

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 exercise, from my lab. I can't find a way to do solve this, because I am new in Perl.

In C, generating this array would by very easy, but I don't know how to do it in Perl.

JJJ
  • 32,902
  • 20
  • 89
  • 102
Mateusz
  • 853
  • 4
  • 12
  • 19

24 Answers24

61

For amusement value:

A method that works on systems where EOL is a single character:

#!/usr/bin/perl

use strict;
use warnings;

$/ = \1;

open 0;
my @r = map rand,<0>;

print "@r\n";

A possibly nondeterministic method that does not use for, while, until:

#!/usr/bin/perl
use strict; use warnings;

my @rand;

NOTLOOP:
push @rand, rand;
sleep 1;
goto NOTLOOP if 100 > time - $^T;

print 0 + @rand, "\n";

Using regular expressions:

#!/usr/bin/perl
use strict; use warnings;

my $s = '-' x 100;
$s =~ s/(-)/rand() . $1/eg;
my @rand = $s=~ m/([^-]+)/g;

Copying and pasting 100 rand invocations by hand is really passé:

#!/usr/bin/perl
use strict; use warnings;

my $s = '(' . 'rand,' x 100 . ')';
my @rand = eval $s;

A file I/O based solution that does not require /dev/random:

#!/usr/bin/perl
use strict; use warnings;

$/ = \1;

my @rand;

seek \*DATA, 0, 0;

NOTLOOP:
scalar <DATA>;
push @rand, rand;
goto NOTLOOP if $. < 100;
__DATA__

No reason to use recursion with Perl's goto

#!/usr/bin/perl
use strict; use warnings;
use autodie;

$/ = \1;

open my $F, '<', \( 1 x 100 . 0 );

my @rand or &notloop;

sub notloop {
    my $t = <$F>;
    $t or return;
    push @rand, rand;
    goto \&notloop;
}

Here is a recursive string eval version:

#!/usr/bin/perl
use strict; use warnings; use autodie;

local $/ = \1;
open my $F, '<', \( 1 x 100 . 0 );

my @rand;

eval <<'NOLOOP'
my $self = (caller(0))[6];
<$F> or die;
push @rand, rand;
eval $self;
NOLOOP
;

Of course, all of these actually do contain loops, but they do not use the keywords you were barred from using.

NB: This question has brought out the wacko in me, but I must admit it is amusing.

Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
49

Nothing could be simpler!

my @rands = (rand, rand, rand, rand, rand, rand, rand, rand, rand, rand,
  rand, rand, rand, rand, rand, rand, rand, rand, rand, rand, rand,
  rand, rand, rand, rand, rand, rand, rand, rand, rand, rand, rand,
  rand, rand, rand, rand, rand, rand, rand, rand, rand, rand, rand,
  rand, rand, rand, rand, rand, rand, rand, rand, rand, rand, rand,
  rand, rand, rand, rand, rand, rand, rand, rand, rand, rand, rand,
  rand, rand, rand, rand, rand, rand, rand, rand, rand, rand, rand,
  rand, rand, rand, rand, rand, rand, rand, rand, rand, rand, rand,
  rand, rand, rand, rand, rand, rand, rand, rand, rand, rand, rand,
  rand, rand);
Sean
  • 29,130
  • 4
  • 80
  • 105
37

Pure Regex Solution

perl -E'say for&{sub{"\U\x{fb01}\x{fb03}"=~/.{0,2}.{0,3}.{0,3}.{0,4}+(?{$_[++$#_]=rand})(*FAIL)/||pop;@_}}'

Double‐/e Regex Solution

This:

($_=(120.44.32)x(2+2*2**2)**2)=~s/\170/114.97.110.100/gee;
s/(.*)/64.95.61.40.$1.41.35.89.65.78.69.84.85.84/ee;
print "@_\n";

looplessly produces this:

0.636939813223766 0.349175195300148 0.692949079946754 0.230945990743699 0.61873698433654 0.940179094890468 0.435165707624346 0.721205126535175 0.0322560847184015 0.91310500801842 0.31596325316222 0.788125484008084 0.802964232426337 0.417745170032291 0.155032810595454 0.146835982654117 0.181850358582611 0.932543988687968 0.143043972615896 0.415793094159206 0.576503681784647 0.996621492832261 0.382576007897708 0.090130958455255 0.39637315568709 0.928066985272665 0.190092542303415 0.518855656633185 0.797714758118492 0.130660731025571 0.534763929837762 0.136503767441518 0.346381958112605 0.391661401050982 0.498108766062398 0.478789295276393 0.882380841033143 0.852353540653993 0.90519922056134 0.197466335156797 0.820753004050889 0.732284103461893 0.738124358455405 0.250301496672911 0.88874926709342 0.0647566487704268 0.733226696403218 0.186469206795884 0.837423290530243 0.578047704593843 0.776140208497122 0.375268613243982 0.0128391627800006 0.872438613450569 0.636808174464274 0.676851978312946 0.192308731231467 0.401619465269903 0.977516959116411 0.358315250197542 0.726835710856381 0.688046044314845 0.870742340556202 0.58832098735666 0.552752229159754 0.170767637182252 0.683588677743852 0.0603160539059857 0.892022266162105 0.10206962926371 0.728253338154527 0.800910562860132 0.628613236438159 0.742591620029089 0.602839705915397 0.00926448179027517 0.182584549347883 0.53561587562946 0.416667072500555 0.479173194613729 0.78711818598828 0.017823873107119 0.824805088282755 0.302367196288522 0.0677539595682397 0.509467036447674 0.906839536492864 0.804383046648944 0.716848992363769 0.450693083312729 0.786925293921154 0.078886787987166 0.417139859647296 0.9574382550514 0.581196777508975 0.75882630076142 0.391754631502298 0.370189654004974 0.80290625532508 0.38016959549288

Recursive Numeric Function Solution

As in fact, does this, if you print the array:

@_=(*100=sub{$_[0]?(rand,(*{--$_[0]}=*{$_[0]})->(@_)):()})->($==100);

The second solution now allows for getting different numbers of random numbers easily enough, since following the assignment above, you can do such niceties as:

 print for @42=42->($==42);

And yes, that is indeed a function named 42(). The previous assignment to @_ created it along with a hundred other numerically named functions.


Explanation

The first regex solution relies on Unicode’s tricky casing of the two characters matched against. It may (or may not) be more easily understood with whitespace and comments added:

use 5.010;

say for &{

    sub  { "\U\x{fb01}\x{fb03}" =~ m((?mix-poop)

#include <stdlib.h>
#include <unistd.h>
#include <regex.h>
#include "perl.h"
#include "utf8.h"

#ifndef BROKEN_UNICODE_CHARCLASS_MAPPINGS

                        .{0,2}

                .{0,3}          .{0,3}

                        .{0,4}

#define rand() (random()<<UTF_ACCUMULATION_SHIFT^random()&UTF_CONTINUATION_MASK)
       +(?{ $_  [++$#_] = rand() || rand() || UTF8_TWO_BYTE_LO (*PERL_UNICODE)
#else                                                          (*PRUNE)
#define FAIL                                                   (*ACCEPT)
          })                                                   (*FAIL)
#endif                                                         (*COMMIT)
    )poop || pop                                            @{ (*_{ARRAY})     }
    ;#;                                                     @{ (*SKIP:REGEX)   }
                                                            @{ (*_{ARRAY})     }
    }
}

The way to understand how the second regex solution works is:

  • First, reduce the compile-time constant expressions into their more customary forms so you can more easily read the literals. For example, \170 is "x".
  • Second, trim down the double e to a single e in each substitution, then print out what that leaves in the string both times.

I’m sure you’ll appreciate the comment. :)


For the recursive solution, adding whitespace may help a little:

(*100 = sub { $_[0]
                ? ( rand, ( *{ --$_[0] } = *{ $_[0] } )->(@_) )
                : ( )
            }
)->( $= = 100 );

The need to pass a variable as an argument is due to the auto-decrement requiring an lvalue. I did that because I didn’t want to have to say $_[0] - 1 twice, or have any named temporary variables. It means you can do this:

$N = 100;
print for $N->($N);

And when you’re done, $N == 0, because of pass‐by‐implicit‐reference semantics.

For the cost of a bit of repetition, you can relax the need of an lvalue argument.

(*100 = sub { $_[0]
                 ? ( rand, ( *{ $_[0] - 1 } = *{ $_[0] } )->( $_[0] - 1 ) )
                 : ( )
            }
)->( 100 );

Now you no longer need an lvalue argument, so you can write:

print for 100->(100);

to get all 100 random numbers. Of course, you also have a hundred other numeric functions, too, so you can get lists of random numbers in any of these ways:

@vi = 6->(6); 
@vi = &6( 6);

$dozen = 12;

@dozen         =  $dozen->($dozen);
@baker's_dozen = &$dozen(++$dozen);

@_ = 100;
print &0;  # still a hundred of 'em

(Sorry ’bout the silly colors. Must be an SO bug.)

I trust that clears everything up. ☺

tchrist
  • 78,834
  • 30
  • 123
  • 180
  • 4
    Am I really the only one to include an informative comment in my solution to help out our hapless student? :) – tchrist Nov 04 '10 at 00:33
  • 5
    You win the line noise award of the month/year :) Too bad I can't upvote twice :) – DVK Nov 04 '10 at 02:00
  • Why are there two `e` modifiers? What does it achieve? – Zaid Nov 04 '10 at 06:04
  • 2
    @Zaid: That one's the easier to understand of my two solutions. But to answer your question, the second `e` does the same thing as the first `e`. Kinda. – tchrist Nov 04 '10 at 06:41
33
my @rand = map { rand } ( 1..100 );

But a map is just a loop with fancy window-dressing.

If you need to do something 100 times, you're going to need to use some kind of iterative structure.

friedo
  • 65,762
  • 16
  • 114
  • 184
  • 6
    Well... a "map is just a loop" seems to be a VERY debatable statement... literally. http://stackoverflow.com/questions/3019925/is-map-a-loop .I happen to agree with labelling it a lop, FWIW – DVK Nov 03 '10 at 23:17
  • 2
    I wouldn't call map "just a loop". It's main feature it that it's an expression, so it can be part of a statement. The other looping constructs require their own statements (even in the modifier form). – brian d foy Nov 04 '10 at 16:22
  • 4
    Recursion is the same as iteration. – friedo Nov 09 '10 at 18:35
29
# I rolled a die, honestly!
my @random = (5, 2, 1, 3, 4, 3, 3, 4, 1, 6,
              3, 2, 4, 2, 1, 1, 1, 1, 4, 1,
              3, 6, 4, 6, 2, 6, 6, 1, 4, 5,
              1, 1, 5, 6, 6, 5, 1, 4, 1, 2,
              3, 1, 2, 2, 6, 6, 6, 5, 3, 3,
              6, 3, 4, 3, 1, 2, 1, 2, 3, 3,
              3, 4, 4, 1, 5, 5, 5, 1, 1, 5,
              6, 3, 2, 2, 1, 1, 5, 2, 5, 3,
              3, 3, 5, 5, 1, 6, 5, 6, 3, 2,
              6, 3, 5, 6, 1, 4, 3, 5, 1, 2);
mscha
  • 6,509
  • 3
  • 24
  • 40
24

Too bad most of the solutions focused on the non-looping part and neglected the random numbers part. Some of you may not be old enough to have a printed table of random values (or an appendix of logarithms) in the back of your engineering textbooks. It's all online now:

use LWP::Simple;

my @numbers = split /\s+/, #/ Stackoverflow syntax highlighting bug 
    get( 'http://www.random.org/integers/?num=100&min=1&max=100&col=1&base=10&format=plain&rnd=new' );

Here's the insanity that Tom was waiting to see, but I make it slightly more insane. This solution reduces the problem to a rand call:

my @numbers = rand( undef, 100 ); # fetch 100 numbers

Normal rand normally takes 0 or 1 arguments. I've given it a new prototype that allows a second argument to note how many numbers to return.

Notice some differences to the real rand though. This isn't continuous, so this has far fewer available numbers, and it's inclusive on the upper bound. Also, since this one takes two arguments, it's not compatible with programs expecting the real one since a statement like this would parse differently in each:

 my @array = rand 5, 5;

However, there's nothing particularly special about CORE::GLOBAL::rand() here. You don't have to replace the built-in. It's just a bit sick that you can.

I've left some print statements in there so you can watch it work:

BEGIN {
    my @buffer;

    my $add_to_buffer = sub {
        my $fetch = shift;
        $fetch ||= 100;
        $fetch = 100 if $fetch < 100;
        require LWP::Simple;
        push @buffer, split /\s+/, #/ Stackoverflow syntax highlighting bug
          LWP::Simple::get(
            "http://www.random.org/integers/?num=$fetch&min=1&max=100&col=1&base=10&format=plain&rnd=new"
            );
        };

    my $many = sub ($) {
        print "Fetching $_[0] numbers\n";
        $add_to_buffer->($_[0]) if @buffer < $_[0];
        my @fetched = splice @buffer, 0, $_[0], ();
        my $count = @fetched;
        print "Fetched [$count] @fetched\n";
        @fetched
        };

    *CORE::GLOBAL::rand = sub (;$$) {
        my $max = $_[0] || 1; # even 0 is 1, just like in the real one
        my $fetch = $_[1] || ( wantarray ? 10 : 1 );
        my @fetched = map { $max * $_ / 100 } $many->( $fetch );
        wantarray ? @fetched : $fetched[-1];
        };
    }
    
my @rand = rand(undef, 5);
print "Numbers are @rand\n\n";

@rand = rand(87);
print "Numbers are @rand\n\n";

$rand = rand(undef, 4);
print "Numbers are $rand\n\n";

$rand = rand();
print "Numbers are $rand\n\n";

$rand = rand(undef, 200);
print "Numbers are $rand\n\n";

My source of random numbers isn't important for this technique though. You could read from /dev/random or /dev/urandom to fill the buffer if you like.

brian d foy
  • 129,424
  • 31
  • 207
  • 592
14
sub f {
    my $n = shift;
    if( $n == 0 ) {
        return @_;
    }
    else {
        return f( $n-1, rand, @_ );
    }
}

my @random_array = f(100);
Peter G.
  • 14,786
  • 7
  • 57
  • 75
  • Dammit. You had to post a recursive solution after my answer :-) –  Nov 03 '10 at 23:12
  • I saw it blinking but read it only afterwards, a pity to delete it then. I tested it before posting otherwise I would have been earlier. – Peter G. Nov 03 '10 at 23:17
8

Since the task seems to be either to get you to use recursion or to learn how to write an easy loop in a not-so-easy form, humbly I submit the following FULLY EXECUTABLE Perl programs:

Camel:

#!/usr/bin/perl
                                      ''=~('('.'?'
           .'{'.(                   '`'|'%').("\["^
        '-').('`'|                '!').('`'|',').'"'
 .'\\'.'$'.  ("\`"|              ',').('`'|')').('`'|
'-').'='.('^'^("\`"|            '/')).('^'^('`'|'.')).
('^'^('`'|'.')).';'.(          '!'^'+').('`'|'&').('`'
  |'/').('['^')').'('        .'\\'.'$'.'='.'='.(('^')^(
       '`'|'/')).';'.      '\\'.'$'.'='.'<'.'='.'\\'.'$'
      .('`'|(',')).(     '`'|')').('`'|'-').';'.'+'."\+".
     '\\'.'$'.('=').   ')'.'\\'.'{'.('['^'+').('['^"\.").(
    '['^'(').("\`"|   '(').('{'^'[').'\\'.'@'.'='.','.("\{"^
    '[').('['^')').  ('`'|'!').('`'|'.').('`'|'$').'\\'.'}'.(
    '!'^'+').'\\'.  '$'.'='.'='.('^'^('`'|'/')).';'.('!'^'+')
    .('`'|('&')).(  '`'|'/').('['^')').('{'^'[').'('.'\\'.'@'.
    '='.')'.('{'^'[').'\\'.'{'.('!'^'+').('*'^'#').('['^'+').(
    '['^')').('`'|')').('`'|'.').('['^'/').('{'^'[').'\\'.'"'.(
     '['^')').('`'|'!').('`'|'.').('`'|'$').('{'^'[').'\\'.'$'.
     '='.('{'^'[').('`'|'/').('`'|'&').('{'^'[').'\\'.'$'.("\`"|
      ',').('`'|')').('`'|'-').'='.'\\'.'$'.'_'.'\\'.'\\'.(('`')|
       '.').'\\'.'"'.';'.('!'^'+').('*'^'#').'\\'.'$'.'='.'+'.'+'
        .';'.('!'^'+').('*'^'#').'\\'.'}'.'"'.'}'.')');$:='.' ^((
         '~'));$~='@'|'(';$^=')'^'[';$/='`'|'.';$,='('^"\}";  $\=
          '`'|'!';$:=')'^'}';$~='*'|'`';$^='+'^'_'; $/="\&"|  '@'
            ;$,='['&'~';$\=','^'|';$:='.'^"\~";$~=  '@'|'('   ;$^
             =')'^ '[';$/='`'|'.';$,='('^"\}";$\=   '`'|'!'   ;$:
                   =')'^'}';$~='*'|'`';$^=('+')^    '_';$/=   '&'
                   |'@';$,=    '['&'~';$\ ="\,"^     '|';$:   =(
                   ('.'))^     "\~";$~=   ('@')|     '(';$^  =(
                   (')'))^     "\[";$/=   "\`"|       "\.";  (
                   ($,))=      '('^'}';   ($\)         ='`'
                   |"\!";     $:=(')')^   '}';         ($~)
                    ='*'|     "\`";$^=    '+'^         '_';
                    ($/)=     '&'|'@'     ;$,=         '['&
                    '~';     $\=','       ^'|'         ;$:=
                    '.'^     '~'          ;$~=         '@'|
                    '(';      $^=         ')'          ^((
                    '['        ));       $/=           '`'
                    |((         '.'     ));            $,=
                    '('          ^((   '}'              ))
                    ;(             ($\))=               ((
                    ((              '`'))               ))
                    |+             "\!";$:=             ((
                   ')'            ))^+ "\}";            $~
                  =((           '*'))|  '`';           $^=
                 '+'^         "\_";$/=   '&'          |'@'
               ;($,)=                                ('[')&
             "\~";$\=                               ','^'|'

Martini:

#!/usr/bin/perl
                                                  ''=~('(?{'.
                                                  ('`'|'%').(
                                                  '['^"\-").(
                                                  '`'|"\!").(
                                                  '`'|(',')).
                                                  '"\\$'.('`'
                                                    |',').(
                                                    '`'|')'
                                                    ).('`'|
                                                    ('-')).
                                                    ('=').(
                                                   '^'^('`'|
                                                   ('/'))).(
                                                   '^'^('`'|
                                                   ('.'))).(
                                                  '^'^(('`')|
                                                  '.')).';'.(
                                                 '!'^'+').('`'
                                                 |'&').(('`')|
                                                '/').('['^')').
                                                '(\\$=='.('^'^(
                                               '`'|'/')).';\\$='
                                              .'<=\\$'.('`'|',').
                                             ('`'|')').('`'|"\-").
                                            ';++\\$=)\\{'.('['^'+')
                                           .('['^'.').('['^'(').('`'
                                          |'(').('{'^'[').'\\@'.('`'|
                                          '!').('['^')').('['^"\)").(
                                          '`'|'!').('['^'"').','.('{'
                                          ^'[').('['^')').('`'|'!').(
                                          '`'|'.').('`'|"\$").'\\}'.(
'!'^'+').'\\$'.('`'|')').'='.("\^"^(      '`'|'/')).';'.('!'^('+')).(
 '`'|'&').('`'|'/').('['^')').('{'^       '[').'(\\@'.('`'|'!').('['^
   ')').('['^')').('`'|'!').('['^         '"').')'.('{'^"\[").'\\{'.(
    '!'^'+').('*'^'#').('['^'+')          .('['^')').('`'|')').("\`"|
      '.').('['^'/').('{'^'[')            .'\\"'.('['^')').('`'|'!').
       ('`'|'.').('`'|"\$").(             '{'^'[').'\\$'.('`'|"\)").(
         '{'^'[').('`'|'/')               .('`'|'&').('{'^'[').'\\$'.
          ('`'|',').("\`"|                ')').('`'|'-').'=\\$_\\\\'.
            ('`'|('.')).                  '\\";'.('!'^'+').('*'^'#').
             '\\$'.('`'                   |')').'++;'.('!'^'+').('*'^
               "\#").                     '\\}"})');$:='.'^'~';$~='@'
                |'('                      ;$^=')'^'[';$/='`'|"\.";$,=
                '('^                      '}';$\='`'|'!';$:=')'^"\}";
                ($~)                      ='*'|'`';$^='+'^'_';$/='&'|
                '@';                      $,='['&'~';$\=','^('|');$:=
                '.'^                      '~';$~='@'|'(';$^=')'^"\[";
                ($/)                      ='`'|'.';$,='('^'}';$\='`'|
                '!';                      $:=')'^'}';$~='*'|('`');$^=
                '+'^                      '_';$/='&'|'@';$,='['&"\~";
                ($\)                      =','^'|';$:='.'^'~';$~='@'|
                '(';                      $^=')'^'[';$/='`'|('.');$,=
                '('^                      '}';$\='`'|'!';$:=')'^"\}";
                ($~)                      ='*'|'`';$^='+'^'_';$/='&'|
'@';$,='['&'~';$\=','^'|';$:='.'^'~'      ;$~='@'|'(';$^=')'^"\[";$/=
'`'|'.';$,='('^'}';$\='`'|'!';$:=')'      ^'}';$~='*'|'`';$^='+'^'_';

For the Holidays, snowflakes with recursion rather than iteration inside:

#!/usr/bin/perl
           '?'                          =~(
         '('.'?'                      ."\{".(
        '`'   |'%'  ).('['^"\-").(  '`'|   '!'
         ).('`'|',').    '"'.    '\\'.('$').(
         '`'|(',')).(    '`'|    ')').(('`')|
        ((  '-')   )).    +(    '`'   |')'  ).
       (((    '['   ))^+  ((  '/')   )).    '='
      .('^'^   ('`'|'/')) .( '^'^("\`"|   '.')).
     +(     '^'^('`'|'.')).';'.('!'^"\+").     ((
 '\\')).'$'.('`'|'#').('`'|'/').('['^'.').('`'|'.').(
'['^  '/').'='.  (('^')^(    '`'|'/')  ).(';').(  '!'^
'+'    ).('['^    '(').(      ('[')^    "\.").(    '`'
|'"'  ).(('{')^  ('[')).(    '['^'+')  .('['^'.'  ).+(
 '['^'(').('`'|'(').'_'.('['^')').('`'|'!').('`'|'.')
     .(     '`'|'$').('{'^'[').'\\'."\{".(     ((
      '!'))^   '+').('{'^ (( ('[')))).(   ('{')^
       '['    ).(   '{'^  ((  '[')   )).    (((
        ((  '{')   )))    ^+    '['   ).+(  ((
         '['))^')').(    '`'|    '%').(('[')^
         '/').(('[')^    '.')    .('['^')').(
        '`'   |'.'  ).('{'^"\[").(  '`'|   ')'
         ).('`'|                      "\&").(
           '{'                          ^((
           '['                          )))
         .'\\'.+                      '$'.'#'
        .+(   '`'|  '!').('['^')')  .''.   (((
         '['))^')').(    '`'|    '!').(('[')^
         '"').('_').(    '`'|    '/').(('`')|
        ((  '&')   )).    ((    '_'   )).(  ((
       '['    ))^   ')')  .(  '`'|   '!'    ).(
      ('`')|   '.').('`'| (( ('$')))).(   ('[')^
     ((     '('))).'>'.'\\'.'$'.('`'|',').     +(
 '`'|')').('`'|'-').('`'|')').('['^'/').';'.('!'^'+')
.''.  ('{'^'[')  .(('{')^    ('[')).(  '{'^'[').  ('{'
^((    '['))).    ("\["^      '+').(    '['^'.'    ).(
'['^  '(').('`'  |"\(").(    '{'^'[')  .'\\'.'@'  .''.
 ('`'|'!').('['^')').('['^')').('`'|'!').('['^('"')).
     ((     '_')).('`'|'/').('`'|'&').'_'.     +(
      ('[')^   ')').('`'| (( ('!')))).(   ('`')|
       '.'    ).(   '`'|  ((  '$')   )).    (((
        ((  '[')   )))    ^+    '('   ).((  ((
         ',')))).('{'    ^'['    ).('['^')').
         ('`'|"\!").(    '`'|    '.').(('`')|
        '$'   ).((  ';')).('!'^'+'  ).+(   '{'
         ^'[').(                      '{'^'['
           ).(                          '{'
           ^((                          '['
         ))).''.                      (('{')^
        '['   ).+(  '['^'+').('['^  '.')   .+(
         '['^('(')).(    '`'|    '(').('_').(
         '['^(')')).(    '`'|    '!').(('`')|
        ((  '.')   )).    +(    '`'   |'$'  ).
       '('    .((   ')')  ).  ';'.   (((    '!'
      ))^'+'   ).'\\'.'}' .( '!'^'+').(   ('!')^
     ((     '+'))).('!'^'+').('['^('(')).(     ((
 '['))^'.').('`'|'"').('{'^'[').('['^'+').('['^')').(
'`'|  ')').('`'  |"\.").(    '['^'/')  .'_'.('['  ^')'
).(    '`'|'%'    ).('`'      |'#').    (('[')^    '.'
).+(  '['^')').  ('['^'('    ).("\`"|  ')').('['  ^'-'
 ).('`'|'%').('{'^'[').'\\'.'{'.('!'^'+').('{'^'[').(
     ((     '{'))^'[').('{'^'[').('{'^'[')     .+
      '\\'.+   '$'.("\["^ (( '/'))).'='   .('['^
       '+'    ).(   '`'|  ((  '/')   )).    (((
        ((  '[')   )))    ^+    '+'   ).+(  ((
         '{'))^"\[").    '\\'    .'@'.'_'.';'
         .('!'^'+').(    '*'^    '#').(('[')^
        '+'   ).+(  '['^')').('`'|  ')')   .+(
         '`'|'.'                      ).('['^
           '/'                          ).(
           '{'                          ^((
         '['))).                      ('\\').
        '"'   .''.  ('['^')').('`'  |'!'   ).(
         '`'|('.')).(    '`'|    '$').(('{')^
         '[').('\\').    '$'.    ('`'|"\#").(
        ((  '`')   )|+    ((    '/'   ))).  +(
       '['    ^((   '.')  ))  .''.   (((    '`'
      ))|'.'   ).('['^'/' ). ('{'^'[').   ("\`"|
     ((     '/'))).('`'|'&').('{'^'[').''.     ((
 '\\')).'$'.('`'|',').('`'|')').('`'|'-').('`'|')').(
'['^  '/').'='.  '\\'.'$'    .(('[')^  '/').'\\'  .''.
(((    '\\')))    .('`'|      "\.").    ('\\').    '"'
.';'  .('!'^'+'  ).("\*"^    '#').''.  '\\'.'$'.  ('`'
 |'#').('`'|'/').('['^'.').('`'|'.').('['^'/').('+').
     ((     '+')).';'.('!'^'+').('*'^'#').     +(
      ('[')^   '+').('['^ (( (')')))).(   ('`')|
       ')'    ).(   '`'|  ((  '.')   )).    (((
        ((  '[')   )))    ^+    '/'   ).((  ((
         '_')))).('['    ^')'    ).('`'|'%').
         ('`'|"\#").(    '['^    '.').(('[')^
        ')'   ).+(  '['^'(').('`'|  ')')   .+(
         '['^'-'                      ).('`'|
           '%'                          ).+
           '('                          .((
         '\\')).                      '@'.'_'
        .((   ')')  ).('{'^"\[").(  '`'|   ')'
         ).('`'|'&').    ('{'    ^'[').('(').
         '\\'.'@'.'_'    .')'    .';'.(('!')^
        ((  '+')   )).    ((    (((   '\\'  ))
       )))    .((   '}')  ).  ('!'   ^((    '+'
      ))).+(   '!'^'+').( (( '['))^'+')   .('['^
     ((     '.'))).('['^'(').('`'|'(').'_'     .(
 '['^')').('`'|'!').('`'|'.').('`'|'$').'('.')'.';'.(
'['^  '+').('['  ^"\)").(    '`'|')')  .('`'|'.'  ).+(
'['    ^"\/").    "\_".(      ('[')^    "\)").(    '`'
|'%'  ).(('`')|  ('#')).(    '['^'.')  .('['^')'  ).+(
 '['^'(').('`'|')').('['^'-').('`'|'%').'('.'\\'.'@'.
     +(     '`'|'!').('['^')').('['^')').(     ((
      '`'))|   '!').('['^ (( '"'))).'_'   .('`'|
       '/'    ).(   '`'|  ((  '&')   )).    '_'
        .(  '['^   ')'    ).    (((   '`')  )|
         '!').(('`')|    '.')    .('`'|'$').(
         '['^'(').')'    .';'    .'"'.'}'.')'
        );(   $:)=  '.'^'~';$~='@'  |'('   ;$^
         =(')')^                      '[';#;#
           ;#;                          #;#

In each case, the output is something like this:

rand 1 of 100=0.625268682212667
rand 2 of 100=0.30160434879096
...
rand 100 of 100=0.584811321826528

If you want to see the loops or recursion embedded within, you can use perl -MO=Deparse martini.pl or perl -MO=Deparse camel.pl etc.

Only with Perl, right???

If you want to generate these lovely things -- check out Acme::Eyedrops

the wolf
  • 34,510
  • 13
  • 53
  • 71
7

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&$_:@_};
Eric Strom
  • 39,821
  • 2
  • 80
  • 152
5

While the copy'n'paste examples are novel and the map/foreach alternatives have also been mentioned, I think one approach that has not been discussed is recursion. Using recursion (an implicit loop) would need method calls and a simple 'if' statement: no for/grep/map/etc. It could be side-effecting or side-effect free.

Since this is homework, I will leave the implementation to the poster.

Happy coding.

BTW: Nobody has posted a regular expression solution yet ;-)

It's nice to see some even more innovative solutions! :-)

5

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";
MkV
  • 3,046
  • 22
  • 16
  • 1
    It's trivial if you keep a reference to it in a variable. What I wanted to do was to find a way to do everything in a solitary `sub { }` without storing the ref in a variable. – Sinan Ünür Nov 04 '10 at 01:28
  • Devel::Caller / callee seemed a bit much for the OP's question – MkV Nov 04 '10 at 13:14
  • 1
    @Sinan: bless the closure and invoke it against itself as a method. – tchrist Nov 04 '10 at 14:23
  • 1
    That `x99` token doesn’t look right: it needs to be two tokens. Also, `$#{$foo}` can always be rewritten as `$#$foo` due to the rule that dereferencing braces can be omitted whenever they contain nothing but a bareword symbol with **zero or more** leading dollar signs. I *do* like the double-closure solution. – tchrist Nov 04 '10 at 15:22
  • tchrist: I always find myself being explicit about the {} with *$#* and keeping the *VALxNUMBER* as close together as possible. Changes made. – MkV Nov 04 '10 at 15:57
  • @tcrist - there are plenty of people who consider the "always use {} when dereferencing" rule to be extremely conductive to better maintainability, even if the character count increases. – DVK Nov 04 '10 at 18:10
  • @DVK: Depends what counts as dereferencing then, eh? `${foo}` is seldom written outside of interpolated strings. But yeah, you’re right: I cannot count how many times I’ve seen someone confused because they’ve written `$$foo[0]` and thinking they’re getting `${$foo[0]}` but getting `${$foo}[0]` instead. I think it’s due to C precedence, like in `*argv[0] == '‑'`. Note that `B::Deparse` does show `$foo‑>[0]` as the unshortcutted `$$foo[0]`. I advise people to *always* write that the first way—with the deref arrow— *unless* they’re doing slices like `@{$some_aref}[2,3]`, when you’ve no choice. – tchrist Nov 05 '10 at 02:02
  • 1
    The world would be a better place if more Perl hackers knew about the Y combinator. – Porculus Nov 06 '10 at 17:11
5
#!/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;
brian d foy
  • 129,424
  • 31
  • 207
  • 592
Daniel Holz
  • 148
  • 5
  • Nice work! It’s always nice to see people play with their symbol tables. Been a while since I’ve had to explain perl4’s pass-by-name semantics using `*x` parameters to anybody. Ah, but I see you’ve used a *lexical* `$x`, which sidesteps that issue. – tchrist Nov 05 '10 at 03:39
  • Thanks Tom, I originally used positional parameters before realising I could keep the recursion count in a lexical and add another meaning for 'x' to the script. Wish I could have removed the map (even if it is just a glorified 'if')... – Daniel Holz Nov 05 '10 at 11:07
5

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.

squentin
  • 51
  • 1
4
    push @array, rand;
    push @array, rand;
    # ... repeat 98 more times
mob
  • 117,087
  • 18
  • 149
  • 283
4

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=<>);
ysth
  • 96,171
  • 6
  • 121
  • 214
4

Someone asked for a pure regex solution. How about

#!/usr/bin/perl
open my $slf, $0;
undef $/;
(my $s = <$slf>) =~ s/./rand()." "/eggs;
$s .= rand();
Community
  • 1
  • 1
LSpice
  • 141
  • 1
  • 6
  • @JadeNB: Why open `$0` when it’s already open with `__END__` and `seek(DATA,0,0)`? :) – tchrist Nov 04 '10 at 23:12
  • Oh for goodness sake, self-modifying code! It will only produce 99 of them unless you insert a `#` at byte 0. That or another `/g` in your omelette. :) – tchrist Nov 04 '10 at 23:17
  • IMHO it's NOT pure regex in a sense of you're still hard-coding a 100 char string, only as your own code (and need to fudge the code to get to 100 at that). Outside of "cool" value, I don't see it as any different than taking my original regex solution and replacing "Dx100" with a hardcoded string of 100 "D" characters. – DVK Nov 05 '10 at 16:27
  • My idea of a pure regex solution was to have the counting-to-100 done via some regex magic WITHOUT having 100-char string to do the implicit looping for you. – DVK Nov 05 '10 at 16:28
  • @DVK: Mine has no 100-char string, but good luck at catching it counting. :) – tchrist Nov 05 '10 at 20:19
  • In order: tchrist, good point!—I didn't think of that; tchrist, of course you're right (sloppy cut-and-pasting), and I've now added the #; DVK, yes, I know what you meant, and was just joking—sorry. – LSpice Nov 06 '10 at 16:36
3

Recursion:

sub fill_rand {
 my ($array, $count) = @_;
   if ($count >= 1) {
   unshift @$array, rand();
   fill_rand ($array, --$count);
 }
}

my @array;
fill_rand (\@array, 100);

"Tail-call optimised" version:

sub fill_rand {
    my $array = shift;
    my $count = shift;
    unshift @$array, rand();
    if ($count > 1) {
       $count--;
       @_ = ($array, $count);
       goto &fill_rand;
    }
}

my @array;
fill_rand(\@array, 100);

Using eval:

my @array;
eval("\@array = (" . ("rand(), " x 100) . ");");

If you assume that my perl is random (not an unwarranted assumption), you could use the perl file itself as a source of random data:

open FILE, __FILE__ or die "Can't open " . __FILE__ . "\n";
my $string;
read FILE, $string, 100;
close FILE;
my @array = map { ord } split //, $string;

Of course, you'll get the same results every time, but this is useful for testing.

  • 1
    Instead of using `goto &fill_rand` you could use [Sub::Call::Recur](http://search.cpan.org/perldoc/Sub::Call::Recur) or [Sub::Call::Tail](http://search.cpan.org/perldoc/Sub::Call::Tail) – Brad Gilbert Nov 05 '10 at 14:39
3

its ugly, but it works. the foreach is just to show that it does.

#!/usr/bin/perl

rand1();

$idx = 1;

foreach $item (@array) {
    print "$idx - $item\n";
    $idx++;
}

exit;



sub rand1() {
    rand2();
    rand2();
    rand2();
    rand2();
}

sub rand2() {
    rand3();
    rand3();
    rand3();
    rand3();
    rand3();
}

sub rand3() {
    push @array, rand;
    push @array, rand;
    push @array, rand;
    push @array, rand;
    push @array, rand;
}
Hopasaurus
  • 41
  • 3
2

Generate the data:

my @array = map { rand() } (0..99);

Print the data to show that you have the right result:

print "$_\n" foreach (@array);

The generation loop is hidden (there's no looping keyword visible - just a function/operator).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
2
@foo = (rand(), rand(), rand(), ... rand());
Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
2
my $u;

open(URAND, "/dev/urandom") || die $!;

read(URAND, $u, 100);

close URAND;

my @array = split(/ */, $u);
Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
elwood
  • 29
  • 1
  • That doesn’t work properly. You need to `read(URAND, $u, 100 * length pack("I")` and then `@array = unpack("I*", $u)`. Or `$/ = \length pack "I"; @array = unpack("I*", )`. – tchrist Nov 04 '10 at 15:14
  • 1
    Dunno whether elwood’s ever coming back, but a proper urandom solution is just `perl -le '$/ = \\(100*length pack "L"); print for unpack "L*" => <>' /dev/urandom`. – tchrist Nov 04 '10 at 17:11
1

As per requests from the listeners, a non-pure-regex solution:

$s="D" x 100; 
$s=~s/D/rand()." "/ge; 
@s=split(/ /,$s);
DVK
  • 126,886
  • 32
  • 213
  • 327
  • Come on now, you can do better than that. – tchrist Nov 03 '10 at 23:57
  • 1
    Meh. Why bother when Sinan's having so much fun and my brain's a total mush from 4 hours fighting with Eclipse. I literally ONLY did it because pst asked in his answer :) – DVK Nov 04 '10 at 01:58
  • 2
    Eclipse and I are not on speaking terms. – tchrist Nov 04 '10 at 11:47
  • I don't understand why anyone uses Eclipse with Perl. I've never heard anyone have a good time with it while trying to do Perl. – brian d foy Nov 04 '10 at 17:08
  • @brian - I was fighting in the Java territory :) – DVK Nov 04 '10 at 17:58
  • @DVK: Same here. I refuse to use Eclipse unless forced to. For my own stuff I develop in Bostic’s vi with makefiles or sometimes ant files. I have supergiant vi-style tags files built for both the Java6 and Java7 distributions/stdlibs. Java is often stupid, sometimes evil, and always *always* a royal PITFA nonpareil. I do have the Perl plugin but that’s more for demo purposes. I can’t imagine using it. – tchrist Nov 04 '10 at 18:06
  • BTW, I didn't notice, did anyone post a PURE regex solution? (defined as not needing a string multiplication operator)? – DVK Nov 04 '10 at 18:11
  • Eclipse has its positive points (compared to my main IDE of choice), but i'm not convinced they are worth either the 1GB memory footprint or the aggravation of its painful ways – DVK Nov 04 '10 at 18:12
  • @DVK: “A **PURE** regex solution defined as not needing a string repetition operator”... Well, sure, I can think of two or three ways instantly, one silly and one slick. But it’ll take me a bit to dink around to get the number of backtracks right; that or use (*PRUNE). – tchrist Nov 04 '10 at 23:08
  • 2
    @DVK: Oh, it’s perfectly credible, being a sort of its own existence proof. It works just fine, as does the expanded version, with or without *cpp*  (1) installed—curiously enough. :) – tchrist Nov 05 '10 at 20:17
  • the `x` operator here is a loop – Never Sleep Again Mar 02 '18 at 22:30
1

Recursion:

#!/usr/bin/perl
use warnings; use strict;

my @rands;
my $i=1;

sub push_rand {
    return if $#rands>=99;
    push @rands, rand;
    push_rand();
}

push_rand();

for (@rands) { print "$i: $_\n"; $i++; }
dawg
  • 98,345
  • 23
  • 131
  • 206
0

The question: do something (and something happens to be call rand()) a bunch of times without using a loop. Implied is, and without just repeating the source. None of the above answers actually answer this. They either repeat the source, or hide the looping. Here is a general approach that mitigates the repetition and does not hide looping (by implementing it using computed goto, or a regex, or recursion, etc). I call this approach "managed repetition."

100 has four prime factors: 2*2*5*5 == 100. So we need two repetition managers, five and two:

sub two{ my $y=shift; ($y->(), $y->()) }
sub five{my $y=shift; ($y->(), $y->(), $y->(), $y->(), $y->()) } 

then call them -- we're not recursing, because the recursion degenerate case constitutes a loop test -- just calling them:

my @array = five(sub{five(sub{two(sub{two(sub{rand()})})})});

There you are, a general way to call something a predetermined number of times without using a loop, rather, by delegating the repetition in an efficient and controlled way.

Discussion question: which order should the fives and twos be in and why?

Never Sleep Again
  • 1,331
  • 1
  • 9
  • 10