3

The 'perldoc -f each' say me that is not safe delete or add a value while iterating, except if the item is the most recently returned by each().

When I run this code snnipet:

my ($key,$value);

my %fruits = qw/ banana 1 apple 2 grape 3 /;

while ( $key = each %fruits ) {
    $fruits{$key} *= 7;
    print "$key = $fruits{$key}\n";
}

print "\nRead only\n\n";

while ( ($key,$value) = each %fruits ) {
    print "$key = $value\n";
}

everthing works fine!

But if I use a tied hash, humnn:

#-------------------------------------------------------------------------------
# select entries on database to print or to update.
sub select_urls {
    my ($dbPath,$match,$newValue) = @_;

    tie(my %tiedHash,'DB_File',$dbPath,O_RDWR|O_EXLOCK,0600,$DB_BTREE) || die("$program_name: $dbPath: $!\n");

    while ( my($key,$value) = each %tiedHash ) {
        if ( $key =~ $match ){
            if ( defined $newValue ) {
                $tiedHash{$key} = $newValue;
                ($key,$value) = each %tiedHash; # because 'each' come back 1 step when we update the entry
                print "Value changed --> $key = $value\n";
            } else {
                print "$key = $value\n";
            }
        }
    }

    untie(%tiedHash) ||  die("$program_name: $dbPath: $!\n");
}

was necessary a second call to each().

I have to 'perl -v':

$ perl -v

This is perl 5, version 12, subversion 2 (v5.12.2 (*)) built for amd64-openbsd (with 8 registered patches, see perl -V for more detail)

Copyright 1987-2010, Larry Wall ...

I thinking if it's a bug?!!

Perhaps much more things are behind the scenes...

I asking if my solution is correct???

Dinak
  • 31
  • 1
  • 3

1 Answers1

3

It's the addition or removal of elements (keys) that's the problem. There should be no issue changing the value. There's no inherent difference with tied hashes.

my ($key,$value);

use Tie::Hash;

tie my %fruits, 'Tie::StdHash';
%fruits = qw/ banana 1 apple 2 grape 3 /;

while ( $key = each %fruits ) {
    $fruits{$key} *= 7;
    print "$key = $fruits{$key}\n";
}

print "\nRead only\n\n";

while ( ($key,$value) = each %fruits ) {
    print "$key = $value\n";
}

Output:

banana = 7
apple = 14
grape = 21

Read only

banana = 7
apple = 14
grape = 21

Your second snippet does not demonstrate a bug. It doesn't demonstrate much of anything. It's not runnable, you didn't specify what it outputs, and you didn't specify what you expect it to output. But let's see if there's a problem with DB_File.

use DB_File qw( $DB_BTREE );
use Fcntl   qw( O_RDWR O_CREAT );  # I don't have O_EXLOCK

my ($key,$value);

tie(my %fruits, 'DB_File', '/tmp/fruits', O_RDWR|O_CREAT, 0600, $DB_BTREE)
   or die $!;
%fruits = qw/ banana 1 apple 2 grape 3 /;

while ( $key = each %fruits ) {
    $fruits{$key} *= 7;
    print "$key = $fruits{$key}\n";
}

print "\nRead only\n\n";

while ( ($key,$value) = each %fruits ) {
    print "$key = $value\n";
}

Nope.

apple = 14
banana = 7
grape = 21

Read only

apple = 14
banana = 7
grape = 21
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Sorry! Without the second call, the code runs indefinitely. I think that when it update the value the each's index step back by 1. Then the next call to each gets the same entry again. What I see is a repeated pairs of key,value being printed with the new value. – Dinak Oct 23 '12 at 17:57
  • The expected is a list of URLs that matches with the new value. (that I achieved using the second call to each). – Dinak Oct 23 '12 at 17:59
  • ikegammi, I runned your code and I got: apple = inf apple = inf apple = inf apple = inf apple = inf apple = inf – Dinak Oct 23 '12 at 18:10
  • then I runned 'perl each-ikegami.pl | head' and got: apple = 14 apple = 98 apple = 686 apple = 4802 apple = 33614 apple = 235298 apple = 1647086 apple = 11529602 apple = 80707214 apple = 564950498 – Dinak Oct 23 '12 at 18:13
  • Perl 5.16.0 (threaded), DB_File 1.826, Berkeley DB 4.8.30 (I think) – ikegami Oct 23 '12 at 18:25
  • Also works with 5.10.1 (not threaded), DB_File 1.827, Berkeley DB 4.8.30 (I think). It's not related to Perl version or if it's threaded. Try upgrading your DB_File and/or the underlying C library. – ikegami Oct 23 '12 at 18:29
  • Thanks for your comments Ikegami. At night I will test in my house (I can't change here at work). – Dinak Oct 23 '12 at 18:51
  • In my home Ikegami's examples works. What changed: "perl -v" = This is perl 5, version 14, subversion 2 (v5.14.2) built for x86_64-linux-gnu-thread-multi (with 53 registered patches, see perl -V for more detail) Copyright 1987-2011, Larry Wall – Dinak Oct 24 '12 at 01:05
  • Again, Perl version doesn't matter. I tried with 5 different version, some older than yours, some newer than yours. I even installed the version you used and tried it with that version. It's surely a difference in the version of DB_File or the underlying C library. – ikegami Oct 24 '12 at 01:59