简体   繁体   中英

How do I pass part of a hash to a subroutine?

How do I pass part of a hash to a subroutine?

%list = ( 1 => {name => 'first', quantity => 2},
  2 => {name => 'second', quantity => 3});
$i = 2;

#doesn't work....
check_something ( \%{list}{$i} );
sub check_something {
   %local = @_;
}

#doesn't work....
check_something ( \%list, $i );
sub check_something {
   my ($ref, $item) = @_
   %local = %{$ref}{$item};
}

Pass $list{$i} to subroutine,

use strict;

check_something ( $list{$i} );

sub check_something {
   my ($href) = @_;

   # $href->{name}, $href->{quantity}

   my %hash = %$href;
   # $hash{name}, $hash{quantity}
}

This works:

use strict;
use warnings;


my %list = ( 
    1 => {name => 'first', quantity => 2},
    2 => {name => 'second', quantity => 3}
);
my $i = 2;

check_something ( $list{$i} );

sub check_something {
   my $item = shift;
   #...
}

Whenever you are storing something more than scalar data into Perl variables, you should start thinking of object oriented Perl.

Take a look at the Perl Object Oriented Tutorial .

I'm going to go all object oriented on your here:

#! /usr/bin/env perl

use strict;
use warnings;

my %list;

my $list{1} = Widget->new("first", 2);
my $list{2} = Widget->new("second", 3);
my $i = "2";
check_something( $list{$i} );

Wow! It's obvious what I'm doing. I have a hash called %list that contains objects of Widgets! My check_somthing subroutine is taking a Widget object. It should be $list{$i} as the argument.

But what do Widgets look like:

package Widget;
sub new {
    my $class    = shift;
    my $self     = shift;
    my $name     = shift;
    my $quantity = shift;

    my $self = {};
    bless $self, $class;
    $self->Name($name) if defined $name;
    $self->Quantity($quantity) if defined $quantity;
    return $self;
}

sub name {
    my $self = shift;
    my $name = shift;

    if ( defined $name ) {
       $self->{name} = $name;
    }
    return $self->{name};
}

sub Quantity {
    my $self    = shift;
    my $quanity = shift;

    if ( defined $quanity ) {
       $self->{quantity} = $quantity;
    }
    return $self->{quantity};
}

That's pretty simple. By using objects instead of hashes of hashes, I've simplified my logic. It's easy to see that what I want to pass to my subroutine is a Widget object. In fact, the check_somthing subroutine could be another method:

sub check_something {
    my $widget = shift;

    my $name = $widget->Name;
    my $quanity = $widget->Quantity;

    # Here be dragons...

    return ???
}

Now, my code looks like this:

#! /usr/bin/env perl

use strict;
use warnings;

my %list;

my $list{1} = Widget->new("first", 2);
my $list{2} = Widget->new("second", 3);
my $i = "2";

$list{$i}->check_something;

Notice that this does many things that improve your code:

  • First it cleans up your logic. You're not passing a hash or hash of a list or some hard to understand structure that you probably wasted two to three hours trying to figure out before you gave up and asked a question here. (No crime. I've been there and done that myself). Instead, you're passing in an object that's easy to understand.
  • Second, it makes sure that your check_something subroutine can only be used by Widgets. If you try passing an non-Widget object to check_something , it will fail. Even better, I could have another object called Wooble with its own check_something subroutine, and Perl will figure out what I am really checking.

For example:

my $widget = Widget->new("first", 2);
my $wooble = Wooble->new(1, "slam");

$widget->check_something;
$wooble->check_something;

Widgets and Woobles have their own check_something method, and Perl, because it knows which object is a Widget and which object is a Wooble knows which check_something I should run.

  • Finally, use strict; is no longer broken. The use strict; pragma catches 90% of your errors. However, when you create these complex structures by hewing them out of raw code, you can end up creating the same time of errors that use strict; would catch but can't:

For example:

%list = (
    1 => {name => 'first', quantity => 2},
    2 => {name => 'second', quanity => 3}
);

Do you see the error? I used quantity in one, but then misspelled quanity in the other. This type of error won't be caught by the compiler. However:

$list{1} = Widget->new;
$list{1}->name('first');
$list{1}->quantity(2);

$list{2} = Widget->new;
$list{2}->name('second');
$list{2}->quanity(3);    #Compiler Error!

Now, the Perl compiler will catch my error because quanity isn't merely a key to a hash reference, but is a nonexistent subroutine.

Others have already given you the technically correct answer, but if you regularly use complex data structures like hashes of hashes or hashes of lists, or lists of hashes, it's time to start working with objects. Once you get use to them, you'll find you can code a lot faster with less frustration and fewer logic issues.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM