Nobody did mention array slices, so I naturally couldn't resist fixing the omission. In truth, the whole setup feels like a self-study exercise in slicing and permutations.
In realistic program code, once you do have indices at hand (in other words, a mapping), there's usually no need to rearrange the actual data. Indirection can be used instead: $data[ $mapping[$i] ]. Nevertheless, it's been a somewhat fun exercise.
#! /usr/bin/perl
use strict;
use warnings;
my @words = qw(this array is an);
my @order = (0, 3, 1, 2);
print "words = @words \n";
print "order = @order \n";
# First of all, keep in mind that keys does not only work on hashes:
# using it on arrays will yield their list of indices.
my @indices = keys @words; # (0 .. 3)
print "indices = @indices \n";
# Now, let us introduce the array slice. An array slice
# is used to extract any number of array elements.
# Here, we use the slice to get at a specific full reordering
# (permutation) of the words:
my @aslice = @words[ @order ];
print "aslice = @aslice \n";
# Oh, but this did not work! The @order gave word positions in order,
# instead of giving the order of positions.
# We need to find the inverse mapping of @order.
my @inverse;
@inverse[@order] = keys @order; # (0, 3, 1, 2) => (0, 1, 2, 3)
print "inverse = @inverse \n";
# Just to check out that it checks out:
foreach my $i (keys @order) {
die if $inverse[ $order[$i] ] != $i;
die if $order[ $inverse[$i] ] != $i;
}
# Now, slicing either @inverse[@order] or @order[@inverse] will yield
# the indices (0..3). When used to shuffle the @words, they "undo"
# or reverse each others action.
my @proper = @words[ @inverse ];
print "proper = @proper \n";
# There we have it, ordered per request. And back again:
print "back again = @proper[ @order ] \n";
# Alright. The simplest way to randomly shuffle a list
# is to use the List::Util module.
use List::Util qw( shuffle );
# To create a new puzzle, we use the shuffle() to shuffle the shuffle.
my @tantrum = shuffle keys @words;
@words = @words[ @tantrum ];
@order = @order[ @tantrum ];
@inverse[@order] = keys @order;
print "new words = @words \n";
print "new order = @order \n";
print "new inverse = @inverse \n";
print "new proper = @words[ @inverse ] \n";
# That's it, thanks for reading.