Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Re: Trouble getting size of list returned from sub

by tobyink (Canon)
on Nov 26, 2012 at 09:01 UTC ( [id://1005595]=note: print w/replies, xml ) Need Help??


in reply to Trouble getting size of list returned from sub

"Is there no way to avoid the temp variable?"

Kinda. It comes down to the difference between lists and arrays. Assigning an array to a scalar gives you the length of the array. Assigning a list to a scalar gives you the final item in the list.

That said, Perl allows arrays to be anonymous. So even if you need an array, you don't need to create a variable for it...

say scalar @{[ bad() ]};
perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'

Replies are listed 'Best First'.
Re^2: Trouble getting size of list returned from sub ("list")
by tye (Sage) on Nov 26, 2012 at 16:14 UTC
    It comes down to the difference between lists and arrays. [....] Assigning a list to a scalar gives you the final item in the list.

    Oh, yet another falls victim to this seductive lie. It seems to explain so many things after you first "learn" it. Then you start applying it in more and more places and either just start putting out rather nonsense explanations or start dreaming up elaborate schemes about "that isn't really a 'list'". And then you get an emotional attachment to it and start yelling at people for calling things "list" when clearly they mustn't do that because that thing doesn't behave like you think a "list" should in a scalar context. But in the end, it mostly just gets in the way of deeply and accurately understanding the behavior of complex (and some simple) Perl constructs.

    The simpler truth is that just about everything in Perl that can return a list can also decide exactly what it wants to return in a scalar context. There is no "list vs. array" dichotomy in Perl. An array returns its size, that much is true. A list literal (aka. "the comma operator") returns its "last 'item'", where the exact definition of "item" belies the conflation of "list" and "list literal".

    What qw// returns in a scalar context actually depends on your version of Perl. Some versions of Perl implement qw// as a list literal and so, in those versions of Perl, qw// in a scalar context returns its last item. Other versions of Perl don't and return something else for qw// in a scalar context.

    qw// is just another example of a few constructs in Perl where "what should we return in a scalar context?" nobody bothered to design and so what we got was an accident of implementation details (or even optimizations).

    It seems to me that putting qw// into a scalar context is most likely to indicate a bug and so I wouldn't be surprised if a future version of Perl simply makes that fatal (or just a warning).

    Indeed, it is common for many non-array things in Perl to return "the last 'item'" in a scalar context (where the definition of 'item' is somewhat slippery, if you are paying close attention). And it is common to talk about many such non-array things as being "a list". But not all "lists" in Perl return their last item in a scalar context. And arrays actually are lists.

    The real "difference between lists and arrays" is that an array is a list that is stored in a variable (named or anonymous) and so can persist longer and allow more operations (like pop). The answer to "What to return in scalar context?" is a much more fine-grained than "is it an array or not?", despite it not appearing so when you first start looking.

    - tye        

Re^2: Trouble getting size of list returned from sub
by wanna_code_perl (Friar) on Nov 26, 2012 at 09:28 UTC

    Aha! That makes sense. Thank you (and to the others who replied with similar suggestions). Now, if I do this, can you tell me anything about what's going on under the hood? I.e., is there a performance hit for shoving a 1000's-long list into an anonymous array, and then dereferencing that array to get its length? Or is Perl smart enough to do all of that with one copy of the original list in memory?

      This does, indeed, create a copy of the array. In fact, in my testing it used more memory than just creating an array variable. Surprisingly (to me, anyway) the temporary variable method was the most memory-efficient.

      However, any option you are going to use incurs a lot of overhead that could be avoided if the sub were written to handle this.

      Here is a script I used to play around with memory usage:

      use Modern::Perl; use Win32::OLE qw/in/; sub memory_usage() { my $objWMI = Win32::OLE->GetObject('winmgmts:\\\\.\\root\\cimv2'); my $processes = $objWMI->ExecQuery("select * from Win32_Process wh +ere ProcessId=$$"); foreach my $proc (in($processes)) { return $proc->{WorkingSetSize}; } } sub big_list {return ('blah')x1_000_000}; #Option 1: memory usage 104,443,904 my $size = scalar @{[big_list()]}; #Option 2: memory usage 99,958,784 #my @arr = big_list(); #my $size = scalar @arr; #Option 3: memory usage 108,441,600 #my $size = () = big_list(); #Comparison: memory usage 11,206,656 (results discarded when not used) +. #big_list(); #my $size = 1; #Can't get what you want, obviously. say "Size: $size"; say 'Memory usage: ', memory_usage(), "\n";
      If you are not on Windows, see this Stackoverflow question, whence I got the memory usage sub, and which also gives some non-Windows options.


      When's the last time you used duct tape on a duct? --Larry Wall

      Without wantarray perl doesn't seem to reduce memory usage

      sub mm { print( (`pslist -m $$ 2>NUL`)[-2,-1] )} sub ff { my @fudge = 1 .. 1_000_000; @fudge } sub fa { scalar @{[ &ff ]} } mm; ff; mm; warn fa; __END__ Name Pid VM WS Priv Priv Pk Faults Non +P Page perl 796 62168 46336 44336 48196 12572 +2 34 Name Pid VM WS Priv Priv Pk Faults Non +P Page perl 796 66076 50280 48252 48260 13582 +2 34 1000000 at - line 4.

      Using the accumulator doesn't appear to reduce memory usage, because one function still returns a list

      sub mm { print( (`pslist -m $$ 2>NUL`)[-2,-1] )} sub ff { my @fudge = 1 .. 1_000_000; @fudge } sub fa { scalar( () = &ff ) } mm; ff; mm; warn fa; __END__ Name Pid VM WS Priv Priv Pk Faults Non +P Page perl 1432 62168 46336 44336 48196 12572 +2 34 Name Pid VM WS Priv Priv Pk Faults Non +P Page perl 1432 66076 50280 48252 48260 13582 +2 34 1000000 at - line 4.

      I suppose this could actually be optimized, I see no technical reason, but its fairly minor

        Bah, I fudged it up

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (6)
As of 2024-04-18 02:47 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found