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

Hello all, I'm a new Bioinformatics grad student, and I'm pretty new to perl programming, so if my code looks C'ish, forgive me: In a perl script I'm coding, I 'think' I'm having trouble with variable scope, in this code I wrote (all this is actually in a while loop not posted):
push( @next_cell_arr, $protein_matrix[$row][$col+1] ); push( @next_cell_arr, $protein_matrix[$row+1][$col+1] ); push( @next_cell_arr, $protein_matrix[$row+1][$col] ); # - debug #$arr_len = scalar @next_cell_arr; #for( $var = 0; $var < $arr_len; $var++ ){ # print $next_cell_arr[$var]; # print "\n"; #} #print "\n\n"; #exit; # - debug # - if all values are equal, then the sequence gets a match of + suffers a mismatch (diagonal move), and not a gap penalty. This als +o saves me from having to compute the max of the three cells if( ($next_cell_arr[0] == $next_cell_arr[1]) && ($next_cell_ar +r[1] == $next_cell_arr[2]) ){ push( @sequence_2_arr, $protein_matrix[0][$col+1] ); push( @sequence_1_arr, $protein_matrix[$row+1][0] ); $dowhat = "diagonal"; }
The debug code block in there lets me see the contents of the array I'm pushing data onto. I checked the contents in array, and they are loading correctly. The problem is in my bottom 'if' statement. When I try to utilize the values in the array within the 'if' statement, the Perl interpreter tells me: "Use of uninitialized value in numeric eq (==)," at this line. I'm not sure why, but this looks like I'm accessing the array out of scope. Does anyone know what's wrong?

Replies are listed 'Best First'.
Re: Understanding variable scope in perl
by kwaping (Priest) on Sep 29, 2005 at 15:42 UTC
    As a debugging tool, you might want to print the values of $protein_matrix that you're pushing to @next_cell_arr within your while loop. You can also use Data::Dumper::Simple and print Dumper(@next_cell_arr); after your while loop finishes.
Re: Understanding variable scope in perl
by liverpole (Monsignor) on Sep 29, 2005 at 17:44 UTC
    I didn't get any problem when I ran it this way:
    #!/usr/bin/perl -w + # Strict (always using these 2 lines will save you much grief!) use strict; use warnings; + # Global variables my @next_cell_arr; my @protein_matrix; my @sequence_1_arr; my @sequence_2_arr; my $row = 0; my $col = 0; my $dowhat = 0; + # Generate some test data $protein_matrix[$row][$col] = 1; $protein_matrix[$row][$col+1] = 2; $protein_matrix[$row+1][$col] = 3; $protein_matrix[$row+1][$col+1] = 4; + # BioBoy's code follows push( @next_cell_arr, $protein_matrix[$row][$col+1] ); push( @next_cell_arr, $protein_matrix[$row+1][$col+1] ); push( @next_cell_arr, $protein_matrix[$row+1][$col] ); + # - debug #$arr_len = scalar @next_cell_arr; #for( $var = 0; $var < $arr_len; $var++ ){ # print $next_cell_arr[$var]; # print "\n"; #} #print "\n\n"; #exit; # - debug + # - if all values are equal, then the sequence gets a match of suffers + a mismatch # (diagonal move), and not a gap penalty. This also saves me from hav +ing to # compute the max of the three cells if( ($next_cell_arr[0] == $next_cell_arr[1]) && ($next_cell_arr[1] == +$next_cell_arr[2]) ){ push( @sequence_2_arr, $protein_matrix[0][$col+1] ); push( @sequence_1_arr, $protein_matrix[$row+1][0] ); $dowhat = "diagonal"; }
    But if you're getting an error on such-and-such a line, I'd like to suggest you try putting debugging right near that line, and breaking it up into multiple statements, so you can see the intermediate values of interest:
    #TFD (Temporary for debug)>> my $cell0 = $next_cell_arr[0] || die "cell0 undef!\n"; printf "Cell0 = + %s\n", $cell0; my $cell1 = $next_cell_arr[1] || die "cell1 undef!\n"; printf "Cell1 = + %s\n", $cell1; my $cell2 = $next_cell_arr[2] || die "cell2 undef!\n"; printf "Cell2 = + %s\n", $cell2; if( ($cell0 == $cell1) && ($cell1 == $cell2) ){ print "TFD> All 3 cell values defined and EQUAL\n"; push( @sequence_2_arr, $protein_matrix[0][$col+1] ); push( @sequence_1_arr, $protein_matrix[$row+1][0] ); $dowhat = "diagonal"; } print "TFD> All 3 cell values defined, but NOT equal\n";
    (Note that I use the notation "TFD" to mean that a line or a sequence of lines is "temporary for debugging", and can be removed once I uncovered the cause of the problem).
      Thanks for your replys guys. Is it always a good idea to use 'my'? Also, what does use 'strict' allow you to do?
        This is a good node that will answer both of your questions. Use strict and warnings

        Here are my own takes on these subjects:

        Using "my" is generally a good idea. It sets the scope of the variable it's applied to to be localized instead of global. I'm sure others here can provide a much more detailed explanation of it. Basically, my recommendation is to use it whenever possible, except in times when you explicitly do not want to use it for a very good reason. "You have to know the rules in order to break them", that kind of thing.

        Regarding "strict", that's also a good thing to use whenever possible. Just like "my", only turn it off when you have a good reason to and you know exactly what you're doing. Strict forces you to write "better" code, in the sense of barewords, subs, variable scoping and the like. One of the things "strict" does is it forces you to scope all your variables using "my", "local", "our", or the package name.
        For an answer about what "strict" does, see the output of perldoc strict.

        Yes, I would say it's *almost* always a good idea to use "my".  It causes your variables to be lexically-scoped rather than globally-scoped, which can avoid many unintended bugs, and is considered an important programming practice.  (It's also one of the principles of object-oriented programming).

        If you get into the habit of using it religiously, along with "strict" and "warnings", you'll avoid many pitfalls on your path to Perl enlightenment.  I was lucky enough to be steered onto the path of using the "-w" switch, (eg. perl -w), from almost the first time I started using Perl (the -w switch has the same effect as "warnings", which go hand-in-hand with "strict"), and it's saved me from countless headaches!

Re: Understanding variable scope in perl
by husker (Chaplain) on Sep 29, 2005 at 16:36 UTC
    Never mind. I'm an idiot.

    Edit: this node got an upvote! Does that mean at least one person agrees that I'm an idiot? :)

Re: Understanding variable scope in perl
by BioBoy (Novice) on Sep 29, 2005 at 19:04 UTC
    Hey Guys. This is so frustrating. I added some code as suggested:
    push( @next_cell_arr, $protein_matrix[$row][$col+1] );# - note: 'right +' cell is first element of array push( @next_cell_arr, $protein_matrix[$row+1][$col+1] );# - no +te: 'diagonal' cell is second element of array push( @next_cell_arr, $protein_matrix[$row+1][$col] );# - note +: 'bottom' cell is third element of array # - debug $arr_len = scalar @next_cell_arr; for( $var = 0; $var < $arr_len; $var++ ){ print $next_cell_arr[$var]; print "\n"; } print "\n\n"; #exit; # - debug # - if all values are equal, then the sequence gets a match of + suffers a mismatch (diagonal move), and not a gap penalty. This als +o saves me from having to compute the max of the three cells my $val1 = $next_cell_arr[0] || die "val1 is undefined!!\n"; my $val2 = $next_cell_arr[1] || die "val2 is undefined!!\n"; my $val3 = $next_cell_arr[2] || die "val3 is undefined!!\n";
    When I run my code, $val1 is indeed not being defined, but the debug loop, right above, $next_cell_arr[0] is displaying the correct value. This makes absolutely no sense to me. Something so incredibly simple is not working here. EDIT: By the way, I always use the -w flag, but adding 'use warnings' might be easier
      Regarding this kind of logic:
      my $val1 = $next_cell_arr[0] || die "val1 is undefined!!\n";
      You need to understand a subtle difference between things that evaluate to false, and things that are "undefined". In a nutshell:
      use strict; my $val1; # this creates a named storage location for a scalar value, # but DOES NOT ASSIGN A VALUE -- its value is undefined. my $val2 = 0; # creates a named scalar and assigns a value, so it's d +efined # but the value will evaluate to FALSE in a # boolean sense my $val3 = ''; # similar to $val2: it's defined, it evaluates to false # in boolean tests, but in "reality", it's an # "empty string" (which $val2 is not) print "val3 is an empty string\n" if ( $val3 eq '' ); print "val2 is an empty string\n" if ( $val2 eq '' ); print "val1 is (like) an empty string\n" if ( $val1 eq ''); # this third test would generate a warning in "perl -w" my $i = 1; for ( $val1, $val2, $val3 ) { print "val$i evaluates to false\n" if ( ! $_ ); print "val$i is undefined\n" if ( ! defined( $_ )); $i++; }
      Now, bear in mind that the "||" operator applies a boolean test: the expression that follows it is evaluated only if the expression that precedes it evaluates to false. Do you have empty strings or zeros in your matrix cells?

      Based on the original statement of the problem, are you sure that the very first thing ever pushed onto your "@next_cell_arr" array is a defined value? Are you sure that the array is empty before you do the first of your three pushes from "@protein_matrix"?

        The answer to your questions are yes and yes. Just before I push values from my '@protein_matrix' to my '@next_cell_arr' matrix, I have debug code that shows all the values of my '@protein_matrix', becaue I did some computations in it before hand. When I didn't have the '|| die' code in there, perl threw errors stating that I'm trying to use uninitialized values.
      So, to follow the previous advice, what happens when you *also* put "use strict;" in your program?
        sorry, I just came from class. when I add 'use strict', perl throws a lot of compilation errors asserting that almost all of my global symbols require explicit package naming. I guess it means that I need to prefix everything with main::, no?
      Try changing that || die to or die. It has to do with operator precedence (perldoc perlop for more info).
        I know what you're saying, but I don't believe it matters here.   Try commenting out either Dying(3) or Dying(4) below...
        #!/usr/bin/perl -w # Strict use strict; use warnings; # Assign $x my $x = 123; # Next 2 lines prove $x is defined. defined $x or die "Dying(1) because (defined \$x or die)\n"; defined $x || die "Dying(2) because (defined \$x || die)\n"; # Make $x undefined undef $x; # Doesn't matter which of the next 2 lines is commented-out (either wo +rks fine) defined $x || die "Dying(3) because (defined \$x || die)\n"; defined $x or die "Dying(4) because (defined \$x or die)\n";
Re: Understanding variable scope in perl
by liverpole (Monsignor) on Sep 29, 2005 at 23:51 UTC
    So, BioBoy, here's what I propose you do ... take the entire program you've currently got, and whittle it down to as few a number of lines that you can which still exhibit the error.  Make sure you have use strict; and use warnings; at the top, of course.  In making the problem smaller, you may discover the root cause yourself.  But if not, at least it will attract the attention of more people willing to look at a managable task.

    Make sure that what you submit really is the entire program, though.  It won't do any good if you're making data assignments (or lack thereof) which aren't included.  Your end goal here should be for anyone to run the code you submit on their own system, and see the same problem.

    Good luck! ... and I'll be waiting to help you out more when you're ready ...

      Ahh, after looking at my code for an hour straight (literally, my eyes stayed on the screen on my PowerBook without moving), I found my elusive bug. Guess what it was? Yep, I had used an '<=" instead of a '<' in my while loop all this code is in, causing my code to overshoot my 'protein_matrix' and throw undefined errors. This little bug has caused me like 3 days........grrrrrr You guys are great though. Thanks. I've learned a lot of valuable debugging routes from this thread... By the way: Does perl have exception handling?

      **Special thanks to husker for introducing me to this online perl community**
        Ouch!!

        Now you see why you've got to show ALL the code, lest the words "(all this is actually in a while loop not posted)" come back to sting you!

        A final point -- don't ever submit code that you need reviewed by typing it in by hand (as I think you may have done to convert the loop variable to an actual value).  Always cut and paste!  Otherwise, even if the error is somewhere within the submission, you may "mentally fix the error" when you type it in, and nobody else will ever see it.

        Good luck!

        Howdy!

        Yep. Perl does exceptions.

        eval is the "try" part; checking $@ is the "catch". die is the "throw" bit.

        yours,
        Michael