Вытащено из кэша Yahoo, у Google уже не было. Специально для Habrahabr.ru
10 Perl One Liners to Impress(?) Your Friends
published 05 Jun 2011On May 31st, Marcus Kazmierczak posted 10 one-liners in Scala, each designed to show off a strength of the language. It was quickly Rosetta Stoned into many other languages including Python and Ruby.
The title is a bit of a misnomer, as the one-liners aren’t particularly impressive (and sometimes aren’t even “one-liners” in the Perl sense). I thought I would convert them to Perl mainly to compare with other languages.
Update: I’ve incorporated some improvements from Evan Carroll (see his Disqus post below). Thanks Evan!
1. Multiply each item in a list by 2
say $_ * 2 for 1..10;
If assigning to an array, you can use map: map { $_ * 2 } 1..10 compares favourably with Scala’s (1 to 10) map { _ * 2 }.
2. Sum a list of numbers
We’ll use List::Util which comes with Perl.
use List::Util qw(sum);
say sum 1..1000;
Alternately, a true one-liner:
perl -MList::Util=sum -E 'say sum(1..1000)'
Using reduce
from the same module:
say reduce { $a + $b } 1..1000;
3. Check if word exists in a string
This is a Perlish solution:
my @wordlist = ("scala", "akka", "play framework", "sbt", "typesafe");
my $tweet = "This is an example tweet talking about scala and sbt.";
say for grep { $tweet =~ /\b$_\b/ } @wordlist;
The \b
thingies here represent ‘word boundaries’, which include whitespace but also punctuation. This allows us to match ‘scala’ (surrounded by spaces) but also ‘sbt’ (wedged between a space and a period).
You can also use split
:
my @tweet_words = split /\W/, $tweet;
say for grep { $_ ~~ @tweet_words } @wordlist;
The \W
splits on anything that isn’t a ‘word’ (UTF-8 fans are going to harang me for this; it’s blatantly wrong). The ~~
smart-match operator is like Python’s in
: it looks for the word in the array.
If you have a large number of words to search against, it’s better to use a hash:
my %tweet_words;
$tweet_words{$_} = 1 for split /\W/, $tweet;
say for grep { $tweet_words{$_} } @wordlist;
4. Read in a file
use File::Slurp qw(slurp);
my $file = slurp("filename"); # read_file also works
Before File::Slurp people came up with all kinds of abominations to do this:
my $file = do { local(@ARGV,$/)="filename";<>};
The above code unsets $/ (the input record separator), so Perl reads everything in at once. It sets @ARGV to “filename” so that the <> operator reads that file in (saving a call to open). On the command line, the -0777 option does the same job (see perlrun):
perl -0777E 'my $file = <>; print $file' filename
5. Happy birthday to you
say "Happy Birthday to " . ($_ == 2 ? "dear Name" : "you") for 0..3;
6. Filter list of numbers
part
partitions the input into two boxes based on the result of $_ > 60: box 0 (false) or box 1 (true).
use List::MoreUtils qw(part);
my ($failed, $passed) = part { $_ > 60 } (49, 58, 76, 82, 88, 90);
It’s more common to grep
for one case at a time.
my @passed = grep { $_ > 60 } (...);
7. Fetch and parse an XML service
Perl tries not to care that you use XML, but it hates you a little inside.
use LWP::Simple qw(get);
use XML::Simple;
my $data = XMLin(get("http://search.twitter.com/search.atom?&q=xml+sucks"));
8. Minimum and maximum of a list
minmax
does both in one go:
use List::MoreUtils qw(minmax);
my ($min, $max) = minmax @LIST;
9. Parallel processing
I really like Scala’s implementation of “parallel collections”, which is worth mentioning:
val result = dataList.par.map(line => processItem(line))
This is the best I could find on CPAN:
use Parallel::Iterator qw(iterate_as_array);
my @result = iterate_as_array( sub { processItem($_[1]) }, \@dataList );
Parallel::Iterator sounds like an awesome module, so give it a try if you need to do some multi-core computation!
10. Sieve of Eratosthenes
Okay, I’m getting bored of all this readable Perl code. Let’s bust out the big guns, courtesy of tye and tilly:
sub sieve3 {
grep{@_[map$a*$_,2..@_/($a=$_)]=0if$_[$_]>1}@_=0..pop
}
print join " ", sieve(100);
And since we’re talking about Perl and primes, I can’t help but mention Abigail’s regular expression for testing prime numbers (!):
perl -wle 'print "Prime" if (1 x shift) !~ /^1?$|^(11+?)\1+$/' [number]
(How it works.)
Conclusion
Blindly translating examples from one script to another feels like a kind of pan-language pissing contest, but can be educational and shows how Perl deals with domains it wasn’t originally designed for. In that respect, Perl does quite well considering how venerable the language is. Perl may not have as many built-ins as other languages, particularly in the functional domain, but CPAN can fill in for virtually anything that’s missing.
Add New Comment
- Post as jkeks
Image
Share on:
Twitter
Sort by popular now Sort by best rating Sort by newest first Sort by oldest first
Showing 4 comments
Most of these aren't one liners, they're just expressions. Most of them can also be written better...
Here you're iterating twice for no reason..
perl -E 'say for map { $_ * 2 } 1..10'
Just do, -E'say $_*2 for 1..10'
Your algo for checking if a word exists in a string is slow. If one liners don't matter...
my %tweet_words;
$tweet_words{$_}=1 for split /\W/, $tweet;
say $tweet_words ? "found [$_]" : "not found [$_"] for @wordlist;
List::Util is nice, but for one liners this will achive the same effect.
perl -00E'my $foo = <>; print $foo' foobar.txt
Again, you seem to be hellset on using `for` and `map`... you should pick.
say for map { "Happy Birthday to " . ($_ == 2 ? "dear Name" : "you") } 0..3;
say "Happy Birthday to " . ($_ == 2 ? "dear Name" : "you") for 0..3;rjh29
I used map because it's a direct analog to the Scala solution in the original article - then tacked a 'say for' at the beginning and didn't think about it. D'oh! Same deal with using smart-match rather than a hash lookup (because other languages have an 'in' operator). Thanks, though! Do you mind if I add your improvements to the article?
Btw, checking 'exists $tweet_words{$_}' seems to be about 5-10% faster than not using exists on my machine (Perl 5.10.1 on Ubuntu 10.04).the fastest thing is to set the hash value to undef which doesn't have ref counting, and is an internal pointer to SV_UNDEF, and to use exists(). However, that's reasonably more ugly. And, the point is you can at least stay O(n) with a hash, non-set array intersections are always O(n**2) which gets bad fast.
You can use all of the suggestions, also this rule is universal. One loop is always better.
Not:
say for grep { $tweet =~ /\b$_\b/ } @wordlist;
But:
for (@wordlist) { say if /\b$_\b/ }Craig DeForestCollapse
It's interesting that so many of your examples are numeric but you haven't tried "use PDL;", which loads a scientific grade numerical processing package. PDL minmax is between 30 and 150 times faster than List::MoreUtils::minmax (it gets the gain by working on traditional structured arrays of numbers rather than on Perl lists of polymorphous scalars).
Trackback URL
Richard Harris 2011 ©