Serendipitously, a few minutes later I was working on my own Perl script, and a minor change I made generated a warning which I thought might be of value to share. I know it's been said here before (and if you're a devout believer already, you know where I'm coming from), but I sincerely believe that the use of warnings and strict are the 2 best ways you can improve your programming!
First, here's the program (simplified to a tenth its original size):
When run, this generates pseudo-random delta changes to a ratio of values, displaying the new ratios when they are different, and at the end shows the starting and ending ratios as percentages:#!/usr/bin/perl -w # # Demonstrate the importance of "use warnings" and "use strict"! # 051004 liverpole # ############## ### Strict ### ############## use strict; use warnings; ################## ### Prototypes ### ################## sub ratio_change($$$$); sub random_change($); #################### ### Main program ### #################### my $start_value = my $new_value = 100; my $start_total = my $new_total = 1000; my $ratio0 = 100 * $start_value / $start_total; my $ratio1 = $ratio0; for (my $i = 0; $i < 100000; $i++) { if (ratio_change($start_value, $start_total, $new_value, $new_tota +l)) { $ratio1 = $ratio1 = 100 * $new_value / $new_total; } $start_value = $new_value; $start_total = $new_total; $new_value = random_change($start_value); $new_total = random_change($start_total); } printf "Start percentage ... %7.4f%%\n", $ratio0; printf " End percentage ... %7.4f%%\n", $ratio1; ################### ### Subroutines ### ################### sub random_change($) { my ($value) = @_; (rand(100) > 0.005) and return $value; # No change 99.995% of the + time return $value + int(rand(3)) - 1; # Make a change of -1, 0, +or +1 } sub ratio_change($$$$) { my ($value0, $total0, $value1, $total1) = @_; my $ratio0 = $value0 / $total0; my $ratio1 = $value1 / $total1; ($ratio0 == $ratio1) and return 0; # No change in ratio my $text = "Ratio Change: $ratio0 => $ratio1\n"; print STDERR $text; return 1; }
What happened next was that I decided to modify line 59 to show the percentages to only 4 decimal places, and in altering the line, didn't pay attention to the fact that it wasn't a sprintf statement. Thus I changed:Ratio Change: 0.1 => 0.101 Ratio Change: 0.101 => 0.100899100899101 Ratio Change: 0.100899100899101 => 0.101898101898102 Ratio Change: 0.101898101898102 => 0.100899100899101 Ratio Change: 0.100899100899101 => 0.0999000999000999 Ratio Change: 0.0999000999000999 => 0.0989010989010989 Ratio Change: 0.0989010989010989 => 0.0988023952095808 Start percentage ... 10.0000% End percentage ... 9.8802%
to:my $text = "Ratio Change: $ratio0 => $ratio1\n";
although what I really meant was:my $text = "Ratio Change: %7.4f => %7.4f\n", $ratio0, $ratio1;
and this gave me the following pair of duplicate warnings (since $ratio0 and $ratio1 aren't doing anything useful):my $text = sprintf "Ratio Change: %7.4f => %7.4f\n", $ratio0, $ra +tio1;
Now I could turn off "use warnings" (and I'd also have to remove the -w switch if I'm running it under Unix/Linux), and my apparent problems would go away, right?Useless use of private variable in void context at warn.pl line 59 +. Useless use of private variable in void context at warn.pl line 59 +.
Well, not so fast! It turns out that my output is not at all what I want either:
And if I'm disciplined enough to track down the cause of my warnings, by looking at line 59 and realizing what I really wanted was the sprintf statement, then I fix the underlying problem and get the desired output:Ratio Change: %7.4f => %7.4f Ratio Change: %7.4f => %7.4f Ratio Change: %7.4f => %7.4f Start percentage ... 10.0000% End percentage ... 9.9900%
As another example -- this one illustrating the importance of "use strict" -- try changing both occurrences of "my $ratio0" to "$ratio0", and both occurrences of "my $ratio1" to "$ratio1":Ratio Change: 0.1000 => 0.1010 Ratio Change: 0.1010 => 0.1011 Ratio Change: 0.1011 => 0.1012 Ratio Change: 0.1012 => 0.1013 Ratio Change: 0.1013 => 0.1012 Start percentage ... 10.0000% End percentage ... 10.1202%
Yikes, that's a lot of errors!Global symbol "$ratio0" requires explicit package name at ./warn.pl li +ne 26. Global symbol "$ratio1" requires explicit package name at ./warn.pl li +ne 27. Global symbol "$ratio0" requires explicit package name at ./warn.pl li +ne 27. Global symbol "$ratio1" requires explicit package name at ./warn.pl li +ne 30. Global symbol "$ratio1" requires explicit package name at ./warn.pl li +ne 30. Global symbol "$ratio0" requires explicit package name at ./warn.pl li +ne 37. Global symbol "$ratio1" requires explicit package name at ./warn.pl li +ne 38. Global symbol "$ratio0" requires explicit package name at ./warn.pl li +ne 54. Global symbol "$ratio1" requires explicit package name at ./warn.pl li +ne 55. Global symbol "$ratio0" requires explicit package name at ./warn.pl li +ne 57. Global symbol "$ratio1" requires explicit package name at ./warn.pl li +ne 57. Global symbol "$ratio0" requires explicit package name at ./warn.pl li +ne 59. Global symbol "$ratio1" requires explicit package name at ./warn.pl li +ne 59. Execution of ./warn.pl aborted due to compilation errors.
Now, you could comment out "use strict" (and I'm sure it would be tempting to the beginning programmer who just wants his code to ship on time), but is the output really correct now ...?
Not a bit. It might seem fine at first glance, but when you look carefully, you'll notice that both the start and end percentages are actually equal to the final ratio calculated by the subroutine ratio_change().Ratio Change: 0.1000 => 0.1001 Ratio Change: 0.1001 => 0.1000 Ratio Change: 0.1000 => 0.0990 Ratio Change: 0.0990 => 0.1000 Ratio Change: 0.1000 => 0.0990 Ratio Change: 0.0990 => 0.0991 Ratio Change: 0.0991 => 0.0992 Start percentage ... 0.0992% End percentage ... 0.0992%
That's because the variables ratio0 and ratio1 in the main program need to be treated as distinct from the ones in the subroutine ratio_change(), where they serve a different function. With "use strict" it's an error NOT to treat them that way. Without "use strict", you're asserting "I know better -- let me decide which variables I want to be global, and which I want to be lexically-scoped!" The problem is, if you don't get used to "my"-ing all your variables (even the ones at the top of the program, which will function like global variables to the rest of the file, so you have to be careful not to override them with non "my"-ed variables within blocks and subroutines), you'll eventually make a mistake you wish you hadn't. Trust me.
The moral is this -- I got used to "use warnings" and "use strict" from the beginning, and since I'm unwilling to sacrifice their use, I have to respect the complaints they generate. They have saved me time and time again from bugs that I can't even imagine, and all they ask in return is that you heed their advice as soon as you receive it. Once you're in the habit of using them, ALWAYS, they will do more at a fundamental level to improve the discipline of your programming than anything else I know of!
|
|---|