Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

Re: Uninitialized value in reverse

by davido (Cardinal)
on Apr 24, 2020 at 21:10 UTC ( #11116049=note: print w/replies, xml ) Need Help??


in reply to Uninitialized value in reverse

Consider this: while(<DATA>) is about the same as while(defined($_ = <DATA>)). We should be able to agree that there are two pieces of magic going on here. The first is the implicit assignment to $_, and the second is the implicit defined test. perlop can help shed some light. The implicit assignment is interesting:

If and only if the input symbol is the only thing inside the conditional of a while statement (even if disguised as a for(;;) loop), the value is automatically assigned to the global variable $_

That explains one of the bugs in your code. You have more than just the input operator in your while(...) construct. Therefore, there is no implicit assignment to $_. Your loop's body expects there to be a useful value in $_, but there isn't, because it's not being assigned to.

Next consider reverse. Perl's reverse is actually a rather bizarre operator in how it handles context. The context of a conditional expression in a while(COND) construct is scalar context. Therefore, you get this behavior, as described in reverse:

reverse LIST ... in scalar context, concatenates the elements of LIST and returns a string value with all characters in the opposite order.

The documentation provides this example:

print scalar reverse "dlrow ,", "olleH"; # Hello, world

All the lines get concatenated together, and then everything is reversed. So if you resolved the first bug, you would have the second; that instead of getting the lines in reverse, you get the entire slurped in file's characters in reverse.

The third bug is a little harder; Since you're pulling in the entire file in one fell swoop, your loop body will only execute once. On the second pass <DATA> will return undef.

Your goal, I believe, is to read each line in reverse order. There are two reasonable ways to do this, based on how big the input file is. If the file doesn't ever grow too big, just slurp the whole thing in: my @lines = reverse <DATA>. This invokes reverse in LIST context, so that it leaves the character order for each line in tact, and just reverses the order of the lines. But if the file could possibly grow large enough that memory is a consideration, use File::ReadBackwards. This example is a little awkward since File::ReadBackwards isn't really designed for reading the __DATA__ segment. But it should convey the simplicity of using this module:

#!/usr/bin/env perl use strict; use warnings; use FindBin qw($Bin); use File::Spec::Functions qw(catfile); use File::ReadBackwards; my $bw = File::ReadBackwards->new(catfile($Bin,$0)) or die "Can't read $0: $!\n"; while( defined(my $line = $bw->readline) ) { last if $line =~ m/^__(?:END|DATA)__/; print $line; } __DATA__ 1234 5678 9ABC DEFG

This produces:

DEFG 9ABC 5678 1234

The File::ReadBackwards module reads the file in chunks of 8192 bytes, seeking from the end upward. Therefore, it never needs to hold the entire file in memory at once.


Dave

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://11116049]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (3)
As of 2021-04-18 17:41 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?