Search code examples
perlscoping

Scoping in Perl


As a biology student, I'm trying to extend my programming knowledge and I ran into a problem with Perl.

I'm trying to create a program that generates random DNA strings and performs analysis work on the generated data.

In the first part of the program, I am able to print out the strings stored in the array, but the second part I cannot retrieve all but one of the elements of the array.

Could this be part of the scoping rules of Perl?

#!usr/bin/perl

# generate a random DNA strings and print it to file specified by the user.

$largearray[0] = 0;

print "How many nucleotides for the string?\n";
$n      = <>;
$mylong = $n;

print "how many strings?\n";
$numstrings = <>;

# @largearray =();
$j = 0;

while ( $j < $numstrings ) {

    $numstring = '';    # start with the empty string;
    $dnastring = '';
    $i         = 0;

    while ( $i < $n ) {

        $numstring = int( rand( 4 ) ) . $numstring;    # generate a new random integer
                                                       # between 0 and 3, and concatenate
                                                       # it with the existing $numstring,
                                                       # assigning the result to $numstring.
        $i++;                                          # increase the value of $i by one.
    }

    $dnastring = $numstring;
    $dnastring =~ tr/0123/actg/;                       # translate the numbers to DNA characters.
                                                       #print $dnastring;
                                                       #print "\n";

    $largearray[j] = $dnastring;                       #append generated string to end of array

    #print $largearray[j];
    #print $j;
    #IN HERE THERE ARE GOOD ARRAY VALUES
    #print "\n";

    $j++;
}

# ii will be used to continuously take the next couple of strings from largearray
# for LCS matching.

$mytotal = 0;
$ii      = 0;

while ( $ii < $numstrings ) {

    $line = $largearray[ii];
    print $largearray[ii];    #CANNOT RETRIEVE ARRAY VALUES
    print "\n";

    $ii++;
    @string1 = split( //, $line );

    $line = $largearray[ii];

    #print $largearray[ii];
    #print "\n";
    $ii++;
    chomp $line;
    @string2 = split( //, $line );

    $n = @string1;    #assigning a list to a scalar just assigns the
                      #number of elements in the list to the scalar.
    $m = @string2;

    $v  = 1;
    $Cm = 0;
    $Im = 0;

    $V[0][0] = 0;     # Assign the 0,0 entry of the V matrix

    for ( $i = 1; $i <= $n; $i++ ) {    # Assign the column 0  values and print
                                        # String 1  See section 5.2 of Johnson
                                        # for loops
        $V[$i][0] = -$Im * $i;

    }

    for ( $j = 1; $j <= $m; $j++ ) {    # Assign the row 0 values and print String 2
        $V[0][$j] = -$Im * $j;

    }

    for ( $i = 1; $i <= $n; $i++ ) {    # follow the recurrences to fill in the V matrix.
        for ( $j = 1; $j <= $m; $j++ ) {

            #   print OUT "$string1[$i-1], $string2[$j-1]\n"; # This is here  for debugging purposes.

            if ( $string1[ $i - 1 ] eq $string2[ $j - 1 ] ) {
                $t = 1 * $v;
            }
            else {
                $t = -1 * $Cm;
            }

            $max = $V[ $i - 1 ][ $j - 1 ] + $t;

            #  print OUT "For $i, $j, t is $t \n"; # Another debugging line.
            if ( $max < $V[$i][ $j - 1 ] - 1 * $Im ) {
                $max = $V[$i][ $j - 1 ] - 1 * $Im;
            }

            if ( $V[ $i - 1 ][$j] - 1 * $Im > $max ) {
                $max = $V[ $i - 1 ][$j] - 1 * $Im;
            }

            $V[$i][$j] = $max;
        }
    }    #outer for loop

    print $V[$n][$m];
    $mytotal += $V[$n][$m];    # append current result to the grand total
    print "\n";

}    # end while loop

print "the average LCS value for length ", $mylong, " strings is: ";
print $mytotal/ $numstrings;

Solution

  • This isn't a scoping issue. You have declared none of your variables, which has the effect of implicitly making them all global and accessible everywhere in your code

    I reformatted your Perl program so that I could read it, and then added this to the top of your program

    use strict;
    use warnings 'all';
    

    which are essential in every Perl program you write

    Then I added

    no strict 'vars';
    

    which is a very bad idea, and lets you get away without declaring any variables

    The result is this

    Argument "ii" isn't numeric in array element at E:\Perl\source\dna.pl line 60.
    Argument "ii" isn't numeric in array element at E:\Perl\source\dna.pl line 61.
    Argument "ii" isn't numeric in array element at E:\Perl\source\dna.pl line 67.
    Argument "j" isn't numeric in array element at E:\Perl\source\dna.pl line 42.
    Bareword "ii" not allowed while "strict subs" in use at E:\Perl\source\dna.pl line 60.
    Bareword "ii" not allowed while "strict subs" in use at E:\Perl\source\dna.pl line 61.
    Bareword "ii" not allowed while "strict subs" in use at E:\Perl\source\dna.pl line 67.
    Bareword "j" not allowed while "strict subs" in use at E:\Perl\source\dna.pl line 42.
    Execution of E:\Perl\source\dna.pl aborted due to compilation errors.
    

    Line 42 (of my reformatted version) is

    $largearray[j] = $dnastring
    

    and lines 60, 61 and 67 are

    $line = $largearray[ii];
    print $largearray[ii];    #CANNOT RETRIEVE ARRAY VALUES
    

    and

    $line = $largearray[ii];
    

    You are using j and ii as array indexes. Those are Perl subroutine calls, not variables. Adding use strict would have stopped this from compiling unless you had also declared sub ii and sub j

    You might get away with it if you just change j and ii to $j and $ii, but you are certain to get into further problems

    Please make the same changes to your own code, and declare every variable that you need using my as close as possible to the first place they are used

    You should also improve your variable naming. Things like @largearray are pointless: the @ says that it's an array, and whether it's large or not is relative, and of little use in understanding your code. If you have no better description of its purpose then @table or @data are probably a little better

    Likewise, please avoid capital letters and most single-letter names. @V, $Cm and $Im are meaningless, and you would need fewer comments if those names were better

    You certainly wouldn't need comments like # end while loop and # outer for loop if you had indented your blocks properly and kept them short enough so that both the beginning and the end can be seen on the screen at the same time, and the fewer comments you can get away with the better, because they badly clutter the code structure

    Finally, it's worth noting that the C-style for loop is rarely the best choice in Perl. Your

    for ( $i = 1; $i <= $n; $i++ ) { ... }
    

    is much clearer as

    for my $i ( 1 .. $n ) { ... }
    

    and declaring the control variable at that point makes it unnecessary to invent new names like $ii for each new loop