Re: Optimization, side-effects and wrong code
by dragonchild (Archbishop) on Sep 29, 2004 at 18:20 UTC
|
I KNEW I'd read something about this before. p383 in Programming Perl, 3rd ed.
By calling keys in a scalar context, we reset its internal state to ensure that the next each used in the return statement will get the first key.
sub find_stuff {
my $self = shift;
scalar keys %$self;
while (my $k = each %$self) {
return $k if ($self->{$k}->{blablah});
}
return undef;
}
Being right, does not endow the right to be rude; politeness costs nothing. Being unknowing, is not the same as being stupid. Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence. Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.
I shouldn't have to say this, but any code, unless otherwise stated, is untested
| [reply] [d/l] |
|
Thanks for the tip.
I guess it should be printed in bold in each's manpage.
ESC[78;89;13p ESC[110;121;13p
| [reply] |
|
| [reply] |
|
Re: Optimization, side-effects and wrong code
by diotalevi (Canon) on Sep 29, 2004 at 18:23 UTC
|
You were supposed to use keys in scalar or void context to reset the pointer. For normal hashes this is an extremely quick operation as it is simply fetches a C int for the result and resets the iterator.
| [reply] |
Re: Optimization, side-effects and wrong code
by Limbic~Region (Chancellor) on Sep 29, 2004 at 18:30 UTC
|
cbraga,
Have you considered using keys or values in scalar context to reset the iterator? I have heard that this is extremely fast in comparison to list context.
Update: I hate that I don't see how the thread has been modified since I started replying when I click preview. While I was trying to track down in the documentation where I had heard this, dragonchild went and posted it. *Shrug* - I guess it confirms I remember correctly ;-)
| [reply] |
Re: Optimization, side-effects and wrong code
by Zaxo (Archbishop) on Sep 29, 2004 at 18:09 UTC
|
Another solution is to abandon each on account of its side effects and say,
sub find_stuff {
my $self = shift;
$self->{$_}->{'blablah'}
and return $_ for keys %$self;
return;
}
Update: Ok, here's another,
sub find_stuff {
my ($self, $k) = shift;
while (defined($k = each %$self)) {
last if $self->{$k}->{'blablah'}
}
1 while each %$self;
$k
}
You can't avoid going through all the keys one way or another.
| [reply] [d/l] [select] |
|
That was cbraga's original solution and he abandoned it because keys builds the whole list prior to usage. each doesn't do that.
Being right, does not endow the right to be rude; politeness costs nothing. Being unknowing, is not the same as being stupid. Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence. Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.
I shouldn't have to say this, but any code, unless otherwise stated, is untested
| [reply] |
|
keys builds the whole list prior to usage
I thought I remember reading somewhere that keys has an optimization in this case, and the whole list is not actually built up front. I couldn't find any documentation, so I decided to do a few tests. Perhaps my test code is flawed in some way I don't see, but the results seem to agree with my premise.
Here are my snippets:
Here's a table with the results:
Update: I should have mentioned earlier, these are from perl 5.005_03 on FreeBSD 4.9-STABLE. I got the value from the VSZ column in ps aux output.
The change from snippet a to b is much larger than the change from a to c. This seems consistent with the optimization I thought should occur. There may be overhead in the array @keys, but I wouldn't think it would be so much more than building an equivalent list. Maybe someone can explain this another way?
| [reply] [d/l] [select] |
|
Re: Optimization, side-effects and wrong code
by dragonchild (Archbishop) on Sep 29, 2004 at 18:12 UTC
|
my $x = { 1 .. 40000 };
for (1 .. 3 )
{
my $k;
while ($k = each %$x)
{
last;
}
%$x = %$x;
print "$k\n";
}
The assignment back to itself will reset the pointer. But, that doesn't help you very much.
</nitpick>
Being right, does not endow the right to be rude; politeness costs nothing. Being unknowing, is not the same as being stupid. Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence. Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.
I shouldn't have to say this, but any code, unless otherwise stated, is untested
| [reply] [d/l] |
Re: Optimization, side-effects and wrong code
by eric256 (Parson) on Sep 29, 2004 at 22:31 UTC
|
As an alternate solution you could make a linked list out of your hash. Each element has a _next => $next link. Then you can have multiple traversals and none interfere with each other. I know that may not be a usable solution for the current project but in the future if you need to be able to search threw the large list multiple times, you could build the linked list out a regular hash, then traverse it at will and reset your 'pointers' to the beginning when needed. Of course if this is a set that you are doing adds ane removes from then you have to do all the associated work with cleaning up the list. There is quite possibly a modules that ties a hash to a linked list to give you hash lookups and fast searchs. The reseting of each still has global consequences if you are using each somewhere else on the same hash.
| [reply] |
Re: Optimization, side-effects and wrong code
by bpphillips (Friar) on Sep 30, 2004 at 12:56 UTC
|
you could change your while loop to do:
while(my $k = each %$self || each %$self){
return $k if(exists $self->{$k}->{blahblah});
}
and avoid the keys %$self thing altogether. In my 5 minutes of playing with it, it appears to work
Update:
benizi pointed out the loop becomes endless if $self->{$k}->{blahblah} doesn't return a true value. This is solved by changing it to use exists | [reply] [d/l] [select] |
|
perl -Mstrict -lw
my %self;
$self{$_}{blahblah} = 0 for qw/1 2 3/;
while (my $k = each %self || each %self) {
print "testing:$k";
print "true for $k" and last if $self{$k}{blahblah};
}
__OUTPUT__
testing:1
testing:3
testing:2
testing:1
testing:3
testing:2
testing:1
... etc. ...
| [reply] [d/l] |
|
agreed, I've added exists which causes it to work correctly (albeit a bit different test than the OP's). Could maybe even add a check to make sure our while loop doesn't run past scalar(keys %$self) but then we're back to using keys again... :-)
| [reply] [d/l] [select] |