tags:

views:

312

answers:

6

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.

+6  A: 

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');
codelogic
Or just change the brackets to parens and %array can stay a hash, not a ref.
kingkongrevenge
+11  A: 

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');
Adam Bellaire
Consider this post solved. =)
Mark Canlas
Of course, this would even be more correct, if we called that variable %hash instead of %array
innaM
@Manni: Good point. While hashes are technically also called "associative arrays" in practise every Perl programmer I know uses "array" to mean exclusively plain arrays, and "hash" to mean hashes.
Adam Bellaire
Ack, I just britishized the word "practice." I don't know where that came from.
Adam Bellaire
"practice" is the noun, "practise" the verb.
Sam Kington
So it was not only britishized but ungrammatical. Here in America, "practice" is both the noun and the verb.
Adam Bellaire
@Adam: In the English speaking world :P the easy way to remember which is which is to think of advise/advice. License/license, practise/practice et. al. all follow the same rules. ;-)
RET
+2  A: 

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');
Yanick
+3  A: 

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.

Greg Hewgill
I'd recommend use warnings over -w, as the former can be turned off in a lexical scope when needed.
friedo
+4  A: 

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.

daotoad
A: 

Boy, do I feel sheepish. Oh well... thanks for the help! :)

jbourque
Just remember 'use strict;', and 'use warnings;'
Brad Gilbert