Jeremy Bourque
Jeremy Bourque

Reputation: 3543

Is it possible to define anonymous subroutines in a hash constructor in Perl?

Is it possible to define anonymous subroutines in a hash constructor in Perl?

I'm trying to do something like this:

my %array = { one   => sub { print "first $_[0]" },
              two   => sub { print "next  $_[0]" },
              three => sub { print "last  $_[0]" }};

$array{$foo}->('thing');

But it isn't working. The code seems to run and compile, but the values in the array are blank. If I do this:

my %array;

$array{'one'}   = sub { print "first $_[0]" };
$array{'two'}   = sub { print "next  $_[0]" };
$array{'three'} = sub { print "last  $_[0]" };

$array{$foo}->('thing');

Then it seems to work fine. So I have a workaround, but it's just bugging me and I wondered if anyone knows whether it's possible and, if so, what the syntax is.

Upvotes: 1

Views: 1393

Answers (5)

daotoad
daotoad

Reputation: 27234

Greg Hewgill is on the right track. Always enable the strict pragma. Also, always enable warnings--but I recommend against using the -w switch in your code.

Instead, take advantage of the use warnings pragma. That way you can selectively disable particular groups of warnings in lexically scoped areas of your code.

For example:

use strict;
use warnings;

foo('bar');
foo();

sub foo {
    no warnings 'uninitialized';

    my $foo = shift || 'DEFAULT';

    print "Foo is $foo\n";
} 

Consistent use of the strict and warnings pragmas will save you hours and hours of time.

Upvotes: 4

Greg Hewgill
Greg Hewgill

Reputation: 994887

I spent something like two hours trying to track down this exact braces-vs-parentheses problem in a script not too long ago. If you use the -w switch to Perl, then you'll get a warning "Reference found where even-sized list expected", which at least gives a clue where to look. Today, all Perl scripts should start with:

#!/usr/bin/perl -w

use strict;

Your Perl life will be immeasurably less frustrating.

Upvotes: 3

Yanick
Yanick

Reputation: 1280

It's supposed to be parenthesis, not curly braces:

my %array = ( one   => sub { print "first $_[0]" },
              two   => sub { print "next  $_[0]" },
              three => sub { print "last  $_[0]" }});

'{ a => 1, b => 2 }' produces a reference to a hash. So the following would also work:

my $array = { one   => sub { print "first $_[0]" },
              two   => sub { print "next  $_[0]" },
              three => sub { print "last  $_[0]" }};

$array->{one}->('thing');

Upvotes: 2

Adam Bellaire
Adam Bellaire

Reputation: 110539

It looks like you're assigning into the hash incorrectly. Using {} constructs an anonymous hash reference, which you assign to a scalar. But you're assigning to a named hash (%array).

You need to assign into a scalar:

my $array = { one   => sub { print "first $_[0]" },
              two   => sub { print "next  $_[0]" },
              three => sub { print "last  $_[0]" }};

$array->{$foo}->('thing');

Or to not use the anon constructor syntax:

my %array = ( one   => sub { print "first $_[0]" },
              two   => sub { print "next  $_[0]" },
              three => sub { print "last  $_[0]" });

$array{$foo}->('thing');

Upvotes: 11

codelogic
codelogic

Reputation: 73762

That's because in the first case, you're creating a hashref not a hash, what you want is:

my $array;
$array = { one => ... }; # not %array = { .. };
...
$array->{one}->('thing');

Upvotes: 6

Related Questions