Moving data structure around better than globals?

G

Gunnar Hjalmarsson

I have accepted that package globals as well as file scoped lexicals
should be avoided if possible, and hence a program I'm currently writing
does not include any globals. However, in a function main() i declare a
lexical reference $d to a data structure with things that are needed in
more than one subroutine. It includes things like

$d->{param} - hash ref to CGI parameters
$d->{user} - hash ref to certain data about the current user,
grabbed at authentication
$d->{error} - array ref to alerts to be displayed on screen

I'm passing $d around to just about all the functions, and it struck me
that this approach actually is very similar to using global variables.
So my question is: Why would this be better (if you think it is)?
 
M

Mothra

Gunnar said:
I have accepted that package globals as well as file scoped lexicals
should be avoided if possible, and hence a program I'm currently
writing does not include any globals.
(snipped)

I asked something similar in the past.

http://tinyurl.com/8bbel

Hope this helps

Mothra
 
T

Tad McClellan

Gunnar Hjalmarsson said:
I have accepted that package globals as well as file scoped lexicals
should be avoided if possible,


I'd say "if practicable" rather than "if possible".

It is the _abuse_ of global variables that is often warned against,
they do have their place.

My personal red flag is when there are "many" (over 10?) globals.
When I see that developing, I recast them to a single global hash.

$d->{param} - hash ref to CGI parameters
$d->{user} - hash ref to certain data about the current user,
grabbed at authentication
$d->{error} - array ref to alerts to be displayed on screen

I'm passing $d around to just about all the functions,


So then, it would hit my "impracticable" limit. :)

and it struck me
that this approach actually is very similar to using global variables.


I very often have a single global hash with "configuration"
type values in it.

So my question is: Why would this be better (if you think it is)?


I wouldn't say that passing the same arg in every function call
is better.

Judicious use of globals is good practice (it's just that the most
common uses are not judicious, so a knee-jerk "don't use globals"
is more often right than it is wrong).
 
T

Tad McClellan

Purl Gurl said:
Use globals. Doing so is more efficient and less error prone than
passing arguments to routines, over and over and over...


I agree!

(have we entered the Twilight Zone?)

Run some benchmark tests.


Oh shoot, some foolishness following the good advice.

Optimize for labor, optimize for labor, optimize for labor.

Benchmarking a few microseconds here and there is insignificant,
the big win is the "less error prone" part.
 
T

Tassilo v. Parseval

Also sprach Purl Gurl:
Perl core is very slooooooow.... Perl is well known for being
CPU intensive and very slow. As new versions of Perl are released,
core becomes more of a CPU hog, and more slow. There are many
turning away from Perl because of the useless bells and flashing
lights, which true programmers do not like.

Bummer that you didn't go away yet. Or aren't you a true programmer?

Tassilo
 
G

Gunnar Hjalmarsson

Tad said:
I'd say "if practicable" rather than "if possible".

It is the _abuse_ of global variables that is often warned against,
they do have their place.

Yeah, I took it to an extreme this time, contrary to my habit. ;-)
So then, it would hit my "impracticable" limit. :)


I very often have a single global hash with "configuration"
type values in it.


I wouldn't say that passing the same arg in every function call
is better.

Thanks for confirming my feeling.

My "impracticable" approach is well working, though, so I won't change
it now. But I'll keep this in mind for next time.
 
A

Anno Siegel

Tad McClellan said:
I'd say "if practicable" rather than "if possible".

It is the _abuse_ of global variables that is often warned against,
they do have their place.

My personal red flag is when there are "many" (over 10?) globals.

It isn't only the number of globals but also the complexity (or sheer
size) of the code that potentially uses (and changes) them. In a big
file a single file global can be one too many. It's the number of
variables times code size that should raise the red flag.
When I see that developing, I recast them to a single global hash.

I must admit I wonder what that really gains. The root of the evil
of globals is that they are fully exposed to all code that follows.
So is a global hash. So how is a global hash with n keys better than
n global variables? Serious question.

When I get worried about globals, I put them into one or (usually) more
bare blocks and add the necessary accessor routines. Other code must
go through those. That way I get real access restriction and can also
compartmentalize (is that really a word?) the mess of globals. The
bare blocks form lexical islands that can be designed like objects,
since they combine a set of data with a set of routines to manipulate
them.

Admittedly, the method is quite heavy on programmer time. But then,
the point when you have to do something about your globals is usually
a point where a program has outgrown its original framework and some
re-organization is due anyway. Organizing your globals into this
kind of poor man's objects can be part of the process.

Anno
 
G

Glenn Jackman

At 2005-10-28 09:35AM said:
When I get worried about globals, I put them into one or (usually) more
bare blocks and add the necessary accessor routines. Other code must
go through those. That way I get real access restriction and can also
compartmentalize (is that really a word?) the mess of globals. The
bare blocks form lexical islands that can be designed like objects,
since they combine a set of data with a set of routines to manipulate
them.

Would you mind posting a short example?
 
A

Anno Siegel

Glenn Jackman said:
Would you mind posting a short example?

Wanting to be somewhat realistic, and also to show a few variations of
the basic theme, the example I came up with isn't exactly short.

Suppose you are writing a program that takes user input (which may
come from a batch file) and produces graphics in response to commands --
something like interactive turtle graphics for instance. At some point,
a roundup of your global variables may yield this:

my $x_pos; # graphics cursor x
my $y_pos; # graphics cursor y
my $fg_color # foreground color for graphics
my $bg_color # background color for graphics
my %color_table # map color names to rgb colors

my $use_interactive; # is input from a terminal?
my $last_command; # last input line from user
my $last_reply; # last output to user

I have already sorted them in two blocks, one graphics-related and one
related to the interactive ("shell"-) functions. In a sufficiently
chaotic source they would come up in random order.

Looking over the graphics-related variables, you may decide that the
position variables are always used together, so you'll write combined
get- and set- accessors for x and y. Similarly, when a drawing
routine needs one of the foreground/background colors, it wants the other
too, so these get a combined get-accessor. The user wants to set the
colors independently, so they get individual set-accessors. That suggests
a design for the graphic status like this (untested code):

{ # graphics status
my ( $x_pos, $y_pos); # cursor

sub get_pos { return ( $x_pos, $y_pos) }

sub set_pos {
my ( $x, $y) = @_;
# check validity
( $x_pos, $y_pos) = ( $x, $y);
}

my ( $fg_color, $bg_color) # default foreground/background colors

sub set_fg_color { $fg_color = _encode_color( shift || 'black') }
sub set_bg_color { $bg_color = _encode_color( shift || 'white') }
sub get_colors { return ( $fg_color, $bg_color) }


my %color_table = (
white => 0xFF_FF_FF,
black => 0x00_00_00,
# etc
);

sub _encode_color {
my $name = shift;
return $color_table{ $name} if exists $color_table{ $name};
Carp::croak( "Invalid color name: '$name'");
}
}

That wraps a nice chunk of functionality into an identifiable package (umm,
lexical scope). Code that doesn't need to access the variables directly
goes outside:

sub shove_pos { # move position relative to current
my ( $dx, $dy) = @_;
my ( $x, $y) = get_pos();
set_pos( $x + $dx, $y + $dy);
}

Use similar considerations to corral the shell-related variables into
their lexical scope:

{ # shell status
my $use_interactive = -t STDIN; # no set-accessor, just initialize
sub is_interactive { return $use_interactive }

# more code for $last_command and $last reply
}

What I haven't shown are the necessary changes to the rest of the code
so that it goes through the accessors. The design of the accessor routines
should consider this step and make it easy, it is usually the hardest part.
In any case, the Perl compiler (under strict) will tell you if anything
anywhere still tries to access an ex-global. It's not a matter of test
coverage, you'll know when you're done.

Anno
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top