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

If you have a file with a header line and then every 2 lines after the header should be 1 line, how would you write this program?

I have written this program and it works, but I'm sure that it could be written much more compactly:

use strict; use warnings; my $line_count = -1; my $accum; while (<STDIN>) { # ignore first line of file. It is a header line # nonetheless, increment line count if ($line_count == -1) { print; ++$line_count; next; } s!\n!!g; if ($line_count % 2) { print "$accum$_\n"; $accum = ''; } else { $accum = $_; } ++$line_count; }
I have beheld the tarball of 22.1 on ftp.gnu.org with my own eyes. How can you say that there is no God in the Church of Emacs? -- David Kastrup
[tag://golf,etl]

Replies are listed 'Best First'.
Re: When every 2 lines of a file (sans first) should be 1...
by moritz (Cardinal) on Dec 04, 2007 at 19:14 UTC

    There's no need to carry $line_count, you can use $. instead.

    And s!\n!!g; can be replaced with chomp.

    Apart from that, your solution looks fine.

Re: When every 2 lines of a file (sans first) should be 1...
by BrowserUk (Patriarch) on Dec 04, 2007 at 19:29 UTC
      I like both CountZero's and BrowserUK's solutions. They are short and readable. However, since the version I was going to post is similar to BrowserUK's, I have to say I'm a little baised toward his solution. ;) The less code I need to read through to decipher functionality, the better.

      ---
      s;;:<).>|{>?\\^0<|)=|*?=>|!./>(%<|:%<,|(!#+%<{;;y; -/:-@[-`{-};`-{/" -;;s;;$_;see;
      Warning: Any code posted by tuxz0r is untested, unless otherwise stated, and is used at your own risk.

Re: When every 2 lines of a file (sans first) should be 1...
by CountZero (Bishop) on Dec 04, 2007 at 19:27 UTC
    What about this
    use strict; <DATA>; # Throw away the header while (<DATA>) { chomp if not $.%2; print; } __DATA__ Line 1: Header Line 2: Keep me! Line 3: Join me to the previous line. Line 4: Keep me! Line 5: Join me to the previous line. Line 6: Keep me! Line 7: Join me to the previous line. Line 8: Keep me! Line 9: Join me to the previous line.
    Which gives:
    Line 2: Keep me!Line 3: Join me to the previous line. Line 4: Keep me!Line 5: Join me to the previous line. Line 6: Keep me!Line 7: Join me to the previous line. Line 8: Keep me!Line 9: Join me to the previous line.

    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

      Perhaps I had a little extra caffeine today, but I find this solution heartwarming.
Re: When every 2 lines of a file (sans first) should be 1...
by GrandFather (Saint) on Dec 04, 2007 at 19:51 UTC

    There are a few interesting edge cases you may care to take care of. Consider:

    use strict; use warnings; <DATA>; print ''.(chomp, s/\s*$/ /, $_).($_ = <DATA>, s/^\s*(?=\S)//, $_) whil +e <DATA>; __DATA__ Header line First line part 1. Part 2 of first line. Second line parts one and two. Third line - second part empty. Fourth and last line

    Prints:

    First line part 1. Part 2 of first line. Second line parts one and two. Third line - second part empty. Fourth and last line

    which handles a number of cases, but spits warnings if the last line of the final pair is missing.


    Perl is environmentally friendly - it saves trees
Re: When every 2 lines of a file (sans first) should be 1...
by chromatic (Archbishop) on Dec 04, 2007 at 19:11 UTC

    Aren't you missing a chomp? Assuming so, sSomething like:

    my $fh = open_file( 'filename' ); print read_first_line( $fh ); print read_two_lines( $fh ) until eof( $fh ); sub read_first_line { my $fh = shift; return scalar <$fh>; } sub read_two_lines { my $fh = shift; chomp(my $first = <$fh>); return $first . <$fh>; }
      Aren't you missing a chomp?
      I had this:
      s!\n!!g;
      I have beheld the tarball of 22.1 on ftp.gnu.org with my own eyes. How can you say that there is no God in the Church of Emacs? -- David Kastrup
      [tag://sort,etl,data]
Re: When every 2 lines of a file (sans first) should be 1...
by ikegami (Patriarch) on Dec 05, 2007 at 04:49 UTC
    As a one-liner:
    perl -pe"BEGIN{<>} chomp if $.%2==0"

    Reads from STDIN or from the file provided as an argument.

    >type data Line 1: Header Line 2: Keep me! Line 3: Join me to the previous line. Line 4: Keep me! Line 5: Join me to the previous line. Line 6: Keep me! Line 7: Join me to the previous line. Line 8: Keep me! Line 9: Join me to the previous line. >perl -pe"BEGIN{<>} chomp if $. % 2 == 0" data Line 2: Keep me!Line 3: Join me to the previous line. Line 4: Keep me!Line 5: Join me to the previous line. Line 6: Keep me!Line 7: Join me to the previous line. Line 8: Keep me!Line 9: Join me to the previous line.
Re: When every 2 lines of a file (sans first) should be 1...
by graff (Chancellor) on Dec 05, 2007 at 02:02 UTC
    FORE!!
    # sample input: $ ls | head -11 Desktop Documents Downloads Library Movies Music Pictures Public Sites bin cgi-bin # 21-character perl script: $ ls | head -11 | perl -pe 's{$/}{ } unless($.%2)' Desktop Documents Downloads Library Movies Music Pictures Public Sites bin cgi-bin
    =D

    (update: That version replaces the default line termination with a space. If you just want to delete the line termination (first char of line 3 immediately follows last char of line 2, etc), take the space out of the replacement block in the regex, making it a 20-character script.)

    Another update: oops! just noticed that I'm not supposed to output the first line, so I either have to lengthen the perl script, or else lengthen the command line:

    # using a longer perl script: $ ls | head -11 | perl -pe '$_="" if($.==1);s{$/}{ } unless($.%2)' Documents Downloads Library Movies Music Pictures Public Sites bin cgi-bin # using a longer command line: $ ls | head -11 | tail +2 | perl -pe 's{$/}{ } if($.%2)' Documents Downloads Library Movies Music Pictures Public Sites bin cgi-bin
    (The latter allows me to shorten the perl script even more, and so would be my personal favorite.)