views:

126

answers:

4

I see people using two styles for passing named parameters in Perl:

use strict;
use warnings;
use Data::Dumper;

sub foo {
    print Dumper @_;
}

sub bar {
    print Dumper @_;
}

foo( A => 'a', B => 'b' );
bar( { A => 'a', B => 'b' } );

What are the advantages in using foo() style instead of bar() style?

+3  A: 

First, an explanation of the two methods:

sub foo {
    # Transform the array to a hash
    my %args = @_;

    foreach my $key ( keys %args ) {
        print "$key => $args{$key}\n";
    }
}

# Pass an array of values
foo( A=>'a', B=>'b' );

In this first case all you're doing is passing an array. The => in this context is not the hash key / value indicator which you might think. In this context it's just a "fat comma".

sub bar {
    my ($hash_ref) = @_;
    foreach my $key ( keys %$hash_ref ) {
        print "$key => $hash_ref->{$key}\n";
    }
}

# pass a ref to an anonymous hash
bar( { A=>'a', B=>'b' } );

In this second case you are creating an anonymous hash and passing a reference to that hash as the argument to the function.

Why choose one over the other? In the book, Perl Best Practices, chapter 9 under the heading "Named Arguments" the author recommends using the second style when there are more than three arguments to the function. He also prefers it because it catches mismatched numbers of arguments at compile time rather than at run time.

Robert S. Barnes
it's a while since i read it, but i thought the recommendation to use a hashref with 3+ arguments was instead of a standard unnamed argument list, i.e. calling `foo( 'a', 'b' )`, rather than recommending hashrefs over hashes
plusplus
@plusplus: The title of the section is: "Use a hash of named arguements for any subroutine that has more than three arguements." In the body of the piece he specifically says to use a hash ref over raw name / value pairs converted to a hash.
Robert S. Barnes
+1 for specifying your reference.
Philippe A.
A: 

When i executed the same program on windows.i got the following output:

C:\Documents and Settings\Administrator>perl perltest.pl
$VAR1 = 'A';
$VAR2 = 'a';
$VAR3 = 'B';
$VAR4 = 'b';
$VAR1 = {
          'A' => 'a',
          'B' => 'b'
        };
Vijay Sarathi
what did you expect to see?
plusplus
+6  A: 

The second method passes a reference to hash, while the first just passes a list.

There are two aspects here: in theory, a reference to hash could be better in terms of performance, though for short argument lsits this is neglecable. For a simple call like foo(a => 1, b => 2) there is no performance difference, because @_ is actually an alias to the original values.

But if the caller already has the values in a hash, the first stlye requires conversion from hash to list, and then back again to hash, which can be slow.

The second aspect is the question who is responsible for the conversion to a hash. The first style leave it up the function being called, and if that just does my %args = @_, it will produce curious warnings if the argument list is not of even length.

That's why I slightly prefer the second style (or I use Perl 6, which supports named arguments natively).

moritz
+1 for pointing out the performance aspects.
Robert S. Barnes
These "performance aspects" are unnecessary micro-optimizations that yield no practical benefit. A hash can be used as easily as a list: `foo(%hash)` or a hashref: `foo(%{$hashref})`. Sticking with the foo style leaves more power to the user.
jmz
Actually, my own benchmarking has shown (on multiple installations) that it is faster to pass lists than references--although it appears faster to *return* references.
Axeman
The warnings are a better reason than dubious performance claims, but if you're generating a slice of the arguments at run-time anyway it doesn't actually protect you all that much.
fennec
+4  A: 

The foo(a => 1, b => 2) style is the usual way of emulating named arguments. The bar({a => 1, b => 2}) is usually used only for supplementary (and possibly optional) arguments.

For typical usage, I prefer the first form. The {} are extra typing, extra noise to read, and create a possible error if you leave out either or both braces. Any performance difference is negligible. (If it's not, you have bigger problems.) On the other hand, wrapping the arguments in an anonymous hash constructor can help you find errors at compile-time rather than runtime.

The second form is typically seen mixed with positional arguments. e.g. Benchmark does this:

cmpthese(10000, {
    foo => \&foo,
    bar => \&bar,
});

While Tk leaves the {} out:

my $text = $w->Scrolled('Text', -width => 80, -height => 50);

It's usually a stylistic choice.

Michael Carman