Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Global vs. local?

by jpavel (Sexton)
on May 28, 2009 at 09:14 UTC ( [id://766610]=perlquestion: print w/replies, xml ) Need Help??

jpavel has asked for the wisdom of the Perl Monks concerning the following question:

Hello all! I actually don't have a problem with my code; I am, quite literally, seeking perl wisdom. I've used a similar bit of code in two different scripts to modify the default sub functions associated with variable Win32::GUI components. My first bit of code:
$comboboxes[$accountCount] = $main->AddCombobox( -name => $accountName."ComboBox", -top => $top, -left => $left, -width => 145, -height => 150, -tabstop => 1, -style => WS_VISIBLE | 3 | WS_VSCROLL ); foreach my $x (0..$accountCount){ $SUB = $accounts[$x]."ComboBox_Change"; *$SUB = sub { ComboBox $x }; }
That works fine. Porting it to my second script failed, though... the variable $j below gets passed as a reference instead of the value. That is to say, if I declared $j globally, everytime my sub got called, it had the global value. If it was local to the sub function, no value was passed. So, I ended up doing this:
$uniqueWindow[$i] = $main->AddTextfield( -height => 200, -width => $w-30, -background => [255,255,255], -top => $top, -left => 10, -text => "", -name => $i."Textfield", -align => left, -readonly => 1, -multiline => 1, -autovscroll => 1, -vscroll => 1, ); foreach my $j (0..$i-1) { $SUB = $j."Textfield_MaxText"; print "-->Assigning sub function for $j\n"; # Really?! Do I have to do this?! switch ($j) { case 0 {*$SUB = sub { variableMaxText(0); };} case 1 {*$SUB = sub { variableMaxText(1); };} case 2 {*$SUB = sub { variableMaxText(2); };} case 3 {*$SUB = sub { variableMaxText(3); };} case 4 {*$SUB = sub { variableMaxText(4); };} case 5 {*$SUB = sub { variableMaxText(5); };} case 6 {*$SUB = sub { variableMaxText(6); };} case 7 {*$SUB = sub { variableMaxText(7); };} case 8 {*$SUB = sub { variableMaxText(8); };} case 9 {*$SUB = sub { variableMaxText(9); };} case 10 {*$SUB = sub { variableMaxText(10); };} case 11 {*$SUB = sub { variableMaxText(11); };} } }
I don't plan on ever having more then 12 textfields, so it's functional, but the fact that it isn't truly variable bothers me. :-) Maybe I'm just too "Type A" or something. A key difference is probably the context: code snippet #1 occurs in the "main" section of the code, before any user interaction (before Win32::GUI::Dialog(), if you're familiar with the module). The second snippet exists in a sub function and is called during the course of user interaction. Hope I explained that adequately. I'm eager for an explanation!

Replies are listed 'Best First'.
Re: Global vs. local?
by Corion (Patriarch) on May 28, 2009 at 09:24 UTC
    switch ($j) {

    Whoa! Don't.

    This code suggests to me that you're using Switch, which is known to introduce random and weird errors into your scripts. The first step is to remove everything related to Switch.

    After you've exorcised all usage of Switch from your script, your usage should basically work. Potentially, you need to declare another variable and store the index in it rather than the loop variable:

    foreach my $j (0..$i-1) { my $idx = $j; # make a copy my $SUB = "${idx}Textfield_MaxText"; *$SUB = sub { variableMaxText( $idx ); };
      Whoa! Don't.

      If you tell people what not to use, also tell them what to use instead ;-)

      here: perl 5.10 introduces given/when, see perl5100delta. Short example:

      use 5.010; given ($value) { when (1) { ... } when (2) { ... } default { die "Don't know what to do with '$_'" } }

      It's not needed here, because a closure is much better, but in general it's surely good to know.

        Thanks, I'll make that change!
      This did it! Thanks. I'm not entirely sure why this was necessary, when the first snippet worked taking $x from the for loop? I guess there is something about variable reference that I don't understand going on.

        I have no explanation for the failure. I tried to replicate your behavior with 5.8.8 and 5.10.0 but my tests worked in every case I tried. My last test program was:

        #use strict; use warnings; sub subfunction { foreach my $j (0..5) { my $SUB = 'testsub'.$j; *$SUB = sub { display($j); }; } } sub display { my $x = shift; print "\$x = $x\n"; } subfunction(); &testsub0(); &testsub1(); &testsub2(); &testsub3(); &testsub4(); &testsub5(); __END__ $x = 0 $x = 1 $x = 2 $x = 3 $x = 4 $x = 5

        Can you post a minimal running test script that demonstrates the bug unexpected behavior?

        What version of perl and what platform?

Re: Global vs. local?
by moritz (Cardinal) on May 28, 2009 at 09:22 UTC
    You can replace your entire switch/case with this code:
    *$SUB = sub { variableMaxText($j); };

    And you're done. This will create a closure, ie a subroutine that's "closed" over $j.

      This is what I initially tried - makes sense to me. In the context of this sub function, "variableMaxText" always gets called with no value getting passed. If I declare $j globally, then the last value of $j gets passed every time (e.g., if I have 5 of them, the value is always "4" no matter which GUI element makes the call).
        See Corion's reply below: if you make a copy of that variable first (with my, not local, it should work.
Re: Global vs. local?
by shmem (Chancellor) on May 28, 2009 at 11:55 UTC
    foreach my $j (0..$i-1) { $SUB = $j."Textfield_MaxText"; ... *$SUB = sub { variableMaxText(0); };

    This will create a typeglob with an illegal subroutine name - sub names may not start with a digit. You can do that, but the sub can not be called directly by name:

    $SUB = "1Foo"; *$SUB = sub {print "$SUB!\n"}; 1Foo(); Bareword found where operator expected at -e line 3, near "1Foo" (Missing operator before Foo?)

    To call that weird sub, you'd have to do

    $SUB = "1Foo"; *$SUB = sub {print "$SUB!\n"}; &{"1Foo"}; __END__ 1Foo!

      the sub can not be called directly by name

      The subroutine is not called directly, but rather dispatched by the Win32::GUI event model. Ignoring that there are better ways to handle event definitions altogether, that's an argument for doing precisely what jpavel does. That way the Win32::GUI event handlers won't accidentally collide with any "regular" subroutines in the program.

      lodin

        The subroutine is not called directly, but rather dispatched by the Win32::GUI event model.

        How is the sub accessed by the event handler?

        Ignoring that there are better ways to handle event definitions altogether, that's an argument for doing precisely what jpavel does.

        No, it's not. Polluting the name space with funny things is not a good idea. I don't know about Win32::GUI, but I guess it uses coderefs rather than typeglobs. If it doesn't, then I hope for never needing to look at that module.

Re: Global vs. local? (Win32::GUI NEM)
by lodin (Hermit) on May 28, 2009 at 12:34 UTC

    This isn't a reply to what you asked for, but the old event model is really tiresome. One ends up generating a bunch of global (named) subroutines just as you do. So now that you have the closure working and found enlightenment, perhaps you should see if you can use the new event model (NEM). It's not that well documented I believe, but basically your subroutines sub NAME_EVENT { ... } can be replaced with -onEVENT => sub { ... } when you create the component called NAME.

    I don't know which components support the new event model or which events for those components that have a NEM callback.

    lodin

      First I've heard of the NEM. That's definitely more inline with how I'd like to handle this. Thanks all for the quick and enlightening responses!
      I've never used the Win32::GUI module. I tried to install it on ActiveState 5.10 but there is a bug in the doc installation so it is hard for me to read about it.

      Anyway what in the heck is this type glob stuff about? Why? All my GUI's so far with Perl use Tk. I can run my stuff on Windows, MAC, Linux,... so I haven't fiddled with platform specific Windows things like this, but I am curious about this type glob stuff...it just looks weird to me. Very weird. There is obviously something very basic about Win32::GUI that I don't understand. Why can't this thing do what other GUI's for Perl do?

        He used the globs to dynamically generate named subs.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://766610]
Approved by Corion
Front-paged by targetsmart
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (5)
As of 2024-04-16 13:34 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found