Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

Re^7: Why does each() always re-evaluate its argument? ("for_list" )

by LanX (Saint)
on Dec 07, 2023 at 20:20 UTC ( [id://11156171]=note: print w/replies, xml ) Need Help??


in reply to Re^6: Why does each() always re-evaluate its argument? ("for_list" )
in thread Why does each() always re-evaluate its argument?

> fetched up front.

I'm confused, you just explained

> > 1) There's no copying, 2) it uses the built-in iterator.

Did you correct yourself or am I misunderstanding you?

FWIW: I'm installing 5.38.2 for testing via Perlbrew and it takes hours...

Cheers Rolf
(addicted to the Perl Programming Language :)
see Wikisyntax for the Monastery

  • Comment on Re^7: Why does each() always re-evaluate its argument? ("for_list" )

Replies are listed 'Best First'.
Re^8: Why does each() always re-evaluate its argument? ("for_list" )
by ikegami (Patriarch) on Dec 07, 2023 at 20:27 UTC

    It uses the built-in iterator to obtain the keys and values from the hash and push them unto the stack before any looping is performed.

    If you want me to be more precise, no extra copying occurs.

    • The keys are copied (i.e. fresh scalars are made for the keys) since they're not found as scalars in the hash. But that has to happen no matter the interface.
    • The pointers to the scalars placed on the stack. But that has to happen no matter the interface.
    $ perl -e' use v5.14; use experimental qw( for_list ); my %h = ( a => 4, b => 6 ); my $i = 0; for my ( $k, $v ) ( %h ) { if ( !$i++ ) { ++$h{a}; ++$h{b}; } say "$k:$v"; } ' a:5 b:7
      • The pointers to the scalars placed on the stack. But that has to happen no matter the interface.

      I believe LanX's performance concern is whether a hash with 100,000 elements must extend the perl stack to 200,000 elements before beginning the loop. It's not a guaranteed implementation detail since, as I recall, for (1..100_000) is optimized to use a counter and repeatedly modify a scalar rather than generating 100_000 distinct scalars at once.

      So I think I'm hearing that yes it does need to allocate the perl stack to 200_000 elements, and does not perform some fancy optimized use of 'each'.

      Also I would not say that "it uses the built-in iterator" unless it picks up from the current position of the internal iterator, rather than from the start of the hash. @foo= %bar always gives you the full contents of %bar, but while (my @x= each %bar) { push @foo, @x; } could omit elements if a previous 'each' iteration left off in the middle.

        > unless it picks up from the current position of the internal iterator, rather than from the start of the hash

        That's not desirable and the reason why I was speculating about "localizing" the iterator, i.e. saving the state and restoring it after leaving the loop.

        This would allow to nest two for loops over the same hash. That's not possible while using each and this "global" effect is constantly using headaches when passing around hashes.

        But ... I can't even tell if such a localization can be implemented in a reasonable way.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        see Wikisyntax for the Monastery

        I believe LanX's performance concern is whether a hash with 100,000 elements must extend the perl stack to 200,000 elements before beginning the loop.

        Extending the stack by one element or by 200,000 has the same fixed performance cost.

        It's cheaper in fact, because it avoids 99,999 checks to see if the stack needs to be extended.

        Also I would not say that "it uses the built-in iterator" unless it picks up from the current position of the internal iterator

        By your logic, keys and values don't use the iterator.

        my %h = ( a => 5, b => 6, c => 7 ); each %h; say for keys( %h );
        b c a

        That's wrong. %h in list context, keys and values all use the iterator. They just reset it first. You can see this by the fact that it's not left in its previous state.

        my %h = ( a => 5, b => 6, c => 7 ); each %h; while ( my $k = each( %h ) ) { say $k; } say "--"; each %h; my @a = %h; while ( my $k = each( %h ) ) { say $k; }

        If the iterator wasn't used, you'd see the same output twice. But because the iterator is used, you see something like

        c a -- b c a

        In fact, keys and values are specifically documented to reset the iterator, and they're specifically document to do this and nothing else in void context. (I don't know if %h in list context is specifcally documented to reset the iterator.)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11156171]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others exploiting the Monastery: (3)
As of 2024-04-20 02:40 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found