No Fisher-Yates Shuffle In Perl 6

Ovid on 2009-01-11T16:30:48

When I wrote Hangman in Perl 6, I used the following for shuffling a list:

my method shuffle (*@items) {
    # Fisher-Yates shuffle
    my $i = @items.elems;
    while ($i) {
        my $j = $i.rand.int;
        $i--;
        @items[ $i, $j ] = @items[ $j, $i ];
    }
    return @items;
}

Today I was bothered by this and realized that it's not very Perl 6ish. In fact, Perl 6 is so focused on solving common programmer problems that I realized that the List class should have a shuffle builtin. After much playing around in the internals of Perl 6 and attempting to implement the Fisher-Yates shuffle, I realized that the pick method was better, so I was going to implement it internally with pick, but then I read the documentation more carefully. Shuffling an array in Perl 6 is trivial; the name is just not what I expected.

my @shuffled = @array.pick(*);


Overwriting the array in place

Juerd on 2009-01-12T20:51:52

@array.=pick(*);

This might even be optimized and actually shuffle the elements within the array.

Re:Overwriting the array in place

Ovid on 2009-01-12T21:01:19

Yeah, I'm discovering that as I'm writing tests for my $string.trim function (and it looks like it will be accepted as part of core Perl 6! Yay! :)

Re:Overwriting the array in place

afbach on 2009-01-20T23:20:12

Trying that in the hangman code:
@items.=pick(*);

gets:
Null PMC access in find_method()

if I do either (after:
my $i = @items.elems;
):
@items.=pick($i);

or:
@items = @items.pick($i);

I get:
Cannot assign to readonly variable

Doing:
return @items.pick($i);

works though ('*' again gets the Null PMC).

Re:Overwriting the array in place

Ovid on 2009-01-21T11:12:40

That pick seems to work for me:

~ $ perl6 -e 'my @items = ^10; @items .= pick(*); @items.perl.say'
[2, 8, 9, 4, 1, 5, 3, 7, 6, 0]
~ $ perl6 -e 'my @items = ^10; my $i = @items.elems; @items .= pick(*); @items.perl.say'
[6, 2, 7, 1, 4, 8, 9, 5, 3, 0]
~ $ perl6 -e 'my @items = ^10; my $i = @items.elems; @items .= pick($i); @items.perl.say'
[7, 8, 5, 1, 2, 0, 4, 3, 9, 6]
~ $ perl6 -v
This is Rakudo Perl 6, revision 35858 built on parrot 0.9.0-devel
for darwin-2level.

Copyright 2006-2008, The Perl Foundation.

If you update parrot and can produce a small test case, I'd be curious to see it. Or I just might be misunderstanding you :)

Re:Overwriting the array in place

afbach on 2009-01-21T16:35:11

Yeah, I was able to get it to work from the command line yesterday too, so I'm guessing it's got to do w/ @items being a param to a method, maybe?, as in hangman.pl. Just try your new shuffle in there. I was working on making up a smaller test case. I use a nightly fetch of parrot/rakudo.

Re:Overwriting the array in place

afbach on 2009-01-24T19:17:54

Good news! as of Rakudo Perl 6, revision 35960 built on parrot 0.9.0-devel:
return @items.pick(*);

works, at least on x86 Leopard box.