tags:

views:

421

answers:

2

I'm reading the Llama (Learning Perl) book, and working on the exercises. And for this exercise:

Write a program that reads a list of strings on separate lines until end-of-input and prints out the list in reverse order. [. . .]

Well, I already figured out a simpler approach (I remembered you could use reverse on arrays... Perl is so... awesome so far), but I am wondering why this one isn't working.

#!/usr/bin/perl

use 5.010;

chomp(@strings = <STDIN>);

foreach (@strings){
    push @revstrings, $_;
}

while($i++ <= scalar @revstrings){
    say pop @revstrings;
}

It goes like this:

$ ./first
one
two
three

[^D]
three
two
$

the output is the same if I change the <= in the while loop to just <.

+14  A: 

You'll never get past halfway... Each time through that last iteration, you'd get:

  • $i++ means that $i will increase by one;
  • pop @revstrings will mean that scalar @revstrings will decrease by one.

They'll meet in the middle, when $i++ just exceeds half the original @revstrings length.

Actually, the $i++ is unnecessary, since scalar @revstrings will be zero when the array is empty, so you just need:

while(scalar @revstrings){
    say pop @revstrings;
}
Stobor
No need for the 'scalar' either. The while evaluates the conditional in boolean (scalar) context. So while( @revstrings ) { ... } will do the trick.
daotoad
@daotoad: +1 good point.
Stobor
`*smacks forehead*`. for some reason I assumed @revstrings would get evaluated only once. Haha, that was a stupid mistake--I had the same problem before with a different structure (I forget what), so I thought I would just use $i++ so that it was just an unrelated count. If I stored the length of @revstrings in a variable and then used that in the loop, I guess it would have worked. Thanks though
Carson Myers
You don't need the second array either: `say pop @strings while (@strings);`
Telemachus
@Telemachus: Damnit, you're right; foreach (@strings){ push @revstrings, $_;} is equivalent to @revstrings = @strings;
Stobor
also, how did you get code tags in your comment?
Stobor
@Stobor - As of a few days ago, you can use backticks to produce code in comments (just as in a post). I don't think it always worked, but it does now. I keep meaning to check if the format rules for comments are documented anywhere, but I haven't yet.
Telemachus
I think it doesn't always work. I just tried it (posted my comment three times!). I wonder if there's some magic needed. ``backticks format $variables in perl <nicely>``
Stobor
Hrm. That's weird.
Stobor
@Telemachus: Damnit, you're right; `` foreach (@strings){ push @revstrings, $_; } `` is equivalent to ``@revstrings = @strings; ``
Stobor
Okay, I swear it wasn't doing that before. And now it only requires 1 backtick? :-S /me is confused.
Stobor
Hasn't it always required one backtick?And damn! I figured, push them on a stack and pop them off in reverse order... I guess I should have thought about this one a little harder before asking :)
Carson Myers
I've always done two backticks, but maybe that wasn't necessary. As for the stack, Perl arrays are already built to handle all that for you. But if you're used to another language, it's natural to expect to have to do it yourself. I'm learning C now (Perl was my first language), and I'm having the reverse problem. I keep wanting there to be a built-in way for me to do things.
Telemachus
A: 

EDIT: Short answer is "Because your loop condition is wrong." more verbose "scalar @revstrings is evaluated each iteration".

while (<STDIN>) {
    push @lines, $_
}

while(@lines){
    print pop @lines;
}

less typing

@lines = <STDIN>;

while(@lines){
    print pop @lines;
}

lesser typing

@lines = <STDIN>;
print reverse @lines;

lesser typing

print reverse <>;

but far best solution is

exec("tac");
Hynek -Pichi- Vychodil
It's not the best solution if your goal is _to learn Perl_. Not to mention that `tac` isn't present on all systems. I.e., the solution isn't portable.
Telemachus
What happened to `print reverse <STDIN>`?
Cirno de Bergerac
Ok, I got curious: `tac` is mentioned in the question and the answer is `print reverse <>`. But, again, if he's trying to learn Perl, he also needs to know why his answer _didn't_ work. (_Learning Perl_ chapter 5 if you're playing along at home.)
Telemachus
OK, so short answer is "Because your loop condition is wrong." more verbose "scalar @revstrings is evaluated each iteration".
Hynek -Pichi- Vychodil
`sub tac { tac() if defined(my $line=<>); print $line } tac`
ysth
"`print reverse <>`" Oh wait, ... we're not going for Perl-golf.
Brad Gilbert