Re: A cleaner way of scoping variables
by Arunbear (Prior) on Aug 09, 2004 at 13:08 UTC
|
Use indentation to remove ugliness:
{
my $bar;
if ($foo) {
$bar = "true";
}
print $bar;
}
I suppose you could hide $bar in a subroutine:
sub test
{
my $arg = shift;
my $bar;
if($arg) {
$bar = "true";
}
return $bar;
}
my $foo = 1;
print test($foo);
But that's an extreme solution.
Update:
This approach eliminates the global and the curlies:
use strict;
my $foo = 1;
if ($foo) {
package Strange;
our $bar = "true";
}
print $Strange::bar || "false";
:-) | [reply] [d/l] [select] |
|
|
| [reply] |
|
|
Agreed; it was just a little brainstorming exercise.
| [reply] |
|
|
| [reply] |
Re: A cleaner way of scoping variables
by Aristotle (Chancellor) on Aug 09, 2004 at 13:15 UTC
|
There is no answer in the general case of how that naked block could be avoided, but I find that do can often help group things like this better. (In this example, you could use a ternary so the do isn't even necessary, but I'll humour you and assume it stands for a more complex piece of code.)
{
my $bar = do {
if( $foo ) { "true" } else { undef }
};
print $bar;
}
Makeshifts last the longest.
| [reply] [d/l] |
Re: A cleaner way of scoping variables
by adrianh (Chancellor) on Aug 09, 2004 at 13:13 UTC
|
my $bar = $foo ? "true" : undef; ? | [reply] [d/l] |
Re: A cleaner way of scoping variables
by ihb (Deacon) on Aug 09, 2004 at 13:13 UTC
|
Adding a bare block is a common technique to scope tighter. I don't understand the question really. The point of having the my inside the if block is so that $bar won't be visible outside of the block. Do you want another my-like keyword that makes a variable exist only "below", regardless of scope?
Is your question specifically about this kind of if blocks? If you, so you perhaps what to look at the ternary ?: operator which is an blockless if-else.
ihb
Read argumentation in its context!
| [reply] [d/l] [select] |
|
|
| [reply] |
Re: A cleaner way of scoping variables
by Chady (Priest) on Aug 09, 2004 at 13:26 UTC
|
If $bar is temporary, and your main worry is about ending up declaring a lot of globals for temporary usage, then consider changing your code.
I know that your example is simplified for the sake of posting, but nevertheless, in your example you can throw away $bar completely.
if ($foo) {
print "true";
}
But if you want to use the value a little farther below it probably means that you need that variable, and hence you should declare it, or maybe you should group together the parts of code that are working on the same temporary variable so that you can scope tightly.
just my 2 cents.
He who asks will be a fool for five minutes, but he who doesn't ask will remain a fool for life.
Chady | http://chady.net/
| [reply] [d/l] |
|
|
Another consideration along this lines and that of using subroutines is general code style/design.
If you break your code up more into subrotines, methods, modules, classes etc. you still have the problem though it is considerably less relevent.
i.e. if your whole code block (sub/method) is already naturally defined as 10-20 lines of code, then the fact $bar is scoped slightly longer than you want, hardly matters since it is going to go right out of scope a few lines later anyway when your subroutine/method ends.
I know my preference may vary from others, but when I get my code reduced to classes and a "driver" script that just says:
do_step_1
do_step_2
do_step_3
exit_gracefully
I am quite happy with it. Debugging generally very quickly gets you to 10-20 lines of code making it much easier to figure out. Then, if you are using global variables in this scenario, you deserve whatever you get...
| [reply] |
Re: A cleaner way of scoping variables
by Crackers2 (Parson) on Aug 09, 2004 at 15:50 UTC
|
But if $bar is still in scope after the if, how is this any different from just declaring it right before the block ?
In your example you use the extra braces to limit the scope of your variable to prevent it from being a global. If your "preferred" code would be allowed, where do you think $bar should go out of scope? i.e. :
if ($foo) {
my $bar = "true";
}
print $bar;
print "again" . $bar; #-- does this still work
<cut 500 lines>
print "and again" . $bar; #-- what about this?
If all those prints work, your code is essentially the same as when you just declare $bar before the if block. If not, at what point does $bar go out of scope ?
Another point: what happens in your code if the condition is false? Would you want a runtime error (because $bar is not declared in that case) ? Or are you thinking more of declaring $bar in both the if and else branch?
There's one possible case where I could see your suggestion being somewhat useful, namely something like this:
my $bar = "hello"; #-- global
sub mysub {
if (condition) {
my $bar = "goodbye";
}
print $bar;
}
i.e. where the sub-local variable would obscure an already-existing global. But this is both ugly and bad design.
So to summarize, my main question to you is: when would $bar go out of scope in the code you propose | [reply] [d/l] [select] |
|
|
If all those prints work, your code is essentially the same as when you just declare $bar before the if block.
Almost, but not quite. You could have code between the start of the if block and the $bar declaration that wasn't allowed to use $bar if using what the OP was asking for. I'm not saying that this is something I want, because as you say, it would be like a global except you can't use it above the statement. But I think that's the detail that the OP was thinking about.
ihb
Read argumentation in its context!
| [reply] [d/l] [select] |
|
|
| [reply] |
Re: A cleaner way of scoping variables
by ambrus (Abbot) on Aug 09, 2004 at 13:29 UTC
|
use strict;
my $foo = 1;
{
my $bar = do {
if ($foo)
{ "true"; }
};
print $bar;
}
You still need a pair of braces around the whole construct.
If you want $bar to be local,
you need a block, how else would perl know
where the variable is local.
The block is not ugly imo.
I often use blocks in perl just to declare local variables.
There is a way to avoid such blocks, but that involves
changing programming style:
you have to write short (50 lines most) subroutines
that do one thing and do it well,
and you can declare local variables at the top of each
subroutine, with one my() statement perl
sub (and preferable at most 7 variables).
This is a good programming style in C imo
(see Documentation/CodingStyle in the linux source),
but doesn't always worth in Perl.
This is a bit OT, but note that I write the curlies of a block
inline if the block contains only one line of code.
| [reply] [d/l] [select] |
|
|
$ perl -le'print do { "x" if 1 }'
x
$ perl -le'print do { "x" if 0 }'
0
In your case, the value of $foo is assigned to $bar if $foo is false. This could be particularly nasty because you'll never notice if you don't run tests with $foo being 0 or the empty string rather than undefined.
You must make sure that your do blocks always evaluate to an intended result. In this case, you have to add an else { undef } clause.
Makeshifts last the longest.
| [reply] [d/l] |
|
|
#!/usr/bin/perl -w
my $true = "foo";
my $false = "0";
my $dc = "bar";
warn do { $dc if $false };
warn do { $dc unless $true };
warn do { $dc while $false };
warn do { $dc until $true };
warn do { if ($false) { $dc } };
warn do { unless ($true) { $dc } };
warn do { while ($false) { $dc } };
warn do { until ($true) { $dc } };
warn do { $dc if "0" };
warn do { $dc unless "foo" };
warn do { $dc while "0" };
warn do { $dc until "foo" };
warn do { if ("0") { $dc } };
warn do { unless ("foo") { $dc } };
warn do { while ("0") { $dc } };
warn do { until ("foo") { $dc } };
__END__
prints:
Useless use of private variable in void context at a line 9.
Useless use of private variable in void context at a line 10.
Useless use of private variable in void context at a line 13.
Useless use of private variable in void context at a line 14.
Useless use of private variable in void context at a line 21.
Useless use of private variable in void context at a line 22.
0 at a line 7.
foo at a line 8.
0 at a line 9.
foo at a line 10.
0 at a line 11.
foo at a line 12.
0 at a line 13.
foo at a line 14.
0 at a line 15.
foo at a line 16.
0 at a line 17.
Warning: something's wrong at a line 18.
0 at a line 19.
Warning: something's wrong at a line 20.
Warning: something's wrong at a line 21.
Warning: something's wrong at a line 22.
| [reply] [d/l] [select] |
|
|
|
|
|
|
Re: A cleaner way of scoping variables
by tilly (Archbishop) on Aug 09, 2004 at 21:10 UTC
|
My solution is to start putting all code into functions early, and then break out subfunctions whenever it makes sense. That way random code is always in a reasonably small lexical scope, and I don't have to care much if a random lexical has a slightly larger scope than I'd ideally want. | [reply] |
|
|
| [reply] |
Re: A cleaner way of scoping variables
by davido (Cardinal) on Aug 09, 2004 at 15:55 UTC
|
my $foo = 1;
print +($foo)? "True" : "";
Or, if it's more involved than that...
my $foo = 1;
print do{
if( $foo ) {
"True"
}
}
In your last example, you asserted that it would be nice if $bar were accessible outside of the if(){} block. But you previously stated that you didn't want to use a global variable (in this case what you really meant was a lexical scoped to package level). The problem is that if $bar is accessible outside of the if(){} block, it's no different from a package-scoped lexical. You don't want your lexicals leaking outside of the block they're defined in; that defeats the purpose of a lexical variable.
| [reply] [d/l] [select] |
|
|
| [reply] |
Re: A cleaner way of scoping variables
by TomDLux (Vicar) on Aug 10, 2004 at 01:31 UTC
|
- If it's short, use the trailing if ...
my $bar = "true" if $foo;
- If it's short and you have two options, use the trinary operator ...
my $bar = $foo ? "Winner!!!" : "Please play again.";
- If it's a longer block, it doesn't matter if you have one declaration line at the front. You can even use default values if you want.
my ( $foo, $bar, $baz, $wumpus );
# or
# my ( $foo, $bar, $baz, $wumpus ) = ( 5555, "Blossom Garden Rd", "San
+ Jose, CA", 95123 );
if ( $tiddly_rump ) {
$foo ....
$bar ....
$baz ....
$wumpus ....
}
That's not so bad, is it?
--
TTTATCGGTCGTTATATAGATGTTTGCA
| [reply] [d/l] [select] |
|
|
| [reply] [d/l] |
|
|
So greppeth perlsyn:
NOTE: The behaviour of a my statement modified with a statement modifier conditional or loop construct (e.g. my $x if ...) is undefined. The value of the my variable may be undef, any previously assigned value, or possibly anything else. Don't rely on it. Future versions of perl might do something different from the version of perl you try it out on. Here be dragons.
| [reply] [d/l] [select] |