Re: Caller, caller, wherefore art thou, caller?
by jdporter (Paladin) on Oct 10, 2005 at 16:19 UTC
|
Well, considering that your problem here would be no different if you were using perl's intrinsic hashes, I don't see any reason to jump through hoops.
That is, if you were simply doing
my %hash = @array;
while ( my($k,$v) = each %hash ) {
last if some_condition($k,$v);
}
you'd still have a dangling iterator, which you'd need to reset with an explicit call to keys %hash.
Since your hash object's semantics seem to be so similar to perl's, I'd go all the way and make them exactly the same, on the principle of least surprise.
(That being said, I don't know why you don't wrap your object in a tied class...)
| [reply] [d/l] [select] |
|
|
For my needs, assigning the array to a hash fails because I need ordered pairs. Further, unless there is a very unusual need, I do not tie variables. If I am passing it around, programmers will see what looks like an ordinary hash and may be surprised when it doesn't do what they expect. By using a class, it's clear to the maintenance programmer that something else is going on.
My code also allows objects as keys and has insert_before and insert_after functionality. In short, there's a number of other things which makes it preferable for my needs when a tied hash just doesn't quite get there.
| [reply] |
Re: Caller, caller, wherefore art thou, caller?
by adrianh (Chancellor) on Oct 10, 2005 at 17:05 UTC
|
"I'll just mark my position with caller and if each() isn't called from the same spot, automatically reset the index."
Not addressing the question directly - but I'd not like this behaviour even if you could do it. Consider the case when you go back to the same location without having accessed the hash in between. Would it really make sense for it to carry on from the same position?
Personally I'd abstract out a separate iterator so you would do something like:
my $array = Array::AsHash->new({array => \@array});
{
my $next_key_value = $array->each;
while ( my ($key, $value) = $next_key_value->() ) {
print "$key : $value\n";
last if some_condition($key, $value);
}
}
| [reply] [d/l] |
|
|
Of course the current implementation already is an iterator – if you need several, you can just instantiate any number of Array::AsHash objects on the same underlying array.
If Ovid prefers this, he could facilitate it by providing a ->copy constructor which resets the iterator in the new instance, for use by code which has no access to the original underlying array.
However, assuming the class does not contain more methods, I wonder why an object is at all necessary for this. I’d just bake closure directly:
my $each = make_array_iterator \@array;
while( my( $idx, $val ) = $each->() ) {
# ...
}
That seems far more natural to me.
Makeshifts last the longest. | [reply] [d/l] |
|
|
Of course the current implementation already is an iterator – if you need several, you can just instantiate any number of Array::AsHash objects on the same underlying array.
True, but when Ovid said (my emphasis):
I'm working on some code which lets me do, amongst other things
I assumed that iterators were only one part of what he wanted Array::AsHash to do, so it still might make good sense to abstract out the iterator part.
| [reply] |
Re: Caller, caller, wherefore art thou, caller?
by Roy Johnson (Monsignor) on Oct 10, 2005 at 16:07 UTC
|
Would your original conundrum be fixed by
while (my ($key, $value) = $array->each) {
last if some_condition($key, $value);
}
$array->reset_counter;
? Seems like good functionality for the object to have.
Caution: Contents may have been coded under pressure.
| [reply] [d/l] |
|
|
| [reply] |
|
|
I don't see what you are trying to gain. Can you show an
example of how this is to be used?
Be well,
rir
| [reply] |
|
|
Re: Caller, caller, wherefore art thou, caller?
by demerphq (Chancellor) on Oct 10, 2005 at 16:40 UTC
|
The problem lies in the following:
while (my ($key, $value) = $array->each) {
last if some_condition($key, $value);
}
If I exit the loop early, the counter doesn't get reset. "Fine", I thought. "I'll just mark my position with caller and if each() isn't called from the same spot, automatically reset the index."
This is normal behaviour. It happens with hashes as well, and has tripped up many a person who use each() in loops without resetting the iterator before they start. Ie:
#!perl -l
my %hash=(1..10);
sub printit {
print join "\t",keys %hash;
}
while (my ($key, $value) = each %hash) {
print "First: $key $value";
last if $value>5;
}
while (my ($key, $value) = each %hash) {
print "Second: $key $value";
#printit;
}
__END__
First: 1 2
First: 3 4
First: 7 8
Second: 9 10
Second: 5 6
Unfortunately the documented way to reset the iterator is via perlfunc:keys or perlfunc:values, so you probably should special case the behavior of keys in void context to reset yours. At least thats what i do, as well as providing a method to reset.
But it should be clear, to be safe you need to reset the iterator before you start the loop and not after you have finished it as I have seen mentioned in another reply. In fact, each can be viewed as unreliable if any other code can operate on the item being iterated in the period between iterations. IE, if you use each() and you call a sub that can call keys or each() itself on the item being traversed expect an infinite loop as your iterator gets reset every time. (Uncomment the 'printit' line for an example... :-)
---
$world=~s/war/peace/g
| [reply] [d/l] [select] |
Re: Caller, caller, wherefore art thou, caller?
by BrowserUk (Patriarch) on Oct 10, 2005 at 16:21 UTC
|
Use keys %hash; or values %hash;, to reset the iterator.
I seem to recall that this was recently optimised in a void context to reset the iterator without building a list?
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
| [reply] [d/l] [select] |
Re: Caller, caller, wherefore art thou, caller?
by Zaxo (Archbishop) on Oct 10, 2005 at 16:27 UTC
|
It's not what you want, but an ordinary hash behaves the same way:
my %h;
@h{a..d}=1..4;
while (my ($k,$v) = each %h) { last }
while (my $k = each %h) { print $k }
__END__
abd
The each call for the print loop only finds three keys. You have to manually reset the each iterator if you do not read all pairs or keys. That's typically done with keys %h; in void context.
| [reply] [d/l] [select] |
Re: Caller, caller, wherefore art thou, caller?
by japhy (Canon) on Oct 10, 2005 at 16:27 UTC
|
The problem is that, after the first iteration of the while loop, the next time the condition is tested is the final line of the while loop's block. This is seems very weird and counter-intuitive to me. I can't see an easy way around this right now.
| [reply] |
Re: Caller, caller, wherefore art thou, caller?
by pg (Canon) on Oct 10, 2005 at 16:43 UTC
|
perlfunc says:
"When the hash is entirely read, a null array is returned in list context (which when assigned produces a false (0) value), and undef in scalar context. The next call to each after that will start iterating again. There is a single iterator for each hash, shared by all each, keys, and values function calls in the program; it can be reset by reading all the elements from the hash, or by evaluating keys HASH or values HASH."
If run the following program, see how each "each" continue from the last one. (The 4th print get a warning which indicates the end of the iteration). The 5th restart from the begining:
use strict;
use warnings;
my %hash = ('a' => 1, 'b' => 2, 'c' => 3);
my $key = (each(%hash))[0];
print $key;
$key = (each(%hash))[0];
print $key;
$key = (each(%hash))[0];
print $key;
$key = (each(%hash))[0];
print $key;
$key = (each(%hash))[0];
print $key;
The following code resets the iteration with a keys(), see how the second pair returns the same results as the first pair:
use strict;
use warnings;
my %hash = ('a' => 1, 'b' => 2, 'c' => 3);
my $key = (each(%hash))[0];
print $key;
$key = (each(%hash))[0];
print $key;
keys(%hash);
$key = (each(%hash))[0];
print $key;
$key = (each(%hash))[0];
print $key;
| [reply] [d/l] [select] |