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

I am having trouble converting a foreach loop to a for loop. here is the code:

sub get_all_seqs{ my $protein = shift; #print "protein = $protein\n"; my @all_seqs; my %Deg_codons = get_degenerate(); my %SEQ = (); my @aas = split(//, $protein); my $aa = $aas[0]; my @codons = @{$Deg_codons{$aa}}; foreach my $codon(@codons){ $SEQ{$codon}=1; } for(my $i=1; $i<@aas; $i++){ my $aa = $aas[$i]; @codons = @{$Deg_codons{$aa}}; foreach my $seq(keys %SEQ){ foreach my $codon(@codons){ $SEQ{$seq.$codon}=1; } delete $SEQ{$seq}; } } @all_seqs = sort keys %SEQ; return @all_seqs; }


here is what I've tried to do:

sub get_all_seqs{ my $protein = shift; #print "protein = $protein\n"; my @all_seqs; my %Deg_codons = get_degenerate(); my %SEQ = (); my @aas = split(//, $protein); my $aa = $aas[0]; my @codons = @{$Deg_codons{$aa}}; foreach my $codon(@codons){ $SEQ{$codon}=1; } for(my $i=1; $i<@aas; $i++){ my $aa = $aas[$i]; @codons = @{$Deg_codons{$aa}}; my $s=(keys %SEQ); for(my $j=0;$j<$s;$j++){ my $seq=(keys %SEQ)[$j] foreach my $codon(@codons){ $SEQ{$seq.$codon}=1; $j++; $s++; } delete $SEQ{$seq}; $j--; $s--; } } @all_seqs = sort keys %SEQ; return @all_seqs; }


Why doesn't this work?

Replies are listed 'Best First'.
Re: foreach to for (related to my last question)
by shmem (Chancellor) on Jun 21, 2006 at 14:57 UTC
    In the inner loop, you reference an element at index $j of a list generated with "keys", which is funny bizarre. Note that this list doesn't have any particular order.
    my $seq=(keys %SEQ)[$j] <--- missing semicolon
    Later you delete from the hash %SEQ.

    Question: What will (keys %SEQ)[$j] return the next time?
    Answer: (this space intentionally filled with nonsense)

    If you want to iterate over the keys of a hash, get a list of them and iterate over this list:

    for(my $i=1; $i<@aas; $i++){ my $aa = $aas[$i]; @codons = @{$Deg_codons{$aa}}; my @SEQ = keys %SEQ; my $s = scalar @SEQ; for(my $j=0;$j<$s;$j++){ my $seq=@SEQ[$j]; foreach my $codon(@codons){ $SEQ{$seq.$codon}=1; $j++; $s++; } delete $SEQ{$seq}; $j--; $s--; } }

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: foreach to for (related to my last question)
by ikegami (Patriarch) on Jun 21, 2006 at 15:02 UTC

    Why?

    By the way, your first snippet simplifies to

    sub get_all_seqs{ my $protein = shift; #print "protein = $protein\n"; my @aas = split(//, $protein); my %Deg_codons = get_degenerate(); my @codons = @{$Deg_codons{$aas[0]}}; my %SEQ = map { $_ => 1 } @codons; foreach my $aa (@aas[1..$#aas]) { foreach my $seq (keys %SEQ) { foreach my $codon (@codons) { $SEQ{$seq.$codon} = 1; } delete $SEQ{$seq}; } } return sort keys %SEQ; }
      so the first version works, the second doesn't

      the second version, when everything to do with $s++ and $s-- is removed, doesn't iterate enough times.

      when the lines with $s are inserted, it goes through to many times and acts upon the wrong data sets. basically I'm asking: how do I replace the foreach loops with for loops?

        basically I'm asking: how do I replace the foreach loops with for loops?

        And I asked why you want to do that. It's like asking how to remove weeds with kitchen utensils when you have perfectly good gardening tools. They're kinda similar, but they have different purposes.

        You have list over which you want to iterate. That's what "foreach" loops do. Counting ("for") loops, on the other hand, cannot iterate over lists. It's possible to store the list in an array, and loop over the indexes of the array, but why do you want to do that?

        To go from a "foreach" loop to a "for" loop, change

        foreach my $item (...list...) { ... }

        to

        my @list = (...list...); for my $i (0..$#list) { my $item = $list[$i]; ... }
        tricolaire:

        Just to amplify on the point ikegami makes: foreach takes care of the niggling little details for you:

      • 0-based or 1-based array? (I code in several languages, so it doesn't stay burned in.)
      • Is my condition == or >=?
      • How do I handle the case if my code adding an item to the list inside the code?
      • Having a nice iterator handle the simple details is a blessing. Why not use it?

        --roboticus

Re: foreach to for (related to my last question)
by dsheroh (Monsignor) on Jun 21, 2006 at 15:00 UTC
    I assume you mean to say that the first version of the code works and the second doesn't, but you haven't said how it fails to work. Or, for that matter, what you are trying to accomplish by changing it. (I have a vague feeling this could be an [id://XY problem].)

    My best guess is that you're seeing the for loop in the second version terminate at the wrong time (either dying early or running past the end of %SEQ depending on the size of @codons) because you keep changing the value of $s. But that's just a guess as to how it's failing. If I'm wrong about the failure mode, then I'm most likely wrong about the cause as well.