How do I interact with a Perl object with a hash attribute?

I have a class with several variables, one of which is a hash (_runs):

sub new { my ($class, $name) = @_; my $self = { _name => $name, ... _runs => (), _times => [], ... }; bless ($self, $class); return $self; } 

Now all I'm trying to do is create an accessor / mutator, as well as another routine that pushes new data into the hash. But I get damn time when all calls / dereferences / $ self calls work together. I burned my eyes: "I can’t use a string (" blah ") as a HASH ref, etc."

For an accessor, what is “best practice” for returning hashes? Which of these parameters should I use (if any) ?:

 return $self->{_runs}; return %{ $self->{_runs} }; return \$self->{_runs}; 

Also, when I use a hash inside other routines in the class, what syntax do I use to copy it?

 my @runs = $self->{_runs}; my @runs = %{ $self->{_runs} }; my @runs = $%{ $self->{_runs} }; my @runs = $$self->{_runs}; 

The same thing happens for iterating over the keys:

 foreach my $dt (keys $self->{_runs}) foreach my $dt (keys %{ $self->{_runs} }) 

What about actually adding data?

 $self->{_runs}{$dt} = $duration; %{ $self->{_runs} }{$dt} = $duration; $$self->{_runs}{$dt} = $duration; 

You understand. I read articles about using classes and articles about references and dereferences, but I can't get my brain to combine knowledge and use it at the same time. I got the _times array, which finally works, but imitating the hash array syntax did not work.

+4
object perl hash
source share
3 answers

You store array references or hashes in your object. To use them with standard features, you need to dereference them. For example:

 @{ $self->{_array_ref_key} }; %{ $self->{_hash_ref_key} }; 

If you need transfer parameters for a standard function:

 push( @{ $self->{_array_ref_key} }, $some_value ); for my $hash_key ( keys %{ $self->{_hash_ref_key} }) { $self->{_hash_ref_key}{$hash_key}; ## you can access hash value by reference } 

Also, the syntax $self->{_hash_ref_key}{$hash_key} is a shortcut to $self->{_hash_ref_key}->{$hash_key} (which may make sense if you see it for the first time).

Also see the corresponding manual page .

+6
source share

You can also take my comments and make the right answer. I will illustrate why your sample code failed.

 use warnings; my $self = { _name => $name, _runs => (), _times => [], }; bless ($self, $class); use Data::Dump::Streamer; DumpLex $self; __END__ Odd number of elements in anonymous hash at … $self = bless( { _name => undef, _runs => '_times', "ARRAY(0x88dcb8)" => undef, }, '…' ); 

All items in the list form key / value pairs for the hash, the link to which will be bless ed. () is an empty list, so you really express the list of '_name', $name, '_runs', '_times', [] . You can see that _times moves up to become a value, and the link [] gated as a hash key. You receive a warning because there is no value to it; this will automatically be bound to undef . (Always always include the warnings pragma.)

Now for part of the guts: the hash values ​​should be a scalar value. Arrays and hashes are not; but links to them. Thus:

 my $self = { _name => $name, _runs => {}, _times => [], }; 
+5
source share

First, you need to find out what you really want to return, and what you want the higher level to work with data.

If you want to return a copy of the data or any changes to the returned data, do not affect the copy of the object, you cannot make simple decisions that other answers will tell you, because they return small copies, which will still have common links. You need to make a deep copy and then return the disconnected data structure. Storable makes it easy to work with dclone :

  use Storable qw( dclone ); sub some_method { my( $self, ... ) = @_; ...; my $clone = dclone( $self->{_runs} ); $clone; } 

If you want a higher level to modify the object by changing the structure of the returned data, just return the link that you already store. You do not need to do anything for this:

  sub some_method { my( $self, ... ) = @_; ...; $self->{_runs}; } 

In addition, your task is to create an interface so that people cannot think about your data structure at a higher level. You encapsulate everything so that your implementation details are not shown. Thus, you can change the implementation without breaking the code of a higher level (as long as the interface is stable).

The runs method is created, which returns a list of runs:

  sub get_run_keys { my( $self ) = @_; keys %{ $self->{_runs} }; } 

Or maybe you just need the values:

  sub get_run_values { my( $self ) = @_; values %{ $self->{_runs} }; } 

Or maybe all:

  sub get_run_hash { my( $self ) = @_; $self->{_runs}; # subject to the cloning stuff I mentioned earlier } 

If you want to get values ​​for a specific run, you will access it using another method:

  sub get_run { my( $self, $key ) = @_; $self->{_runs}{$key}; } 

Setting the trigger value is similar to:

  sub set_run { my( $self, $key, $value ) = @_; $self->{_runs}{$key} = $value; } 

Now your higher level knows nothing about the infrastructure, and the method names describe what you are trying to do, and not how the infrastructure should do it:

  foreach my $key ( $self->get_run_keys ) { my $run = $self->get_run( $key ); ...; $self->set_run( $key, $new_value ); } 

Object oriented design is a big topic, and you can do a lot. This is enough to get you started. You can also wrap other operations:

  sub does_run_exist { my( $self, $key ) = @_; exists $self->{_runs}{$key}; } sub delete_runs { my( $self, @keys ) = @_; delete $self->{_runs}{$key} foreach my $keys ( @keys ); } sub reset_runs { my( $self, $key ) = @_; $self->{_runs} = {}; } 
+3
source share

All Articles