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

Oh wise oh, powerful monks! On p.97 of Llama, merlyn states thusly:

Write a program that acts like cat, but reverses the order of the output lines. (Some systems have a utility like this called tac.) If you run yours as ./tac fred barney betty, the output should be all of file betty from last line to first, then barney then fred, also from last line to first.


I seem to be having trouble reversing the input, but not the files.
#!/usr/bin/perl -w use strict; @ARGV = reverse @ARGV; while (<>) { chomp; print "\n@_\n"; }


I've tried a couple of different approaches that didn't work. Here's an example:
#!/usr/bin/perl -w my $in; @ARGV = ( reverse @ARGV); foreach my $x (@ARGV){ @in = <$x>; @in = reverse @in; } while (@in) { chomp; print "\n$_\n"; }
I get perl complaining of "readline() on unopened file at ./tac.pl line 7."when I tried that solution. This seems simple, but in practice I am stumped. I'd appreciate the help of a more powerful monk.

Many THANKS!

Replies are listed 'Best First'.
Re: Llama ./tac stumper
by davido (Cardinal) on Jun 07, 2004 at 03:39 UTC
    You're pretty close:
    use strict; use warnings; my @lines; foreach my $file ( reverse @ARGV ) { open my $fh, '<', $file or die "Bleah: $!"; my( @slurp ) = <$fh>; push @lines, reverse @slurp; close $fh; } print foreach @lines;

    This could be done with a lot more memory efficiency (without slurping the files) if you take steps to read the file in backwards in the first place (using File::ReadBackwards or Tie::File). But I kept it to stuff that you would probably find explained in the Llama book.

    You usually automagically open the contents of @ARGV with the bare <> operator. Since I wanted to treat the files in reverse order, I didn't use <>'s magic, and instead opened the files explicitly. Actually that step could have been avoided if I had reversed the contents of @ARGV first, and then still relied upon <> to do its magic.

    UPDATE: I wanted to go ahead and show how it is simplified by letting <> do its magic. Here goes:

    use strict; use warnings; my @lines; @ARGV = reverse @ARGV; foreach ( @ARGV ) { my( @slurp ) = <>; push @lines, reverse @slurp; } print foreach @lines;

    Enjoy!


    Dave

      Unless I'm badly confused, I'm pretty sure that foreach( @ARGV ){ <> } isn't doing what you want. <> in list context should open every file in @ARGV, read all the lines, and return them.
      @ARGV=reverse @ARGV; print reverse <>;
        My apologies on botching the 2nd solution. Here's an update:

        use strict; use warnings; my( @slurp ) = <>; @slurp = reverse @slurp; print @slurp;

        Dave

Re: Llama ./tac stumper (GOLF)
by BrowserUk (Patriarch) on Jun 07, 2004 at 05:18 UTC

    42.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
      That is more complex than you need.

        Agreed. The only downside is that all the files to be reversed have to be in memory simultaneuosly, but that probably ok for golf :)


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "Think for yourself!" - Abigail
Re: Llama ./tac stumper
by ggg (Scribe) on Jun 07, 2004 at 14:15 UTC
    On p.256, Appendix A of Llama, merlyn states thusly:
    print reverse <>;
    ggg