http://www.perlmonks.org?node_id=991666

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

Hi. Help seems to be required. I am having problems passing variables from one subroutine to another. Are there any obvious problems with the code?
#!/usr/local/bin/perl use strict; use warnings; use DBI; use DBD::mysql; my $dbh = DBI->connect('DBI:mysql:e:XXX.XXX.XXX.XX', 'x.xxxxxxx', 'xxx +xxx') or die "Couldn't connect to database: " . DBI->errstr; sub retrieve_ESLIM_codes { my $Multiple_lines = "Lines_string.txt"; my $lines_one_col = "List_of_lines_relating_to_particular_gene_kno +ckouts.txt"; my $sql_A = "SELECT DISTINCT parameter_id FROM e.measurement WHERE + parameter_id LIKE 'M%' OR parameter_id LIKE 'G%' OR parameter_id LIK +E 'E%' AND parameter_id NOT LIKE '%_________8__' ORDER BY parameter_i +d;"; my $Output_A = "List_parameters_common_to_centers.txt"; open (OUTFILE_A, ">>$Output_A") || die "Error opening outfile.$!,s +topped"; my $sth_A = $dbh->prepare($sql_A) or die "Cannot prepare: " . $dbh +->errstr(); $sth_A->execute() or die "$sth_A->errstr\n"; my @row_A; my @parameters_A; my @record_A; while(@row_A = $sth_A->fetchrow_array()) { @record_A = @row_A; push(@parameters_A, @record_A); print OUTFILE_A "@record_A\n"; } $sth_A->finish(); my @sorted_parameters_A = sort { $a cmp $b } @parameters_A ; return (@sorted_parameters_A); } sub look_through_file ($$) { my ($start_point, $continue_tag) = @_; my $entry_no_new = $start_point; # entry number new my @array_of_lines; my $Line_input = "List_of_lines_relating_to_particular_gene_knocko +uts.txt"; open (LINE_INPUT, "<$Line_input") || die "Error opening outfile.$! +,stopped"; print "I am here A\n"; LINE: while (<LINE_INPUT>){ print "I am here B\n"; chomp; my $entry_no_old = $entry_no_new; if ($_ =~ /^(\d{1,3})\t(\d{3,5})/){ next LINE if $1 < $start_point; print "I am here C\n"; $entry_no_new = $1; print "Entry no new: ".$entry_no_new."\n"; my $line = $2; push (@array_of_lines, $entry_no_new); last LINE if ($entry_no_new > $entry_no_old); } else { $continue_tag = "FALSE"; } } print @array_of_lines; print "\n"; close (LINE_INPUT); return (@array_of_lines, $start_point, $entry_no_new, $continue_ta +g); } sub create_output (\@$) { my (@array_of_lines, $entry_no_new) = @_; print "\n\n$entry_no_new"; mkdir "Output"; my $Line_set_Output_A = 'Output/Line_set_Output_A_'.$entry_no_new. +'.txt'; open (LINE_SET_OUTPUT_A, ">>$Line_set_Output_A") || die "Error ope +ning outfile.$!,stopped"; my $sql_command_A = "CREATE TEMPORARY TABLE Temp_Table ( Key_m INT + NOT NULL AUTO_INCREMENT, Value INT NOT NULL)"; my @insert_statements; print LINE_SET_OUTPUT_A @array_of_lines; print LINE_SET_OUTPUT_A "\n"; foreach (@array_of_lines){ my $element_line = $_; my $sql_command_B = 'INSERT INTO Temp_table (Line) VALUES ('.$elem +ent_line.');'; # uninitialized value $element_line ???????? print LINE_SET_OUTPUT_A "$sql_command_B\n"; } my $sql_command_C = "DROP TABLE Temp_table\n"; my $sql_command_D = "SELECT * from eur.annotation WHERE Line IN (S +ELECT Line FROM #Temporary_table) AND entity_name LIKE '_%' ORDER BY +evidence_code;"; print LINE_SET_OUTPUT_A "$sql_command_D\n"; my $dbh; my $sth_A = $dbh->prepare($sql_command_D) or die "Cannot prepare: +" . $dbh->errstr(); $sth_A->execute() or die "$sth_A->errstr\n"; while(my @row_A = $sth_A->fetchrow_array()) { print LINE_SET_OUTPUT_A @row_A; print LINE_SET_OUTPUT_A "\n"; my @record_A = @row_A; push(my @fields_A, @record_A); } $sth_A->finish(); } retrieve_ESLIM_codes(); my $starting_point = 0; my $continue_tag = "TRUE"; my @array_of_lines; my $entry_no_new; while ($continue_tag eq "TRUE"){ look_through_file ($starting_point, $continue_tag); create_output (@array_of_lines, $entry_no_new); }

Replies are listed 'Best First'.
Re: problems passing variables between subroutine
by CountZero (Bishop) on Sep 04, 2012 at 20:35 UTC
    Read again what I explained about using prototypes.

    The (\@$) prototype definition expects an array as its first argument and makes it into a reference (i.e. a scalar). So your

    my (@array_of_lines, $entry_no_new) = @_;
    is quite wrong.

    It should be

    my ($arrayref_of_lines, $entry_no_new) = @_;
    and next you must dereference the reference:
    my @array_of_lines = @$arrayref_of_lines;

    Trust me, you are far better off forgetting all about prototypes and simply use references.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

    My blog: Imperial Deltronics
      Hi there, Unfortunately there is a problem as I don't seem to be passing anything in. Is the code you suggest ok, or have I made an error elsewhere in the code? What is the most convenient way for me to find out?
      sub create_output (\@$) { my ($arrayref_of_lines, $entry_no_new) = @_; my @array_of_lines = @$arrayref_of_lines; print "Hi there\n"; print "@array_of_lines"; # THIS IS NOT PRINTING ANYTHING exit;
      Update:

      I get an error message:

      Use of uninitialized value $_[1] in join or string at receive_annotati +ons_B.pl line 162.
      When the following is used:
      sub create_output (\@$) { my ($arrayref_of_lines, $entry_no_new) = @_; # THIS DOES NOT WO +RK my @array_of_lines = @$arrayref_of_lines; print "Hi there\n"; print "@_"; print "@array_of_lines"; exit;
        The error is not in the subroutine.

        In your script when you call create_output     (@array_of_lines, $entry_no_new); the array is empty since you never put anything into @array_of_lines.

        You are getting yourself confused because in the look_through_file subroutine you also declare an @array_of_lines array and fill it with data. BUT as this is a lexical array scoped to this subroutine only (and totally separate of the lexical @array_of_lines declared in the main body of your script), it will simply disappear at the end of the subroutine, taking all its data with it.

        There are two solutions:

        1. use the @array_of_lines declared in the main body of your script also in the subroutine. In other words, do not do a my @array_of_lines in your sub. THIS IS THE BAD SOLUTION!
        2. Take the return value from your look_through_file sub and put that in the @array_of_lines array declared in the main body of your script. You will have to re-adjust the return parameters of that sub and put the array at the back or the returned scalar values will get eaten by the array.

        CountZero

        A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

        My blog: Imperial Deltronics
Re: problems passing variables between subroutine
by GrandFather (Saint) on Sep 05, 2012 at 02:22 UTC

    You completely ignored the last round of advice I gave you so I'd be dumb to offer you more advice wouldn't I? Hey ho, so be it, I'm dumb.

    Your actual code shows exactly the problems my previous sample was designed to avoid.

    1. retrieve_ESLIM_codes uses the global variable $dbh
    2. retrieve_ESLIM_codes returns @sorted_parameters_A but the return result is not used
    3. look_through_file uses and updates $continue_tag, but the result is not used
    4. look_through_file populates a local @array_of_lines, but the result is not used
    5. look_through_file sets a local $entry_no_new, but the result is not used
    6. create_output is passed parameters that have no value

    and so on, and so on. On top of that:

    my $dbh; my $sth_A = $dbh->prepare($sql_command_D) ...

    is never going to give a happy result! You have a lot of work to do before your code has any chance of working.

    True laziness is hard work
      Hi. Your post is useful, thanks. I have gone through points that you have made and added my comments.


      retrieve_ESLIM_codes uses the global variable $dbh

      Should I be calling the database from within each subroutine, seperately?


      retrieve_ESLIM_codes returns @sorted_parameters_A but the return result is not used

      I was planning to use this at a future point. Anyway this is a useful reminder thanks.


      look_through_file uses and updates $continue_tag, but the result is not used

      It is supposed to be used. When the end of file is reached the $continue_tag goes to "FALSE" and this gets passed out of the subroutine from which point the while loop (that makes the subroutine calls in the first place) is broken.


      look_through_file populates a local @array_of_lines, but the result is not used

      Again. This will be used at a future point. Although I did not correctly build this array (as hinted in your next point).


      look_through_file sets a local $entry_no_new, but the result is not used

      At one point I did not correctly use this variable. I have removed this incorrect use. However, I don't believe that you are correct in saying that the result is not used. Basically the code is saying that if you get to the next entry number then exit the subroutine.


      create_output is passed parameters that have no value

      I'm not sure if I understand what you are suggesting here. Is this problem covered in the other posts?
        look_through_file uses and updates $continue_tag, but the result is not used

        It is supposed to be used. When the end of file is reached the $continue_tag goes to "FALSE" and this gets passed out of the subroutine from which point the while loop (that makes the subroutine calls in the first place) is broken.

        The value of $continue_tag gets passed out of the subroutine indeed and is immediately discarded because there is no variable to store it in.

        CountZero

        A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

        My blog: Imperial Deltronics

        Avoid "future proofing" code. Almost always we guess wrong about how things are going to go in the future. Leaving decisions until they need to be made generally works better - you have better information to base the decision on. If you guess and decide early it can be much harder to change your mind. You can easily end up with either a lot of work throwing away the code that was never used and rewriting it differently, or accept a sub-optimum solution that becomes hard to understand and maintain.

        With that in mind:

        1. Only pass parameters that are used
        2. Only calculate and return results that are used
        3. Don't use global variables
        True laziness is hard work
Re: problems passing variables between subroutine
by philiprbrenan (Monk) on Sep 04, 2012 at 19:34 UTC

    Please use a reference an array rather than an actual array in:

    sub create_output (\@$) { my (@array_of_lines, $entry_no_new) = @_;

    as below:

    use feature ":5.14"; use warnings FATAL => qw(all); use strict; use Data::Dump qw(dump pp); sub foo(\@) {my ($array) = @_; say "@$array"; } my @array = qw(1 2 3); foo(@array);

    Produces

    1 2 3

      Please don't encourage needless use of prototypes!

      Also in simple cases passing arrays by reference serves only to obscure the code so don't do that. Just because a technique fixes a problem in some cases, doesn't mean it should be used in all cases. Be selective about the habits you acquire and apply them along with a little consideration rather than blindly following a set of rules.

      True laziness is hard work

        Also in simple cases passing arrays by reference serves only to obscure the code so don't do that.

        Nonsense

Re: problems passing variables between subroutine
by philiprbrenan (Monk) on Sep 05, 2012 at 21:36 UTC

    Please use a reference to an array rather than an actual array in:

    sub create_output (\@$) { my (@array_of_lines, $entry_no_new) = @_;

    as below:

    use feature ":5.14"; use warnings FATAL => qw(all); use strict; use Data::Dump qw(dump pp); sub foo(\@) {my ($array) = @_; say "@$array"; } my @array = qw(1 2 3); foo(@array);

    Produces

    1 2 3