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 - II

 



rpaskudniak
User


Oct 30, 2014, 7:41 AM

Post #1 of 5 (2152 views)
Hash slice syntax - II Can't Post

Hi Folks (capitalized yet! Angelic)
I again express my gratitude to Bill and Laurent for their assistance on the first part of this quixotic quest for the perfect Perl-ish syntax (see http://perlguru.com/gforum.cgi?post=80088;sb=post_latest_reply;so=ASC;forum_view=forum_view_expandable;#80088) I had promised (threatened? Wink) another part to this syntax issue.

Using the syntax I learned from Laurent, I figured I would replace this straightforward (however, tedious) code - adding up data in accumulators - with more consolidated code. Thus, I replaced this:

Code
$dbspace_hash{$cur_dbspace}->{pg_reads}  # Add 6 entries from @iof_data 
+= $iof_data->[$gfd]->{pg_reads};
$dbspace_hash{$cur_dbspace}->{reads}
+= $iof_data->[$gfd]->{reads};
$dbspace_hash{$cur_dbspace}->{reads_avgtime}
+= $iof_data->[$gfd]->{reads_avgtime};
$dbspace_hash{$cur_dbspace}->{pg_writes}
+= $iof_data->[$gfd]->{pg_writes};
$dbspace_hash{$cur_dbspace}->{writes}
+= $iof_data->[$gfd]->{writes};
$dbspace_hash{$cur_dbspace}->{writes_avgtime}
+= $iof_data->[$gfd]->{writes_avgtime};
$dbspace_hash{$cur_dbspace}->{qlength} # Add 2 entries from @ioq_data
+= $ioq_data->[$gfd]->{qlength};
$dbspace_hash{$cur_dbspace}->{max_qlength}
+= $ioq_data->[$gfd]->{max_qlength};

with these two one-liners:

Code
@{$dbspace_hash{$cur_dbspace}}{qw(pg_reads  reads  reads_avgtime 
pg_writes writes writes_avgtime)}
+= @{$iof_data->[$gfd]}{qw(pg_reads reads reads_avgtime
pg_writes writes writes_avgtime)};
@{$dbspace_hash{$cur_dbspace}}{qw(qlength max_qlength)}
+= @{$ioq_data->[$gfd]}{qw(qlength max_qlength)};

(Hmm.. Kinda long for one-liners.. Wink)

Perl accepted the syntax with no objections. Home free! I naively thought. Alas, no such luck. Frown It actually only added up the last field in each set (the ones I underlined). That is: It added to accumulators only {writes_avgtime} and {max_qlength}, the last field named in each of the += additions. All other fields were left at 0. Ignored, forlorn,... (Whoops, I gotta stop listening to opera while I'm typing this in. Cool)

Clues, anyone?

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


rpaskudniak
User


Oct 30, 2014, 10:50 AM

Post #2 of 5 (2148 views)
Re: [rpaskudniak] Hash slice syntax - II [In reply to] Can't Post

OK, I withdraw the question on the grounds that it is based on a totally wrong impression. As far as I can [now] see you can't just add arrays element-by-element in one fell swoop. Observer the following little session in the debugger:

Code
  DB<3> @a1 = (1,2,3,4,5,6) 
DB<4> @a2 = (2,2,2,2,2,2)
DB<5> @a1 += @a2
Can't modify array dereference in addition (+) at (eval 15)[/usr/local/lib/perl5/5.8.7/perl5db.pl:628] line 2, at EOF

A peek in StackOverflow http://stackoverflow.com/questions/1865910/how-can-i-sum-arrays-element-wise-in-perl reminds me not shy away from looping.

Actually, I'm a bit surprised that you can't sum arrays in Perl. My first programming language, PL/1, (How many geezers out there remember that? Laugh) it was an elementary array operation.

In any case, this is the code I'm now using:

Code
my @iof_fields = qw(pg_reads  reads  reads_avgtime 
pg_writes writes writes_avgtime);
my @ioq_fields = qw(qlength max_qlength);
. . .
foreach $field (@iof_fields)
{ $dbspace_hash{$cur_dbspace}->{$field} += $iof_data->[$gfd]->{$field}; }
foreach $field (@ioq_fields)
{ $dbspace_hash{$cur_dbspace}->{$field} += $ioq_data->[$gfd]->{$field}; }

Had there been an array += array operation it would have been no more efficient than this. More elegant? Yes. More Perlish? Debatable. But this code works and gives me grief so it gets the nod.

I will consider this thread solved. On to other fish to broil. (I don't do much frying these days. Angelic)
--------------------
-- Rasputin Paskudniak (In perpetual pursuit of undomesticated, semi-aquatic avians)


Laurent_R
Veteran / Moderator

Oct 30, 2014, 3:19 PM

Post #3 of 5 (2140 views)
Re: [rpaskudniak] Hash slice syntax - II [In reply to] Can't Post

Hi Rasputin,
it is fairly easy to add to respective items from two arrays. For example:

Code
  DB<1> @a1 = (1,2,3,4,5,6) 

DB<2> @a2 = (2,2,2,2,2,2)

DB<3> @a3 = map { $_ + shift @a2} @a1;

DB<4> x @a3
0 3
1 4
2 5
3 6
4 7
5 8

Of course, this has the inconvenient of depopulating @a2, which may or may not be a problem, depending on whether you still need @a2.

Actually, in your example, you are changing @a1, so that you don't care about depopulating @a1 if it get populated to the new expected values. So you could have:

Code
  DB<1>  @a1 = (1,2,3,4,5,6) 

DB<2> @a2 = (2,2,2,2,2,2);

DB<3> @a1 = map { $_ + shift @a1} @a2;

DB<4> x @a1
0 3
1 4
2 5
3 6
4 7
5 8

Of course, the next step might be to put that in a function, and the further next step to put that function in a module.

Another way to look at it would be to make this much more generic.

Consider this approach. First, you write a combine function, for example something like this:

Code
sub combine (&\@\@) { 
my ($code_ref, $l1_ref, $l2_ref) = @_;
my @result;
my $i = 0;
while (1) {
local ($a, $b) = ($l1_ref->[$i], $l2_ref->[$i++]);
return @result unless defined $a and defined $b;
push @result, $code_ref->($a, $b);
return @result;
}
}


This function visits everay pair of items from the two lists and applies the code-ref to each such pair.

Then you can use the combine function to write a library of functions doing things with respective items of two lists or arrays. For example sums and averages:

Code
sub add2 (\@\@) { 
my ($l1, $l2) = @_;
return combine {$a + $b} @$l1, @$l2;
}

sub avg2(\@\@) {
my ($l1, $l2) = @_;
return combine {($a + $b) /2} @$l1, @$l2;
}


That works fine and you can write a module containing a full library of functions to act on respective items two lists,

Then, if you are like me a fan of Mark-Jason Dominus' Higher Order Perl (the book can be found (legally) in PDF format), you could go even one step further in the direction of functional programming in Perl.

I now add an intermediate function whose aim is to create functions:

Code
sub generate_function2 { 
my $code_ref = shift;
return sub {
my ($l1_ref, $l2_ref) = @_;
return combine {&$code_ref} @$l1_ref, @$l2_ref;
}
}

It could also be written a bit more concisely:

Code
sub generate_function2(&) { 
my $code_ref = shift;
return sub {
return combine {&$code_ref} @{$_[0]}, @{$_[1]};
}
}

or even more concisely:

Code
sub generate_function2 { 
my $code_ref = shift;
sub { combine {&$code_ref} @{$_[0]}, @{$_[1]};
}
}

Now I can write a code ref to add items of two lists:

Code
my $add2 = generate_function2(sub {$a + $b});

So far, it might seem that I am complicating the matter rather than simplifying it. But consider the ease with which I can now write a full library of functions:

Code
my $add2       = generate_function2( sub { $a + $b } ); 
my $avg2 = generate_function2( sub { ( $a + $b ) / 2 } );
my $substr2 = generate_function2( sub { $a - $b } );
my $diff2 = generate_function2( sub { abs( $a - $b ) } );
my $mult2 = generate_function2( sub { $a * $b } );
my $equal_nr = generate_function2( sub { $a if $a == $b } );
my $equal_str = generate_function2( sub { $a if $a eq $b } );
my $each_array = generate_function2( sub { $a, $b } );

# ...

You can now test this library with something like that:

Code
my @list1 = qw /1 32 5 7 98/; 
my @list2 = grep $_%2, 1..10;

print join " ", $add2->(\@list1, \@list2), "\n";
print join " ", $avg2->(\@list1, \@list2), "\n";
print join " ", $substr2->(\@list1, \@list2), "\n";
print join " ", $diff2->(\@list1, \@list2), "\n";
print join " ", $mult2->(\@list1, \@list2), "\n";
print join " ", $equal_nr->(\@list1, \@list2), "\n";
print join " ", $equal_str->(\@list1, \@list2), "\n";
print join " ", $each_array->(\@list1, \@list2), "\n";

Isn't it a great module to have in your utils?


BillKSmith
Veteran

Oct 30, 2014, 4:07 PM

Post #4 of 5 (2137 views)
Re: [rpaskudniak] Hash slice syntax - II [In reply to] Can't Post

Another neat solution to adding arrays is to use the function pairwise in module List::Moreutils. Refer to the example in the documentation.


UPDATE: My memory of PL/1 is one of error messages hiding in a forest of warnings!
Good Luck,
Bill

(This post was edited by BillKSmith on Oct 31, 2014, 10:54 AM)


Laurent_R
Veteran / Moderator

Oct 31, 2014, 12:12 AM

Post #5 of 5 (2133 views)
Re: [BillKSmith] Hash slice syntax - II [In reply to] Can't Post

Yeah, I meant to say it and finally forgot. The pairwise function of the List::MoreUtils module is doing almost exactly the same thing as my combine function above, except that it is probably written in C and therefore probably faster.

 
 


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

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