CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Intermediate:
Hash slice syntax

 



rpaskudniak
User


Oct 27, 2014, 11:06 AM

Post #1 of 8 (3400 views)
Hash slice syntax Can't Post

Greetings.

I have been merrily using hash slices since I found them early in an advanced book.
So it's been no problem for me to use expressions like:

Code
my $out_line = sprintf($chunk_format, 
@{$chunks->[$clc]}{qw(dbspace dbsnum pgsize chunk_num offset size free)});

So I found it distressing that there were some situation where I just could not get the syntax right. I'll go after three instances where I gave up and just used brute force, though in this first case a teeny bit of finesse. (I have trouble keeping it short so I'll only present one case at a time.)

For this case I had created a hash used as a structure to contain a number of accumulators. Hence, they all need to start off as 0. The hash itself is an anonymous hash referenced by a member of another hash. This is starting to read like gibberish so here it is:

Code
$dbspace_hash{$cur_dbspace}->{qw(pg_reads  reads  reads_avgtime pg_writes writes writes_avgtime qlength max_qlength)}

(Yes, I'm an Informix DBA, for those who recognize the field names.)

I tried a few things, the cleverest failure being:

Code
@{$dbspace_hash{$cur_dbspace}}->{qw(pg_reads  reads  reads_avgtime pg_writes writes writes_avgtime qlength max_qlength)} = map {0} 0..7;

BTW, since this is still a loop, I was able to step through it with the debugger. In all those iterations, Dumper(%dbspace_hash) never showed any change in the the hash and after $_ == 7 (the last entry) it gave me an error of:
Not an ARRAY reference at /nas/scripts/cio.pl line 134.
In an effort to get my script out there in a hurry I gave up and used this:

Code
my @fields = qw(pg_reads  reads  reads_avgtime pg_writes writes writes_avgtime  qlength max_qlength); 
foreach my $field (@fields)
{ $dbspace_hash{$cur_dbspace}->{$field} = 0; }

Pragmatic, and the script works great. Only I'm not satisfied with my code.

What would have been the most Perl-ish way to do this. (There are two more examples but let's take it one at a time.)

Thanks much, folks.
--------------------
-- Rasputin Paskudniak (In perpetual pursuit of undomesticated, semi-aquatic avians)


Laurent_R
Veteran / Moderator

Oct 27, 2014, 12:05 PM

Post #2 of 8 (3398 views)
Re: [rpaskudniak] Hash slice syntax [In reply to] Can't Post

If this is a series of counters or accumulators, you probably don't need initialization. Consider this example at the comand line:

Code
$ perl -e ' 
use strict;
use warnings;
use Data::Dumper;
my %hash;
$hash{one} ++;
$hash{two} += 2;
$hash{three} +=3;
print Dumper \%hash;
'
$VAR1 = {
'three' => 3,
'one' => 1,
'two' => 2
};

No warning, no error, it works just fine.


BillKSmith
Veteran

Oct 27, 2014, 2:17 PM

Post #3 of 8 (3395 views)
Re: [rpaskudniak] Hash slice syntax [In reply to] Can't Post

The arrow dotation for dereferencing makes easy things even easier. When things get complicated, the brace notation allows you say exactly what you mean.

Code
use strict; 
use warnings;
use Data::Dumper;
my @fields = qw(
pg_reads
reads
reads_avgtime
pg_writes
writes
writes_avgtime
qlength
max_qlength
);
my %dbspace_hash;
my $cur_dbspace = 'anything';
foreach my $field (@fields) {
$dbspace_hash{$cur_dbspace}->{$field} = 0;
}

my %new_dbspace_hash;
@{$new_dbspace_hash{$cur_dbspace}}{@fields} = (0) x 8;

print Dumper(\%dbspace_hash, \%new_dbspace_hash);


OUTPUT:

Code
$VAR1 = { 
'anything' => {
'qlength' => 0,
'writes_avgtime' => 0,
'max_qlength' => 0,
'reads' => 0,
'reads_avgtime' => 0,
'pg_reads' => 0,
'writes' => 0,
'pg_writes' => 0
}
};
$VAR2 = {
'anything' => {
'qlength' => 0,
'writes_avgtime' => 0,
'max_qlength' => 0,
'reads' => 0,
'reads_avgtime' => 0,
'pg_reads' => 0,
'writes' => 0,
'pg_writes' => 0
}
};

Good Luck,
Bill


Laurent_R
Veteran / Moderator

Oct 27, 2014, 2:41 PM

Post #4 of 8 (3394 views)
Re: [rpaskudniak] Hash slice syntax [In reply to] Can't Post

As I said earlier, you probably don't need to initialize your values, but if you really want the answer on the hash slices, try this (tested under the Perl debugger):


Code
  DB<1> @{$dbspace_hash{foo}}{qw / one two three/} = map 0, 0..2; 

DB<2> x \%dbspace_hash;
0 HASH(0x6005011b0)
'foo' => HASH(0x600501000)
'one' => 0
'three' => 0
'two' => 0
DB<3>


Update: When I made my first answer above, I was awaited by the family for dinner and did not have time to try something for the hash slices. Then, when I tried to test the hash slices, I had not seen that Bill had provided a quite similar answer and that there was probably no need to post the above.


(This post was edited by Laurent_R on Oct 27, 2014, 2:58 PM)


rpaskudniak
User


Oct 27, 2014, 11:49 PM

Post #5 of 8 (3381 views)
Re: [Laurent_R] Hash slice syntax [In reply to] Can't Post

Laurent,

Thanks, it does work as you described, much to my surprise, and makes the code that much neater. But only because my initial values were all 0. However, it still misses my point about a proper Perl syntax to accomplish that kind of initialization on a hash slice of the sort I had described.

Suppose that, for some reason (which I can't dream up at 2 AM Crazy) I had wanted to initialize them all to 1. Laurent's otherwise helpful (for this case) solution would not have helped. My primary interest was in getting that evasive successful syntax to initialize that hash to anything.

Again, thanks for the eye-opener.

On the other hand, BillKSmith's was the answer I'm looking for and am using, if only to have a code sample to refer back to. I just don't get it because in a previous line of code I used ->{} notation to initialize a field NOT to 0:

Code
$dbspace_hash{$cur_dbspace}->{dbsnum} = $cur_chunk->{dbsnum};

yet it was accorded the same status as the 8 fields that Bill's solution treats as direct members of the hash. The result is:

Code
DB<2> p Dumper(%dbspace_hash) 
$VAR1 = 'logsdbs';
$VAR2 = {
'qlength' => 0,
'writes_avgtime' => 0,
'max_qlength' => 0,
'reads' => 0,
'reads_avgtime' => 0,
'pg_reads' => 0,
'writes' => 0,
'dbsnum' => '2',
'pg_writes' => 0
};

So this will take some study on my part, perhaps a few more projects that call for it.

Much thanks. I'll see if this method works for my other examples before I post about those.
--------------------
-- Rasputin Paskudniak (In perpetual pursuit of undomesticated, semi-aquatic avians)


Laurent_R
Veteran / Moderator

Oct 28, 2014, 12:33 AM

Post #6 of 8 (3380 views)
Re: [rpaskudniak] Hash slice syntax [In reply to] Can't Post

Yes, using the ++ auto-increment or the +=, -=, ... operators automatically initializes the value to 0 if not other value was provided. Of course, this would not work if you wanted the values to be initialized to 1, but this is a much less common need (you might want that to give an initial value to a set of probabilities that you will then multiply several times by numbers smaller than 1 to reach a final probability value).

As for hash slices, I also gave you an example (it took me a good ten minutes to find the proper syntax), but Bill was faster than me on the subject for an almost identical syntax.

In your example:

Code
$dbspace_hash{$cur_dbspace}->{dbsnum} = $foo;

you could remove the array and have exactly the same result:

Code
$dbspace_hash{$cur_dbspace}{dbsnum} = $foo;

(well, at least I think so, I can't test right now), so the arrow is optional when placed between two braces or a square bracket and a brace. And, for some reasons, there are cases where the arrow is a nuisance.


Laurent_R
Veteran / Moderator

Oct 28, 2014, 2:28 AM

Post #7 of 8 (3378 views)
Re: [rpaskudniak] Hash slice syntax [In reply to] Can't Post

Maybe these tests under the Perl debugger will help:


Code
  DB<1>  @{$h{foo}}{qw/bar baz/}  = 1..2; 

DB<2> x \%h;
0 HASH(0x302ee218)
'foo' => HASH(0x30028ad8)
'bar' => 1
'baz' => 2
DB<3> p $h{foo}{bar};
1
DB<4> p $h{foo}->{bar}; # same
1
DB<5> %h = ();

DB<6> @{$h{foo}}->{qw/bar baz/} = 1..2;
Can't use an undefined value as a HASH reference at (eval 11)[/opt/freeware/lib/perl5/5.10.1/perl5db.pl:638] line 2.



BillKSmith
Veteran

Oct 28, 2014, 5:23 AM

Post #8 of 8 (3376 views)
Re: [rpaskudniak] Hash slice syntax [In reply to] Can't Post

The difference between the two cases is documented in perlref. See method 3 under "Using References",

Quote
3.Subroutine calls and lookups of individual array elements arise often enough that it gets cumbersome to use method 2. As a form of syntactic sugar, the examples for method 2 may be written:
$arrayref->[0] = "January"; # Array element
$hashref->{"KEY"} = "VALUE"; # Hash element
$coderef->(1,2,3); # Subroutine call


The key words are "individual array elements" (not slices).
Good Luck,
Bill

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives