Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Improper use of global variable? (was: Global variables)

by Basilides (Friar)
on Jul 01, 2002 at 15:41 UTC ( [id://178585]=perlquestion: print w/replies, xml ) Need Help??

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

I frequently read nodes criticising global variables, and I think I might be guilty of using one, but I'm not sure:

I'm traversing a Binary Search Tree looking for a particular value, but there could be more than one node where it occurs...So, I set up an array in my main package, and pass a reference to it to the recursive traversal subroutine in the Tree object. This traverses the whole tree, and every time there's a match, pushes the current node to this array.

So that's it: one object is pushing values to an array in another (in real life the call isn't from main). Incidentally, it works. But I have a feeling I'm breaking some fundamental OO rules, or maybe this is an example of a ghastly global variable? I've even read nodes saying that I shouldn't be passing by reference at all, but I'm not too fussed about that.

edited: Mon Jul 1 18:48:44 2002 by jeffa - title change

Replies are listed 'Best First'.
Re: Global variables
by Abigail-II (Bishop) on Jul 01, 2002 at 16:19 UTC
    I'd consider that bad use of a global variable. Whether you are using OO or not. There are perfectly valid uses for globals, but to pass data in and out of functions isn't one of them.

    A few things to consider:

    • What if during you query an exception is thrown? Then everything that catches an exception has to take care of cleaning up the global.
    • What if you have a more complex operation, and a query might actually do another query on the tree? With a global, your algorithm wouldn't be "re-entrant".
    There's no good reason to want to use a global here, it's trivial in Perl to combine lists. Just use recursion and return a list of answers. Here's an example for doing a range query on an ordered binary tree:
    sub range_query { my ($node, $from, $to) = @_; return () unless $node; return range_query ($node -> left, $from, $to) if $to < $node - +> key; return range_query ($node -> right, $from, $to) if $from > $node - +> key; return range_query ($node -> left, $from, $to), $node -> key, range_query ($node -> right, $from, $to) }

    Abigail

Re: Global variables
by vladb (Vicar) on Jul 01, 2002 at 15:55 UTC
    There are a couple ways to fix this problem (at least from where I stand... err... sit ;):
    • Exactly who you do it now. Instead of using global array, pass array reference to each subroutine that may require this array or modify it in any way. I have done a similar thing in a couple of my scripts. For example,
      my @data; foo(\@data); bar(\@data); sub foo { my $data_aref = @_; # read from a file and # save temporary data in the data # array } sub bar { my $data_aref = @_; # process data array... }
    • Write a separate subroutine which will do just that: 'find one or more nodes in a binary tree'. Declare your array inside that subroutine (local) and use that array in your subsequent calls that do the actual job of searching the tree.


    But seriously, a sample of the code you wrote would help the discussion :).

    _____________________
    # Under Construction
Re: Global variables
by broquaint (Abbot) on Jul 01, 2002 at 15:49 UTC
    This traverses the whole tree, and every time there's a match, pushes the current node to this array.
    This sounds like a case for having the array as a property of the Tree object as it is a property which relates directly to the object. At the very least you could use a lexical over package variable as it isn't needed in your case.
    But I have a feeling I'm breaking some fundamental OO rules, or maybe this is an example of a ghastly global variable?
    I think if you're just writing a small program then it's alright to have somewhat messy OO as long as you're sure it's just a throwaway script. Much like using strict it's a good habit to get into applying proper OO structure to your programs consistently so that your skill remains honed and you're less likely to slip up in future.
    HTH

    _________
    broquaint

Re: Global variables
by demerphq (Chancellor) on Jul 01, 2002 at 17:13 UTC
    Well, lets say you dont want to use a global variable, suppose your binary tree is composed of hash elements with "left" and "right" keys (where left<=mid<right) you might try something like this:
    sub find_key_dupes { my $binary_tree=shift; my $key=shift; my $sub; my @dupes; $sub=sub { my $node=shift; return unless $node; if ($node->{key} lt $key) { $sub->($node->{right}); } else { push @dupes,$node if $node->{key} eq $key; $sub->($node->{left}); } }; $sub->($binary_tree); return \@dupes; }
    This is using closures to simulate the effect of a Pascal's nested subroutines. Its a nice trick for having lexically scoped vars acting as globals to a recursive subroutine. This avoids having to pass them as parameters. Also, my guess is that you could avoid recursion entirely and do this with one sub (without nesting) using iterative techniques, which would be faster but probably slightly more complicated code.

    Oh and I whipped that code out pretty fast, so its untested but you get the idea I hope. (And I hope its correct ;-)

    Yves / DeMerphq
    ---
    Writing a good benchmark isnt as easy as it might look.

      And here's a perfect situation to take advantage of Sub::Lexical's ability to emulate nested subs ...
      use Sub::Lexical; sub find_key_dupes { my $binary_tree=shift; my $key=shift; my @dupes; my sub get_dupes($) { my $node=shift; return unless $node; if ($node->{key} lt $key) { get_dupes($node->{right}); } else { push @dupes,$node if $node->{key} eq $key; get_dupes($node->{left}); } } get_dupes($binary_tree); return \@dupes; }
      Thanks demerphq, I'll be using this as valid example code in future. <simpsons voice="Mr. Burns">ehhhxcellent</simpsons>.
      HTH

      _________
      broquaint

Re: Global variables
by Aristotle (Chancellor) on Jul 01, 2002 at 16:27 UTC

    Look at your traversal routine. Does it use a variable that was defined outside itself? No. The referred array was defined elsewhere, but that is besides the point (and exactly the raison d'etre for references). Inside the function, you use a variable that holds a reference; this reference came from the function's parameters. No global here.

    An approach you might take to make this slightly cleaner would be to declare the actual tree traversal routine as private, and have a public function that declares an array, passes a reference to it and its parameters to the private function, and then returns the results from that array. But it is a question of circumstances and taste whether this is actually any real improvement.

    Update: I have to retract some of my statements I guess. Abigail made excellent points about exceptions and reentrance while I was writing this. ++

    Makeshifts last the longest.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (2)
As of 2024-04-25 19:10 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found