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

I want to perform a seemingly simple task, but am confused. I would like to initialize all values in my hash to equal 60.

my $maxBits=60; # hash reference - gave error message below my %maxBits=60; # wrong %maxBits=map {$_=>60} ; #?
Can't use string ("60") as a HASH ref while "strict refs" in use at fl +agginscript_Flag1.pl line 58, <IN> line 1.

here is a larger chunk of the code so you can see the context

############### read in blast table and parse ####### my $in_blast_tab=$ARGV[0]; open(IN,$in_blast_tab) or die "cannot open $in_blast_tab\n"; my $HoFlg1={}; my $HoFlg2={}; my $maxBits=60; #%maxBits=map {$_=>60} ; # or intialize all values to be 60 using map print "maxBits after intialization\n"; print Dumper($maxBits); while (my $line=<IN>) { next if ($line =~ /^#/); next unless ($line =~ /\S/); chomp $line; my ($Query_id,$strand,$Subj_id,$Perc_iden,$align_len,$num_mm,$ +gap,$q_start,$q_end,$s_start,$s_end,$e_value,$bit_score)=split("\t",$ +line); # extra field of strand next if ($bit_score <60); if ($bit_score > $maxBits->{$Query_id}){ $maxBits->{$Query_id}=$bit_score; } my ($Flag1, $Flag2 ) = &Flag( $Subj_id, \%proph_prots, \%euk_p +rots, \%vir_prots ); $HoFlg1->{$Query_id}->{$Flag1}->{$bit_score}++; $HoFlg2->{$Query_id}->{$Flag2}->{$bit_score}++; print join("\t",$Query_id,$Subj_id,"bit",$bit_score,"F1",$Flag +1,"F2",$Flag2,"maxBits{Query}",$maxBits->{$Query_id})."\n"; } #print "Hash HoFlg1\n"; #print Dumper($HoFlg1)."\n"; #print "Hash maxBits\n"; #print Dumper(%maxBits)."\n"; sub Flag { my ( $Subj_id, $proph_prots, $euk_prots, $vir_prots ) = @_; return "Proph", "Phage" if exists $$proph_prots{$Subj_id}; return "Euk", "Euk" if exists $$euk_prots{$Subj_id}; return "Vir", "Phage" if exists $$vir_prots{$Subj_id}; return "Bact", "Bact"; } ## end sub Flag my $bits_perc=$ARGV[1]; # consider hits within this percent of top hit my $outfile="$in_blast_tab.$bits_perc.FlagTab"; open(OUT,">",$outfile); print OUT "# this file was generate by $0 on".localtime(time)."\n"; print OUT "# these are the counts of hits for each Flag that are withi +n $bits_perc of the top bit_score\n"; print OUT "#".join("\t",qw(Query count_Proph count_Euk count_Vir count +_Bact))."\n"; my @flag_list2=("Phage","Euk","Bact"); my @flag_list1=("Proph","Euk","Vir","Bact"); foreach my $q (keys %{$HoFlg1}){ print OUT "$q\t"; for my $flag (@flag_list1){ my $count={}; if (! exists $HoFlg1->{$q}->{$flag}){ $count->{$flag}=0; } else { for my $b (keys %{$HoFlg1->{$q}->{$flag}}){ if ($b > $bits_perc*$maxBits->{$q}){ $count->{$flag} += $HoFlg1->{$q}->{$flag}->{$b +}; } } } if (! defined $count->{$flag}){ $count->{$flag}=0; } print OUT "$count->{$flag}\t"; } print OUT "\n"; }

Replies are listed 'Best First'.
Re: initialize all values in a hash
by DrHyde (Prior) on May 18, 2012 at 10:35 UTC

    You appear to be very confused!

    my $maxBits=60 is not a hash reference. 60 is a plain old scalar.

    my %maxBits=60 is indeed wrong, because %maxBits is a hash, but 60 is again as plain old scalar. Perl will *try* to Do The Right Thing and assume that the right hand side is a list of key/value pairs, but it can't because there's an odd number of elements in the list (it gets treated as a list with one element). Hence the warning "Odd number of elements in hash assignment". It then, after warning you, extends the list by appending an undef, creating a hash that looks like this:

    60 => undef

    that is, the hash has a single key in it (60) whose value is undef.

    Finally, %maxBits=map {$_=>60};. This is a syntax error, because map needs a list of values to work with.

    I recommend that you stop and read Learning Perl (the Llama book). IIRC (and I admit I've not looked at it in the last several years) it includes a good tutorial on data types - scalars, arrays, hashes. I don't think it includes much on references, but once you've read and understood the Llama you should be able to cope with perlreftut, perldsc, and all the other tutorials that come with perl and, if you still need it, the documentation for the map function.

Re: initialize all values in a hash
by tobyink (Canon) on May 18, 2012 at 10:44 UTC

    If you know the keys in advance, it's relatively easy:

    use Data::Dumper; my %hash = map { $_ => 60; } qw(foo bar baz); print Dumper \%hash;

    If you don't... well... that's not how hashes work. You could do it with a tied hash, but I can't find a module that does this on CPAN, so you'd need to do it yourself.

    But it would be better to modify the code that reads from the hash so that before it reads from the hash, it checks whether that value exists in the hash, and substitutes 60 if not.

    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'

      "You could do it with a tied hash, but I can't find a module that does this on CPAN, so you'd need to do it yourself."

      OK, I've just gone and uploaded Hash::DefaultValue to CPAN. Usage:

      tie my %hash, 'Hash::DefaultValue', 60; $hash{foo} = 8; say $hash{foo}; # says 8 say $hash{bar}; # says 60
      perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'

      Thank you for the suggestion of the other way. I now check and if it is the first time it sees this query it will assign the current bit_score as the max, and then check all subsequent ones to see if they are bigger.

      and maybe I'll re-read the llama book. It was a while ago and I left Perl for a few years, and seem to have a selective memory.

      my $in_blast_tab=$ARGV[0]; open(IN,$in_blast_tab) or die "cannot open $in_blast_tab\n"; my $HoFlg1={}; my $HoFlg2={}; my %maxBits; while (my $line=<IN>) { next if ($line =~ /^#/); next unless ($line =~ /\S/); chomp $line; my ($Query_id,$strand,$Subj_id,$Perc_iden,$align_len,$num_mm,$ +gap,$q_start,$q_end,$s_start,$s_end,$e_value,$bit_score)=split("\t",$ +line); # extra field of strand next if ($bit_score <60); if (!exists $maxBits{$Query_id}){ $maxBits{$Query_id}=$bit_score; } elsif (exists $maxBits{$Query_id} && $bit_score > $maxBits{$Qu +ery_id}){ $maxBits{$Query_id}=$bit_score; } my ($Flag1, $Flag2 ) = &Flag( $Subj_id, \%proph_prots, \%euk_p +rots, \%vir_prots ); $HoFlg1->{$Query_id}->{$Flag1}->{$bit_score}++; $HoFlg2->{$Query_id}->{$Flag2}->{$bit_score}++; print join("\t",$Query_id,$Subj_id,"bit",$bit_score,"F1",$Flag +1,"F2",$Flag2,"maxBits{Query}",$maxBits{$Query_id})."\n"; }
Re: initialize all values in a hash
by Anonymous Monk on May 18, 2012 at 10:33 UTC
    I see that %maxBits is filled with keys that are not known until run-time. If you have a list of constant strings that can occur for $Query_id, then an up-front initialisation is possible.
Re: initialize all values in a hash
by locked_user sundialsvc4 (Abbot) on May 18, 2012 at 14:46 UTC

    Perhaps another approach would be to check if an entry exists in your hash, and if it does not, return 60.0.   You don’t need to burn up memory storing a default-value ...