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

Hi I am pretty new to programming and perl, so sorry if this stupid...but:

I have a foreach loop, to which I pass a bunch of strings hoping to create an array of values for each position in the string, and keep all that in a hash. All the variables are declared using my at the beginning of the sub. The code works, but it gives an "Use of uninitialized value $temp_array[0] in join or string at quality_cutoff.pl line 124" error. Why?

foreach (@string_array){ @string=split(//,$_); $basecount=0; foreach $pos (@string){ $basecount++; $pos_score=ord($pos)-33; @temp_array=$base_stat{$basecount}; # line 124 push (@temp_array,$pos_score); $base_stat{$basecount}="@temp_array"; }

Replies are listed 'Best First'.
Re: Why is it uninitialized?
by davido (Cardinal) on Apr 03, 2011 at 07:38 UTC

    What does %base_stat contain before entering the first iteration of the loop? If %base_stat contains nothing, your first iteration of the loop will be performing operations on @temp_array when it too contains nothing. Perl is warning you that you're making a mistake. See the following snippet that should clarify for you.

    use 5.012.02; use strict; use warnings; my %basestat; my @temparray; my $basecount = 1; @temparray = $basestat{$basecount}; say "\$temparray[0] is ", defined( $temparray[0] ) ? "defined." : "und +efined."; say "\@temparray has ", scalar(@temparray), " elements."; say "\$temparray[0] = $temparray[0]"; say "\@temparray = @temparray";

    And the output:

    Use of uninitialized value $temparray[0] in concatenation (.) or strin +g at test.pl line 14. Use of uninitialized value $temparray[0] in join or string at test.pl +line 15. $temparray[0] is undefined. @temparray has 1 elements. $temparray[0] = @temparray =

    This snippet more or less constructs the state your code experiences on the first iteration of the loop. I'm surprised you're not getting a warning two lines later in the script too, where you wrap @temparray in quotes. On your subsequent loop iterations %basestat and @temparray contain values, and so you no longer are attempting to use the values of uninitialized variables, so your warnings go away.

    I think if you dig a little deeper you'll find that there's a better way to do what you're trying to do.


    Dave

      Thank you I just did!
      push @{hash{$key},$value)
      works better:)
Re: Why is it uninitialized?
by wind (Priest) on Apr 03, 2011 at 07:43 UTC

    You %base_stat hash is not defined initially for each key $basecount. Therefore your @temp_array equals (undef) the first time, and when you include it in the "@temp_array" string, it throws a warning.

    It actually looks like you want an array for the hash values anyway.

    Also, it's good that you're declaring all of your variables with my, but you should aim to always limit the scope of your variables to the highest point possible.

    Observe the following code and see if it is doing what you intend:

    use Data::Dumper; use strict; use warnings; my @string_array = qw(foo bar baz); my %base_stat; foreach (@string_array){ my @string = split ''; my $basecount = 0; foreach my $pos (@string){ $basecount++; my $pos_score = ord($pos)-33; push @{$base_stat{$basecount} ||= []}, $pos_score; # my @temp_array = $base_stat{$basecount}; # line 124 # push @temp_array, $pos_score; # $base_stat{$basecount} = "@temp_array"; } } print Dumper(\%base_stat);
Re: Why is it uninitialized?
by davido (Cardinal) on Apr 03, 2011 at 08:22 UTC

    I keep squinting at your code and wondering how it is that you could say it works. If I give it a seven element array, it returns a hash with six elements. None of the values actually correspond directly to a set of numbers that directly resembles the input string. Take the following example I gathered using your code.

    use 5.012.02; use strict; use warnings; my %base_stat; my @temp_array; my $basecount; my @string; my @string_array = qw/this that and all manner of other/; my $pos; my $pos_score; foreach (@string_array){ @string=split(//,$_); $basecount=0; foreach $pos (@string){ $basecount++; $pos_score=ord($pos)-33; @temp_array=$base_stat{$basecount}; # line 124 push (@temp_array,$pos_score); $base_stat{$basecount}="@temp_array"; } } say "\%base_stat contains:"; foreach my $key ( sort keys %base_stat ) { say "$key => $base_stat{$key}"; }

    And the output:

    Use of uninitialized value $temp_array[0] in join or string at test.pl + line 26. Use of uninitialized value $temp_array[0] in join or string at test.pl + line 26. Use of uninitialized value $temp_array[0] in join or string at test.pl + line 26. Use of uninitialized value $temp_array[0] in join or string at test.pl + line 26. Use of uninitialized value $temp_array[0] in join or string at test.pl + line 26. Use of uninitialized value $temp_array[0] in join or string at test.pl + line 26. %base_stat contains: 1 => 83 83 64 64 76 78 78 2 => 71 71 77 75 64 69 83 3 => 72 64 67 75 77 71 4 => 82 83 77 68 5 => 68 81 6 => 81

    Is that really what you want? Look at how poorly the output corresponds to the input strings. There's not a direct 1:1 correlation of string characters to values on a per-input-line basis. If all you want is a set of values corresponding to an array of input strings, how about this?

    use 5.012.02; use strict; use warnings; my @input_string_array = qw/this that and all manner of other/; my @output_array; foreach my $string ( @input_string_array ) { push @output_array, join " ", map { ord($_) - 33 } split //, $stri +ng; } say $_ for @output_array;

    Or if you really need a hash that is indexed like an array but starting with an index of '1', (all of which seems silly to me, but you might have your reasons), you could do it like this:

    use 5.012.02; use strict; use warnings; my @input_string_array = qw/this that and all manner of other/; my %output_hash; foreach my $iterator ( 0 .. $#input_string_array ) { $output_hash{ $iterator + 1 } = join " ", map { ord($_) - 33 } spl +it //, $input_string_array[$iterator]; } foreach my $key ( sort keys %output_hash ) { say "$key => $output_hash{$key}"; }

    Dave

      Hi Dave

      Thank you - but you seem just to translate all my strings, while i really need the silly thing...:) -i need a hash that will contain the array of values in x position in all the strings, so if the values were just letters I would need the result to look something like

      my @string_array=qw/this that and all manner of other/; ..... %ouput_hash=( '1'=>("t","t","a","a","m","o","o"), '2'=>("h","h","n","l","a","f","t"), ...... )

        :) I'm glad I hedged with "you might have your reasons." You do.


        Dave

Re: Why is it uninitialized?
by Anonymous Monk on Apr 03, 2011 at 07:51 UTC
    All the variables are declared using my at the beginning of the sub

    FWIW, you're supposed to aim for smallest scope necessary, you'll develop this skill with practice :)(Tutorials: Variables and Scoping)

    Compare Silenced, Loudmouth, and Smooth (always strive for Smooth)

    #!/usr/bin/perl -- use strict; use warnings; use diagnostics; use Data::Dumper(); Main( @ARGV ); exit( 0 ); sub Main { warn Dumper( Silenced('hello') ), ' '; warn '------' x 6; warn Dumper( Loudmouth('loud') ), ' '; warn '------' x 6; warn Dumper( Smooth('cop') ), ' '; } ## end sub Main sub Dumper { Data::Dumper->new( \@_ )->Indent(0)->Useqq(1)->Dump; } sub Silenced { no warnings 'uninitialized'; my @string_array = @_; my @string; my $basecount; my $pos; my $pos_score; my @temp_array; my %base_stat; foreach (@string_array) { @string = split( //, $_ ); $basecount = 0; foreach $pos (@string) { $basecount++; $pos_score = ord($pos) - 33; @temp_array = $base_stat{$basecount}; # line 124 push( @temp_array, $pos_score ); $base_stat{$basecount} = "@temp_array"; } ## end foreach $pos (@string) } ## end foreach (@string_array) return \%base_stat; } ## end sub Silenced sub Loudmouth { my @string_array = @_; my @string; my $basecount; my $pos; my $pos_score; my @temp_array; my %base_stat; foreach (@string_array) { @string = split( //, $_ ); $basecount = 0; foreach $pos (@string) { $basecount++; $pos_score = ord($pos) - 33; warn Dumper( \%base_stat ), ' '; @temp_array = $base_stat{$basecount}; # line 124 push( @temp_array, $pos_score ); $base_stat{$basecount} = "@temp_array"; } ## end foreach $pos (@string) } ## end foreach (@string_array) return \%base_stat; } ## end sub Loudmouth sub Smooth { my %base_stat; for my $str (@_) { my $basecount = 0; for my $pos ( split( //, $str ) ) { $basecount++; my $pos_score = ord($pos) - 33; #~ $base_stat{$basecount} = $base_stat{$basecount} ." ". $pos_score; # + loud $base_stat{$basecount} .= " ". $pos_score; # smooth } ## end for my $pos ( split( //...)) } ## end for my $str (@_) return \%base_stat; } ## end sub Smooth __END__ $ perl pm.897159.pl $VAR1 = {4 => " 75",1 => " 71",3 => " 75",2 => " 68",5 => " 78"}; at +pm.897159.pl line 13. ------------------------------------ at pm.897159.pl line 14. $VAR1 = {}; at pm.897159.pl line 62. Use of uninitialized value $temp_array[0] in join or string at pm.8971 +59.pl line 66 (#1) (W uninitialized) An undefined value was used as if it were alread +y defined. It was interpreted as a "" or a 0, but maybe it was a mi +stake. To suppress this warning assign a defined value to your variables. To help you figure out what was undefined, perl will try to tell y +ou the name of the variable (if any) that was undefined. In some cases it + cannot do this, so it also tells you what operation you used the undefine +d value in. Note, however, that perl optimizes your program and the opera +tion displayed in the warning may not necessarily appear literally in y +our program. For example, "that $foo" is usually optimized into "that + " . $foo, and the warning will refer to the concatenation (.) operat +or, even though there is no . in your program. $VAR1 = {1 => " 75"}; at pm.897159.pl line 62. $VAR1 = {1 => " 75",2 => " 78"}; at pm.897159.pl line 62. $VAR1 = {1 => " 75",3 => " 84",2 => " 78"}; at pm.897159.pl line 62. $VAR1 = {4 => " 67",1 => " 75",3 => " 84",2 => " 78"}; at pm.897159.p +l line 15. ------------------------------------ at pm.897159.pl line 16. $VAR1 = {1 => " 66",3 => " 79",2 => " 78"}; at pm.897159.pl line 17.
    Questions?