Skip Menu |

This queue is for tickets about the Var-Pairs CPAN distribution.

Report information
The Basics
Id: 102676
Status: resolved
Priority: 0/
Queue: Var-Pairs

People
Owner: Nobody in particular
Requestors: jep5m [...] virginia.edu
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: (no value)
Fixed in: (no value)



Subject: each_kv used on array-of-hashrefs
Date: Tue, 10 Mar 2015 12:39:45 -0400
To: bug-Var-Pairs [...] rt.cpan.org
From: John Pickard <jep5m [...] virginia.edu>
Hi, First, thank you for publishing Var::Pairs - very exciting. I think I may have identified an issue with using each_kv() as a drop-in replacement for the built-in each. Here's a test case, the first example is the way we are currently using each, then there's a version using the drop-in replacement. The drop-in replacement doesn't work as expected, it does not appear to iterate over the next hashref element in the arrayref I did use strict / use warnings and use Var::Pairs qw(each_kv); for all of these tests. ######## Current with each keyword, need to reset iterators with keys keyword my $test3 = [ { a => 1 }, { b => 2 }, { c => 3 }, ]; foreach my $hash ( @{$test3} ) { my ($key, $val) = each %$hash; keys %$hash; print "Got $key => $val\n"; } foreach my $hash ( @{$test3} ) { my ($key, $val) = each %$hash; keys %$hash; print "Got $key => $val\n"; } ######## Drop-in replacement: each_kv does not iterate over the 2nd hash element in $test3 my $test3 = [ { a => 1 }, { b => 2 }, { c => 3 }, ]; foreach my $hash ( @{$test3} ) { my ($key, $val) = each_kv %$hash; print "Got $key => $val\n"; } foreach my $hash ( @{$test3} ) { my ($key, $val) = each_kv %$hash; print "Got $key => $val\n"; } ########### END It looks like in the second loop through the first foreach, each_kv is still working on the first $hash in @$test3. Actually, I can verify that statement by adding a second key/value to the first hash in @$test: #### Add second key/value my $test3 = [ { a => 1, a1 => 3 }, { b => 2 }, { c => 3 }, ]; foreach my $hash ( @{$test3} ) { my ($key, $val) = each_kv %$hash; print "Got $key => $val\n"; } ### Output Got a => 1 Got a1 => 3 Use of uninitialized value $key in concatenation (.) or string at ... ### Output from using each and keys to reset the iterator: Got a => 1 Got b => 2 Got c => 3 Best, John
Subject: Re: [rt.cpan.org #102676] each_kv used on array-of-hashrefs
Date: Wed, 11 Mar 2015 10:25:19 +0100
To: bug-Var-Pairs [...] rt.cpan.org
From: Damian Conway <damian [...] conway.org>
Thanks for the report, John. This is in fact a documentation bug, as the module is working the way it's intended (i.e. each_kv() is not intended to be resettable by calling keys()...or by any other mechanism). I have updated the documentation to reflect this: In other words, C<each_kv()> is a drop-in replacement for Perl's built-in C<each()>, with two exceptions: one an advantage, the other a limitation. The advantage is that you can nest C<each_kv()> iterations over the same variable without shooting yourself in the foot. The limitation is that, unlike C<each()>, C<each_kv()> does not reset when you call the C<keys()> function on the hash you're iterating. C<each_kv()> acts like a true one-time only iterator (in the OO sense), so there is no way to reset its iteration (i.e. the way that calling C<keys()> on a hash or array, resets any C<each()> that is iterating it). If you need to reset partially iterated hashes or arrays, you will need to use some other mechanism to do so. I'm sorry that the module does not meet this need for you. I will look into whether I can change the implementation to support the behaviour (though clearly not via keys() in any case), but at the moment I don't see any obvious way to do so. The whole point is to isolate iteration from the hash/array being iterated, so being able to alter the iteration by modifying the variable is entirely against the module's fundamental concept. I do, however appreciate the feedback. All the best, Damian
Subject: Re: [rt.cpan.org #102676] each_kv used on array-of-hashrefs
Date: Wed, 11 Mar 2015 11:16:41 -0400
To: bug-Var-Pairs [...] rt.cpan.org
From: John Pickard <jep5m [...] virginia.edu>
Hi Damian, Thank you for clarification of how each_kv interacts with keys. I think we can work around our need to reset the iterator. However, I am still confused about the behavior in the following test. I would expect the foreach loop to print one key/value for each hashref in $test. However, the second time through the loop prints not a key/value from the second hash, but a second key/value from the first hash. my $test = [ { a => 1, a1 => 3 }, { b => 2 }, { c => 3 }, ]; foreach my $hash ( @$test ) { my ($key, $val) = each_kv %$hash; print "Got $key => $val\n"; } ########## OUTPUT Got a => 1 Got a1 => 3 Use of uninitialized value $key in concatenation (.) or string at ... Best, John On Wed, Mar 11, 2015 at 5:26 AM, damian@conway.org via RT < bug-Var-Pairs@rt.cpan.org> wrote: Show quoted text
> <URL: https://rt.cpan.org/Ticket/Display.html?id=102676 > > > Thanks for the report, John. > > This is in fact a documentation bug, as the module is working > the way it's intended (i.e. each_kv() is not intended to be > resettable by calling keys()...or by any other mechanism). > > I have updated the documentation to reflect this: > > In other words, C<each_kv()> is a drop-in replacement for Perl's > built-in C<each()>, with two exceptions: one an advantage, the other > a limitation. The advantage is that you can nest C<each_kv()> > iterations over the same variable without shooting yourself in the > foot. The limitation is that, unlike C<each()>, C<each_kv()> does > not reset when you call the C<keys()> function on the hash you're > iterating. > > C<each_kv()> acts like a true one-time only iterator (in the OO > sense), so there is no way to reset its iteration (i.e. the way that > calling C<keys()> on a hash or array, resets any C<each()> that is > iterating it). If you need to reset partially iterated hashes or > arrays, you will need to use some other mechanism to do so. > > I'm sorry that the module does not meet this need for you. I will look > into whether I can change the implementation to support the behaviour > (though clearly not via keys() in any case), but at the moment I don't see > any obvious way to do so. The whole point is to isolate iteration from the > hash/array being iterated, so being able to alter the iteration by > modifying > the variable is entirely against the module's fundamental concept. > > I do, however appreciate the feedback. > > All the best, > > Damian > >
Subject: Re: [rt.cpan.org #102676] each_kv used on array-of-hashrefs
Date: Wed, 11 Mar 2015 20:14:49 +0100
To: bug-Var-Pairs [...] rt.cpan.org
From: Damian Conway <damian [...] conway.org>
Hi John, Now that one really *is* a bug. I've just uploaded a new release of Var::Pairs (version 0.002002) that fixes the problem. Very much appreciated! Damian
Subject: Re: [rt.cpan.org #102676] each_kv used on array-of-hashrefs
Date: Mon, 16 Mar 2015 13:50:42 -0400
To: bug-Var-Pairs [...] rt.cpan.org
From: John Pickard <jep5m [...] virginia.edu>
Hi Damian, Thanks for the quick turn around on that! I'm sorry for continuing to dwell on this, but I'm still a little confused about how each_kv behaves. In the following example each_kv keeps its iteration state for each call to the get_one_kv sub. On the other hand, built-in each will create a new iterator for every call to get_one_kv. Since each_kv is lexically scoped iterator, I would've thought iteration over %hash would reset once the sub exits (not to mention each_kv is iterating over a lexically scoped hash for each call). sub get_one_kv { my %hash= ( a => 1, b => 2, c => 3 ); my ($key, $val) = each_kv(%hash); print "got $key => $val\n"; return ($key, $val); } get_one_kv_no_args(); get_one_kv_no_args(); get_one_kv_no_args(); ### each_kv output got c => 3 got a => 1 got b => 2 ### replace each_kv with each: output got c => 3 got c => 3 got c => 3 ####### I realize this is not the intended use of each_kv. I'm trying to more fully understand each_kv's behavior so that I can recommend to our development group that we use each_kv instead of each. So my tests are beyond our typical use cases. The behavior of each_kv is similar to built-in each when each_kv is used in a while loop: sub get_one_kv_while { my %hash= ( a => 1, b => 2, c => 3 ); while ( my ($key, $val) = each_kv(%hash) ) { print "got $key => $val\n"; return ($key, $val); } } get_one_kv_while(); get_one_kv_while(); get_one_kv_while(); #### output got c => 3 got c => 3 got c => 3 Best, John On Wed, Mar 11, 2015 at 3:15 PM, damian@conway.org via RT < bug-Var-Pairs@rt.cpan.org> wrote: Show quoted text
> <URL: https://rt.cpan.org/Ticket/Display.html?id=102676 > > > Hi John, > > Now that one really *is* a bug. > > I've just uploaded a new release of Var::Pairs (version 0.002002) > that fixes the problem. > > Very much appreciated! > > Damian > >
Subject: Re: [rt.cpan.org #102676] each_kv used on array-of-hashrefs
Date: Thu, 19 Mar 2015 20:31:32 +0100
To: bug-Var-Pairs [...] rt.cpan.org
From: Damian Conway <damian [...] conway.org>
Hi John, You asked about the behaviour of: Show quoted text
> sub get_one_kv { > my %hash= ( > a => 1, > b => 2, > c => 3 > ); > my ($key, $val) = each_kv(%hash); > print "got $key => $val\n"; > return ($key, $val); > }
The each_kv does not reset because in your example each call to get_one_kv recreates the %hash variable with the same memory address. Thus each_kv treats it as the same variable each time, and so continues iterating. But each time get_one_kv is called, the %hash is recreated with a reset internal hash iterator, so each() restarts each call. Which behaviour is "correct" depends on what behaviour you want. A given call to each_kv() when passed a variable with the same memory address continues to iterate the variable that was passed in the original call to each_kv(), even if the variable's contents or other state information have changed subsequently. That may not always be desirable, but it's hard to see how each_kv() can tell the difference between two variables of the same name at the same memory location (given that it doesn't have access to the internals of the hash). Hope this clarifies, Damian
Subject: Re: [rt.cpan.org #102676] each_kv used on array-of-hashrefs
Date: Tue, 24 Mar 2015 13:20:19 -0400
To: bug-Var-Pairs [...] rt.cpan.org
From: John Pickard <jep5m [...] virginia.edu>
Hi Damian, Thanks. This is helpful. On Thu, Mar 19, 2015 at 3:32 PM, damian@conway.org via RT < bug-Var-Pairs@rt.cpan.org> wrote: Show quoted text
> <URL: https://rt.cpan.org/Ticket/Display.html?id=102676 > > > Hi John, > > You asked about the behaviour of: >
> > sub get_one_kv { > > my %hash= ( > > a => 1, > > b => 2, > > c => 3 > > ); > > my ($key, $val) = each_kv(%hash); > > print "got $key => $val\n"; > > return ($key, $val); > > }
> > The each_kv does not reset because in your example > each call to get_one_kv recreates the %hash variable > with the same memory address. Thus each_kv treats it > as the same variable each time, and so continues iterating. > > But each time get_one_kv is called, the %hash is recreated > with a reset internal hash iterator, so each() restarts each call. > > Which behaviour is "correct" depends on what behaviour you > want. > > A given call to each_kv() when passed a variable with the same memory > address continues to iterate the variable that was passed in the > original call to each_kv(), even if the variable's contents or other > state information have changed subsequently. That may not always > be desirable, but it's hard to see how each_kv() can tell the difference > between two variables of the same name at the same memory location > (given that it doesn't have access to the internals of the hash). > > Hope this clarifies, > > Damian > >