Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Perl Idioms Explained - my ($foo, $bar) = @{shift(@_)}{qw/ -foo -bar /}

by Roger (Parson)
on Mar 01, 2004 at 13:26 UTC ( [id://332886]=perlmeditation: print w/replies, xml ) Need Help??

Today someone asked about what the following code is doing:
my ($foo, $bar) = @{shift(@_)}{qw/ -foo -bar /};

I thought it's a good idea to add the explanation to the above idiom to the tutorial. (Although tye probably thinks that this is a bad idiom. :-)

What the above code is doing:
shift(@_) # get an element from the parameter list qw/ -foo -bar / # creates a list of two strings: "-foo" and "-bar" @{ shift(@_) }{ qw/ -foo -bar / } # dereference the anonymous hash reference as a hash slice # returns a list containing the values of the given keys # in the anonymous hash, in the form of: # # @values = @hash{@keys} my ($foo, $bar) = @{ shift(@_) }{ qw/ -foo -bar / }; # assign the two element list retrieved into $foo and $bar

This technique can be used to retrieve named parameters in a subroutine. For example:
foobar({ -foo => 'FOO', -bar => 'BAR' }); sub foobar { my ($foo, $bar) = @{shift(@_)}{qw/ -foo -bar /}; print "\$foo => $foo, \$bar => $bar\n"; }

Which I think is pretty handy.

Replies are listed 'Best First'.
Re: Perl Idioms Explained - my ($foo, $bar) = @{shift(@_)}{qw/ -foo -bar /}
by hardburn (Abbot) on Mar 01, 2004 at 14:33 UTC

    That's nice for a short parameter list, but it could get bluky for large lists (like the HTML::Template constructor). I prefer something like this:

    { my @PARAMS = qw( foo bar baz ); sub new { my $class = shift; my $in = shift; my $self = { map { $_ => $in->{$_} || '' } @PARAMS }; bless $self, $class; } }

    The above is more self-documenting, because all the parameters taken are listed above the subroutine and can be easily broken into multiple lines. It also doesn't mix the parameter definitions with other code.

    ----
    : () { :|:& };:

    Note: All code is untested, unless otherwise stated

      What I don't like with default argument setups like yours is that they too often throw away a 0 argument. I find this quite disturbing, especially when I sometimes just want to set something to false but the routine does my $foo = shift; $self->{foo} = $foo if defined $foo and I gave a false (undefined) value. But I digress.

      To adress the real issue again, I'd like to provide some constructors I sometimes use. I'm not very consistant myself in this matter, and I'm lazy and strict from time to time.

      An alternative way to write yours, that I find more attractive is

      { my %SELF = map { $_ => '' } qw( foo bar baz ); sub new { my $self = bless { %SELF } => shift; @$self{keys %SELF) = @{+shift}{keys %SELF}; return $self; } }
      This still explicitly defines the parameters, yet honours false values. You can also easily set other default values by just extending the list assigned to %SELF.

      Another way I've used is

      { my %SELF = ( foo => 0, bar => "", baz => [], ); sub new { my $self = bless {} => shift; my %p = @_; croak("...") if keys %p > keys %SELF; # If given a bad key. $self->$_($p{$_}) foreach keys %p; # Lazy. Damn lazy. return $self; } }
      I can't say this is a routine I recommend for daily use, but it was a slick routine in its context.

      Yet another...

      sub new { my $self = bless { foo => 0, bar => "", baz => [], this_should_not_be_touched_here => {}, } => shift; my %p = @_; $self->{$_} = delete $p{$_} for grep exists $p{$_} => qw/ foo bar baz /; # Note, this list doesn't equal keys(%$self). croak("Unknown parameters: @{[keys %p]}") if %p; return $self; }
      The benefit with this is that you don't expose some elements that perhaps shouldn't be set in the constructor. The other is that you easily detect typos as they'll show up as unknown parameters.

      I really had no point besides the false value issue with this post. I just felt like elaborating and tossing around ideas. :-)

      ihb

        A zero and empty string are (mostly) the same thing as far as Perl is concerned. The only time I worry about ditching undef values is if my interface exlicitly states that undef is different from any other false value. Otherwise you're asking to get a lot of 'use of unintitilized value' warnings.

        BTW--I like your @$self{keys %SELF) = @{+shift}{keys %SELF}; example, but it deserves some comments for the poor maintance programmer, who may not have as much Perl-foo as you do.

        ----
        : () { :|:& };:

        Note: All code is untested, unless otherwise stated

Re: Perl Idioms Explained - my ($foo, $bar) = @{shift(@_)}{qw/ -foo -bar /}
by Aristotle (Chancellor) on Mar 01, 2004 at 20:14 UTC

    I have to agree with the others — this is far too convoluted and too seldomly useful to be a reasonable idiom. If you must, I'd at least replace the shift with a simple $_[0] so what's going on is just a little more obvious. I still don't like it one bit, though. If you don't have any positional parameters, why are you passing an anonymous hash? If you don't, it's easy to make the happenings blindingly obvious at the cost of one extra line:

    sub foobar { my %arg = @_; my ($foo, $bar) = @arg{qw/ -foo -bar /}; # .. }

    Even if you have positional parameters, an easy solution is to put them at the beginning of the list so that you can say something like

    my ($foo, $bar, %arg) = @_; my ($baz, $quux) = @arg{qw(baz quux)};

    The one case a hashref makes sense is in case of a method called on a hash-based class.

    But then your "idiom" means throwing away $self, which I'd consider a bad idea. Even if the particular method's code is sufficiently simple to allow for this, it makes a future change in requirements that requires a calling a method on $self more difficult.

    More importantly, I use my $self = shift; as a form of self documentation in the code. Helper non-method functions included in a package do not have this, so you can see at a glance what is a function and what is a method. And in that case this "idiom" is completely redundant:

    sub moo { my $self = shift; my ($foo, $bar) = @$self{qw(foo bar)}; # .. }

    Makeshifts last the longest.

      I would agree with you that this is not the best approach and I don't use it in my code anyway. Yesterday someone asked in the CB about what the following code is doing:
      my ($foo, $bar) = @{+shift}{qw/ -foo -bar/};

      I thought it might be a good idea to post it. It's not that obvious to the less experienced coders that the hash slice @values = @hash{@keys} is involved. There was a supersticious '+' in the original question as well, which I have removed.

Re: Perl Idioms Explained - my ($foo, $bar) = @{shift(@_)}{qw/ -foo -bar /}
by diotalevi (Canon) on Mar 01, 2004 at 17:51 UTC
    My first thought on reading just the title of this piece was "Gawd! Who says that!" and then I opened the node. Whose perl have you been reading that this looked like an idiom to you?

      I bet it was CGI.pm


      ---
      demerphq

        First they ignore you, then they laugh at you, then they fight you, then you win.
        -- Gandhi


Re: Perl Idioms Explained - my ($foo, $bar) = @{shift(@_)}{qw/ -foo -bar /}
by leriksen (Curate) on Mar 02, 2004 at 00:55 UTC
    This may be more useful as a parser for simple command line arguments if you call it with {@ARGV}

    But there are lots of better ways to do that - this a very lightweight way.

    I've recently (last 3-4 months or so) been using named parameter lists for almost all my library code and also using Params::Validate, so my idomatic (OO) code looks like

    package Chopper::Reed; use Params::Validate qw/validate_with/; sub psychotic { my $self = shift; my %params = validate_with(params => \@_, spec => { punch => 1, charm => {default => 'JOURNALIS +T', regex = qr/^[CON]/} +, mutilate => 0, }, ); $self->mutilate($params{$mutilate}) if $params{$mutilate}; ... } ... 1;
    Note: Chopper Reed is self confessed hitman and was sectioned as a self-mutilator whilst serving time in prison for murder. He has been released, is a successful media personality and author, and recently bought a brewery. He recently left his wife and farm in Tasmania because he "...didn't like the septic loo backing up."

    +++++++++++++++++
    #!/usr/bin/perl
    use warnings;use strict;use brain;

Re: Perl parser tortured (was: Perl Idioms Explained)
by dada (Chaplain) on Mar 02, 2004 at 16:12 UTC
    interestingly enough, I used something (partly, at least) similar in a recent post of mine.

    in my endless quest for Evil Things To Do, I was looking for a way to express this:

    sub deref { my $ref = shift; return $$ref; }
    in a more succint way. the first thing, of course, was to get rid of $ref, and so I wrote:
    sub deref { ${$_[0]} }
    but this is nothing sexy. so I thought, maybe I can get rid of $ref while still retaining the shift:
    sub deref { ${shift} }
    but this doesn't work, and it took me a bit (and the help of B::Deparse) to understand why.

    ${shift} is interpreted by the parser as a way to write $shift, and of course it returns nothing.

    so I realized that the Perl parser needs to be hinted to get shift as a keyword, and this can be done in (at least :-) 3 ways:

    sub deref { ${shift@_} } # too much explicit sub deref { ${+shift} } # supersticious? cfr. Roger sub deref { ${shift;} }
    the last one I find particularly surprising :-)

    cheers,
    Aldo

    King of Laziness, Wizard of Impatience, Lord of Hubris

      That will be fixed in Perl 6, which will distinguish
      ${shift} # always an expression in the middle
      from
      $«shift» # always a literal
      It's really quite amazing how much just adding one set of brackets to a language helps.

        It's really quite amazing how much just adding one set of brackets to a language helps.

        Yeah, but its just really annoying how long it takes the keyboard manufacturers to actually put the new brackets somewhere useful.

        IMO this is something you need to consider, and especially fromt he POV that keyboards vary quite a bit around the world. AltGr-7, AltGr-8, AltGr-9, Algr-10 are where the { [ ] } brackets live in germany which is already frustrating enough for someone that grew up in Canada. I dread having to type these new brackets. But no doubt you are already hip to this problem :-)


        ---
        demerphq

          First they ignore you, then they laugh at you, then they fight you, then you win.
          -- Gandhi


Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://332886]
Approved by broquaint
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chanting in the Monastery: (5)
As of 2024-03-19 06:25 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found