Perl Weekly Challenge 034: Slices and a Dispatch Table

Slices

Write a program that demonstrates using hash slices and/or array slices.

In the spirit of TIMTOWTDI I decided to write a single program that demonstrates both the tasks at the same time.

Let’s start with slices. Slices are parts of structures (arrays and hashes). Slicing has a special syntax by which you tell Perl which indices or keys you want to use to obtain a slice.

For example, consider the following array:

my @keys = qw( not_this_one
               this_one
               this_one_too
               it_was_enough );

Naturally, we want to select the second and third one. We can use

$keys[1], $keys[2]

or

map $keys[$_], 1, 2;

but there’s a shorter and cleaner syntax for the same:

@keys[1, 2]

The at-sign at the beginning says “extract more than one element from the array” and the square brackets contain a list of indices of the elements. Interestingly, it’s usually the fastest solution, too.

For hashes, there’s a similar syntax that lists the keys instead of indices and, unsurprisingly, uses curly brackets—and a bit surprisingly still the at-sign. For example, having

my %hash = (not_this_one  => 'not selected',
            this_one      => 'selected first',
            this_one_too  => 'selected second',
            it_was_enough => 'not selected either');

to extract the “selected” values we can use

@hash{qw{ this_one this_one_too }}

instead of

$hash{this_one}, $hash{this_one_too}

or

map $hash{$_}, qw( this_one this_one_too )

We can even combine the two slices into

@hash{ @keys[1, 2] }

There’s one more type of slices, so called “index-value slices”. They don’t extract just the values from the arrays or hashes, but the indices or keys, as well. They use the percent sign as the sigil, so

%keys[1, 2]

returns

1, 'this_one', 2, 'this_one_too'

and

%hash{ @keys[1, 2] }

returns

'this_one', 'selected first', 'this_one_too', 'selected second'

where the order of the pairs is not guaranteed.

BTW, you can slice a list, too, similarly to an array:

('a' .. 'z')[15, 4, 17, 11]

There’s currently no way of slicing a list in the index-value way.

Dispatch table

Write a program that demonstrates a dispatch table.

Dispatch table is a method how to safely invoke a function based on a value coming from outside. Using an external value directly as a subroutine name is dangerous and prohibited under strict refs (unless you first assign the value to a variable, but let’s pretend we don’t know about it). Using an external value as a method name is legal, but still dangerous. Moreover, you want to be able to change the implementation without changing the API, i.e. the subroutine name can change while the external value stays the same.

A dispatch table is a hash that translates the possible external values to code references or method names. In my example, I created the following subroutines operating on the variables defined above:

sub show_array_slice {
    say for @keys[1, 2];
}

sub show_hash_slice {
    say for @hash{ @keys[1, 2] };
}

sub show_index_value_slice {
    my %selected = %hash{ @keys[1, 2] };
    say for values %selected;
}

and included them to a dispatch table in the following way:

my %dispatch = (show_array_slice       => \&show_array_slice,
                show_hash_slice        => \&show_hash_slice,
                show_index_value_slice => \&show_index_value_slice);

my $what_to_show = shift;
my $action = $dispatch{$what_to_show}
           || sub { die "Unknown action $what_to_show!\n" };
$action->();

The $action contains a code reference, dereferencing it with ->() runs the referenced code. If the user supplies an unknown action, the anonymous subroutine will be used and the program will die.

Leave a comment

About E. Choroba

user-pic I blog about Perl.