http://qs321.pair.com?node_id=1152155

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

Hi, I’m pretty new to perl and coding in general, so apologies if this question has been dealt with before (I couldn’t find a solution anywhere obvious). I have a file that consists of 7 columns and many lines, and I need to alter the value of the 3rd column by performing a range of calculations involving other columns on the same line and also the previous line. I can do this, my problem is that I want the new value for the 3rd column of the current line to be saved as the loop proceeds and the current line becomes the previous line (effectively I want the values in the 3rd column to cumulate as the loop goes over each line). This is what I have so far (the middle part can be ignored as that's just the specific alterations to the value):

use warnings; use strict; open (IN, "in.txt") or die; open (OUT, "out.txt") or die; my $previous_line = <IN>; while (my $current_line = <IN>) { chomp $current_line; chomp $previous_line; my @previous_columns = split(" ", $previous_line); my $end_coor = $previous_columns[2] + $previous_columns[3]; my @current_columns = split(" ", $current_line); my $seq_length = length $previous_columns[6]; my $gap_count = $previous_columns[6] =~ tr/Q//; my $original_length = $seq_length - $gap_count; my $original_end_coor = $previous_columns[2] + $original_length; my $distance = $current_columns[2] - $original_end_coor; $current_columns[2] = $end_coor + $distance; $previous_line = $current_line; print OUT "$current_columns[2]\n"; } close IN; close OUT;

The important lines are the last 3 of the loop, the value of the 3rd column is changed and subsequently printed but when the loop finishes this new value is not passed to the previous line, which instead contains the original value for that column and line. Anyone know how I would change this? Many thanks

  • Comment on How to loop over two lines, alter a value in the current line and save it to the previous line?
  • Download Code

Replies are listed 'Best First'.
Re: How to loop over two lines, alter a value in the current line and save it to the previous line?
by hdb (Monsignor) on Jan 07, 2016 at 10:02 UTC

    I am not sure what your question really is. You are modifying @current_columns but you are keeping $current_line unmodified and store it in $previous_line for further processing. Do you want your modifications reflected in $previous_line in the next iteration of your loop? If that is the question, you should really store the array @current_columns instead of $current_line, e.g.

    my $previous_line = <IN>; chomp $previous_line; my @previous_columns = split(" ", $previous_line); while (my $current_line = <IN>) { chomp $current_line; my $end_coor = $previous_columns[2] + $previous_columns[3]; my @current_columns = split(" ", $current_line); my $seq_length = length $previous_columns[6]; my $gap_count = $previous_columns[6] =~ tr/Q//; my $original_length = $seq_length - $gap_count; my $original_end_coor = $previous_columns[2] + $original_length; my $distance = $current_columns[2] - $original_end_coor; $current_columns[2] = $end_coor + $distance; print OUT "$current_columns[2]\n"; @previous_columns = @current_columns; }

    Not tested...

      Hi, thanks for your very quick answer. Yes, I would like my modifications reflected in $previous_line in the next iteration of the loop, so your suggestion makes a lot of sense. Unfortunately that change seems to make $previous_line always equal the first line of the file for some reason

        Correct, but it is not used anymore at all within the loop.

Re: How to loop over two lines, alter a value in the current line and save it to the previous line?
by 1nickt (Canon) on Jan 07, 2016 at 10:23 UTC

    Hello, adapting your code:

    #!/usr/bin/perl use strict; use warnings; my $previous_line = <DATA>; while ( my $current_line = <DATA> ) { chomp for ( $previous_line, $current_line ); my @previous_cols = split ' ', $previous_line; my @current_cols = split ' ', $current_line; print "$current_cols[0]: $current_cols[1] "; $current_cols[1] += $previous_cols[1]; print "now changed to $current_cols[1]\n"; $previous_line = join ' ', @current_cols; } __DATA__ seed 1 foo 1 bar 2 baz 3 qux 4
    Output:
    foo: 1 now changed to 2 bar: 2 now changed to 4 baz: 3 now changed to 7 qux: 4 now changed to 11

    You may want to look at Text::CSV_XS for this kind of task.

    Hope this helps!

    The way forward always starts with a minimal test.
      Hi, thanks a lot for your answer, I'll have a look at Text::CSV XS. I must admit this is slightly over my head, I've replaced $previous_line = $current_line with:
      $current_columns[2] += $previous_columns[2]; $previous_line = join ' ', @current_columns;
      This cumulates the 3rd column, which although I mentioned it in the question on reflection isn't actually what I was wanting to do. Apologies for wasting your time with a badly worded question. I'm looking to alter a value in the current line, and then have that value saved to the previous line for use in the next iteration of the loop.

        Apologies for wasting your time with a badly worded question.

        You may have an XY problem. Might be a good time to state what it is you are really trying to accomplish. Could be that this thing with using the previous line is not the best approach.

        I'm looking to alter a value in the current line, and then have that value saved to the previous line for use in the next iteration of the loop.

        ??

        The code you posted does exactly that. Maybe this will show it more clearly?

        #!/usr/bin/perl use strict; use warnings; my $previous_line = <DATA>; print " prev\tcurrent\n"; while ( my $current_line = <DATA> ) { chomp for ( $previous_line, $current_line ); print "input:\t$previous_line\t$current_line\n"; my @previous_cols = split ' ', $previous_line; my @current_cols = split ' ', $current_line; $current_cols[1] += $previous_cols[1]; $previous_line = $current_line = join ' ', @current_cols; print "output:\t$previous_line\t$current_line\n"; print "\n"; } __DATA__ nul 0 foo 1 bar 2 baz 3 qux 4
        Output:
        prev current input: nul 0 foo 1 output: foo 1 foo 1 input: foo 1 bar 2 output: bar 3 bar 3 input: bar 3 baz 3 output: baz 6 baz 6 input: baz 6 qux 4 output: qux 10 qux 10

        update: reworked example code to not use a sub


        The way forward always starts with a minimal test.
Re: How to loop over two lines, alter a value in the current line and save it to the previous line?
by QuillMeantTen (Friar) on Jan 07, 2016 at 10:07 UTC

    Greetings,
    First some suggestions, against using barewords, and for using open in its three terms form: more explicit and understandable
    Also I put there use autodie because I'm lazy :$

    use warnings; use strict; use autodie; open my $in,'<', "in.txt"; open my $out,'>', "out.txt";
    Since you start at first line and then jump over each line to save to the previous line you can use the perl auto variable for line counts
    my $previous_line; while (<$in>) { $current_line = $_; chomp $current_line; if(!defined($previous_line) || $. mod 2 != 0){ $previous_line = $current_line; next; } my @previous_columns = split(" ", $previous_line); my $end_coor = $previous_columns[2] + $previous_columns[3]; my @current_columns = split(" ", $current_line); my $seq_length = length $previous_columns[6]; my $gap_count = $previous_columns[6] =~ tr/Q//; my $original_length = $seq_length - $gap_count; my $original_end_coor = $previous_columns[2] + $original_length; my $distance = $current_columns[2] - $original_end_coor; $current_columns[2] = $end_coor + $distance; $previous_line = $current_line; print $out "$current_columns[2]\n"; } close $in; close $out;
    I'm not sure about the way you want to go over the file though, do you want to just jump over half of the lines?

      Why do you think the OP is jumping (wants to jump) over half of the lines?