### Idiom to return 0 or random number of array elements

 on Dec 07, 2012 at 05:21 UTC Need Help??

Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I know the common idiom of getting a random array element: \$array[int rand @array];. I can then extend that to return a random number of elements: @array[0 .. int rand @array];. But I need to also sometimes get 0 elements. Is there a more succinct way than to have two statements like this? my @subset = @array[0 .. int rand(1 + @array); @subset = () if @subset > @array;

Replies are listed 'Best First'.
Re: Idiom to return 0 or random number of array elements
by LanX (Sage) on Dec 07, 2012 at 05:48 UTC
just include the possibility of @array[0..-1] which returns an empty list.

try something like: @array[0 .. (int rand (@array+1)) -1]

DB<130> @array=a..c => ("a", "b", "c") DB<131> @array[0 .. -1 + int rand 1+ @array] => () DB<132> @array[0 .. -1 + int rand 1+ @array] => ("a", "b") DB<133> @array[0 .. -1 + int rand 1+ @array] => ("a") DB<134> @array[0 .. -1 + int rand 1+ @array] => ("a", "b", "c") DB<135> @array[0 .. -1 + int rand 1+ @array] => ("a", "b")

Cheers Rolf

Perfect, that's what I was looking for. Guess I was close, though. Thanks!
Re: Idiom to return 0 or random number of array elements
by davido (Cardinal) on Dec 07, 2012 at 06:18 UTC

LanX got to the @slice[ 0 .. -1 ] solution ahead of me, but I'm thinking you might still be missing something.

In your first example, "\$element = \$array[int rand @array];", you're selecting a random element. In your second example, "@elements = @array[0 .. int rand @array];" you're selecting a random number of elements, but the elements being selected are not random except for the quantity. If your array contains "a, b, c, d, e", you'll get "a", or "a,b", or "a,b,c", etc. Even after dealing with the ability to get no elements at all, you're still not actually getting random elements.

Maybe that's what you want. Or maybe you want a random number of random elements (with no duplicates). If that's the case, you might just shuffle:

use List::Util 'shuffle'; use List::MoreUtils 'minmax'; my @array = 'a' .. 'z'; my @quantities; for( 1 .. 100 ) { # --------------------This is the solution line: ------------------- +---------- my @selected = @{ [ shuffle @array ] }[ 0 .. int( rand @array + 1 ) +- 1 ]; # ------------------------------------------------------------------ +------ push @quantities, my \$quantity = @selected; print "@selected => (\$quantity)\n"; } print "\nMin/Max elements: (", join( ',', minmax @quantities ), ")\n";

Dave

Re: Idiom to return 0 or random number of array elements
by BrowserUk (Patriarch) on Dec 07, 2012 at 05:37 UTC
I can then extend that to return a random number of elements: @array[0 .. int rand @array];. But I need to also sometimes get 0 elements.

That will return 0 elements approx. 10% of the time:

@array = 0 .. 9;; sub x{ return @array[ 0 .. int( rand @array ) ]; };; \$stats{ scalar( x() ) }++ for 1 .. 1000;; pp \%stats;; { "0" => 90, 1 => 103, 2 => 126, 3 => 99, 4 => 101, 5 => 101, 6 => 88, + 7 => 97, 8 => 105, 9 => 90 }

With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
Computers are making people easier to use everydaydiv
@array[0 ..0] is one and not zero elements!

DB<103> @array=a..f => ("a", "b", "c", "d", "e", "f") DB<104> @array[0..0] => "a"

UPDATE:

your statistical proof falls into the trap

scalar @array != scalar (LIST)

if your function() returns a one element list (0), then scalar function() will be 0, since 0 is the last element of the list.

(more detailed in Re^2: Idiom to return 0 or random number of array elements by eyepopslikeamosquito )

initializing @array = 1..10 makes it more obvious, b/c the last element of a sublist is now identical to the length!

DB<106> @array = 1 .. 10; => (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) DB<107> sub x{ return @array[ 0 .. int( rand @array ) ]; }; => 0 DB<108> \$stats{ scalar( x() ) }++ for 1 .. 1000; => "" DB<109> \%stats; => { 1 => 100, 2 => 101, 3 => 108, 4 => 108, 5 => 101, 6 => 103, 7 => + 79, 8 => 98, 9 => 100, 10 => 102 }

Cheers Rolf

Just to clarify what is going on, notice that the scalar is not required in:

\$stats{ scalar( x() ) }++
That is, this:
\$stats{ x() }++
produces numbers in the 0..9 range, as BrowserUk's original did, while this:
\$stats{ scalar( my @r = x() ) }++
produces numbers in the range 1..10 because this time the scalar context is getting the number of items in the array, in contrast to the earlier scalar context which was getting the value of the last element in the list.

For example, a run of this program:

use strict; use warnings; use Data::Dumper; my @array = 0 .. 9; sub x { return @array[ 0 .. int(rand @array) ] } my %stats; \$stats{ scalar( my @r = x() ) }++ for 1 .. 1000; print Dumper( \%stats );
produced:
\$VAR1 = { '6' => 81, '3' => 110, '7' => 84, '9' => 94, '2' => 96, '8' => 98, '4' => 89, '1' => 123, '10' => 117, '5' => 108

Update: For more detail on array vs list context see:

With \$hash{...} a scalar context is imposed on the key. With a slice like @hash{...} list context is imposed on the key.

use 5.010; use strict; sub WA { return wantarray } my %hash = ('' => 'Hello ', '1' => 'World'); say \$hash{ WA() }, @hash{ WA() };
perl -E'sub Monkey::do{say\$_,for@_,do{(\$monkey=[caller(0)]->[3])=~s{::}{ }and\$monkey}}"Monkey say"->Monkey::do'
Are you confusing the element at index 0 with 0 elements- e.g. the empty list? I want to the empty list with equal probability, and my solution does that. I'm just looking for a more succinct way to express it.

Can someone explain why this code produces key values in %stats hash that range "0" to "9"?

The key values, representing the number of elements in the random-length generated arrays should range from "1" to "10", since all generated arrays contain at least one element.

Re: Idiom to return 0 or random number of array elements
by bart (Canon) on Dec 07, 2012 at 12:08 UTC
my @subset = @array[0 .. int rand(1 + @array)-1];
If the second parameter for the .. operator is lower than the first parameter, then you'll get no items.

BTW this will not return a random number of random elements. Instead it'll return the first N elements where N is a random number. If you want random elements, you could shuffle the array first.

Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1007684]
Approved by Old_Gray_Bear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (1)
As of 2022-07-03 12:40 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?

No recent polls found

Notices?