As a blanket statement, please check the FWP archives as most of them are being or have been explained. I'll explain how my five entries worked.
Here's head.pl, which prints the first 10 lines of a file.
#!/usr/bin/perl -p
11..exit
The code expands (loosely) to
while (<>) {
11..exit;
} continue { print }
The 11..exit is the flip-flop operator, in void context (which is as good as scalar context). It returns true when the left-hand side is true, and continues doing so until the right-hand side is true. Since the left-hand side is a numeric constant, that value is compared to $. (as is documented). So once $. == 11 is true, Perl sees is exit is true, but that has a side-effect of exiting the program. Thus, the print statement is only reached for the first 10 lines.
Here's tail.pl, which prints the last 10 lines:
#!/usr/bin/perl
print+(<>)[-10..-1]
Not very exotic. It reads all of the file into a list, and then takes the last 10 elements. There is a one-stroke trick here. print(1+2)*3 happens to parse as (print(1+2))*3, so you often need to do print((1+2)*3). But you can save a stroke with the unary + operator: print+(1+2)*3.
Here's a more inventive solution of mine:
#!/usr/bin/perl
map--$.>9||print,<>
This is written more normally as a for-loop:
for (<>) {
print unless --$. > 9;
}
This code reads all of the file into a list to be iterated over (and thus $. is the number of lines read). Then, for each iteration, we subtract one from $. and see if it's greater than 9. If it is, we don't print the line -- otherwise, we do. --$. will be 9 when we get to the 10th-to-last line.
Here's rev.pl -- it's not mine, since mine is the utterly simplistic print reverse <>. It's the shortest entry, and it's genius.
#!/usr/bin/perl -p
$\=$_.$\}{
That code expands to:
while (<>) {
$\ = $_ . $\
} {;
} continue { print }
Do you notice how there is an empty block before the continue now? That moves the printing to the very end of the program (where it will only occur once). We place $_ at the BEGINNING of the $\ variable, so we end up getting the lines in reverse order. When we do print, $_ is empty, but we also print $\! Why? Because it's the output record separator... what a clever variable to use!
Here's mid.pl. It's not the shortest, and it's pretty direct.
#!/usr/bin/perl
@_=<>;print@_[$#_/2..@_/2]
If the number of lines is odd (13), we only print the ONE middle line. That means $#_/2 is going to be (13-1)/2 = 6, and @_/2 is going to be 13/2 = 6.5. The slice [6 .. 6.5] is as good as [6 .. 6]. However, if there are an even number of lines (20), we print the middle two. $#_/2 is then (20-1)/2 = 9.5, and @_/2 is 20/2 = 10; so our slice is [9.5 .. 10], which is just like [9 .. 10].
Finally, here's wc.pl, which I won with my first entry.
#!/usr/bin/perl
printf"%010d\n",$.,<>
This prints the number of lines, zero-padded and right justified, to 10 digits. This makes use of the fact that by the time $. is accessed for its value, the <> has already returned all its lines, so $.'s value reflects that. So we print $. with the format, and "waste" the lines returned by <>.
_____________________________________________________
Jeff[japhy]Pinyan:
Perl,
regex,
and perl
hacker.
s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??; | [reply] [d/l] [select] |