This article is part of the article series "Perl One-Liners Explained."
<- previous article next article ->
Perl One Liners

This is the third part of a seven-part article on famous Perl one-liners. In this part I will create various one-liners for calculations. See part one for introduction of the series.

Famous Perl one-liners is my attempt to create "perl1line.txt" that is similar to "awk1line.txt" and "sed1line.txt" that have been so popular among Awk and Sed programmers.

The article on famous Perl one-liners will consist of at least seven parts:

After I'm done explaining all these one-liners, I'll publish an ebook. Subscribe to my blog to know when that happens!

The one-liners will make heavy use of Perl special variables. A few years ago I compiled all the Perl special variables in a single file and called it Perl special variable cheat-sheet. Even tho it's mostly copied out of perldoc perlvar, it's still handy to have in front of you, so print it.

Awesome news: I have written an e-book based on this article series. Check it out:

And here are today's one-liners:

Calculations

21. Check if a number is a prime.

perl -lne '(1x$_) !~ /^1?$|^(11+?)\1+$/ && print "$_ is prime"'

This one-liner uses an ingenious regular expression to detect if a given number is a prime or not. Don't take it too seriously, though. I included it for its artistic value.

First, the number is converted in its unary representation by " (1x$_) ". For example, 5 gets converted into " 1x5 ", which is " 11111 ".

Next, the unary number gets tested against the ingenious regular expression. If it doesn't match, the number is a prime, otherwise it's a composite.

The regular expression works this way. It consists of two parts " ^1?$ " and " ^(11+?)\1+$ ".

The first part matches " 1 " and empty string. Clearly, empty string and 1 are not prime numbers, therefore this regular expression matches, which indicated that they are not prime numbers.

The second part determines if two or more 1s repeatedly make up the whole number. If two or mores 1s repeatedly make up the whole number, the regex matches, which means that the number is composite. Otherwise it's a prime.

Let's look at the second regex part on numbers 5 and 6.

The number 5 in unary representation is " 11111 ". The " (11+?) " matches first two ones " 11 ". The back-reference " \1 " becomes " 11 " and the whole regex now becomes " ^11(11)+$ ". It can't match five ones, therefore it fails. But since it used " +? ", it backtracks and matches the first three ones " 111 ". The back-reference becomes " 111 " and the whole regex becomes " ^111(111)+$ ". It doesn't match again. This repeats for " 1111 " and " 11111 ", which also don't match, therefore the whole regex doesn't match and the number is a prime.

The number 4 in unary representation is " 1111 ". The " (11+?) " matches the first two ones " 11 ". The back-reference " \1 " becomes " 11 " and the regex becomes " ^11(11)+$ ". It matches the original string, therefore the number is not a prime.

The " -lne " command line options have been explained in parts one and two.

22. Print the sum of all the fields on a line.

perl -MList::Util=sum -alne 'print sum @F'

This one-liner turns on field auto-splitting with " -a " command line option and imports the "sum" function from "List::Util" module with " -MList::Util=sum " option. The "List::Util" is in the Perl core so you don't need to worry about installing it.

As a result of auto-splitting the split fields end up in the " @F " array and the " sum " function just sums them up.

The -Mmodule=arg option imports arg from module and is the same as writing:

use module qw(arg)

This one-liner is equivalent to the following:

use List::Util qw(sum);
while (<>) {
    @F = split(' ');
    print sum @F, "\n";
}

23. Print the sum of all the fields on all lines.

perl -MList::Util=sum -alne 'push @S,@F; END { print sum @S }'

This one-liner keeps pushing the split fields in " @F " to the " @S " array. Once the input is over and perl is about quit, END { } block gets called that outputs the sum of all items in @F. This sum is the sum of all fields over all lines.

This solution isn't too good - it creates a massive array @S. A better solution is to keep just the running:

perl -MList::Util=sum -alne '$s += sum @F; END { print $s }'

24. Shuffle all fields on a line.

perl -MList::Util=shuffle -alne 'print "@{[shuffle @F]}"'

This is almost the same as one-liner #22 above. Instead of summing all fields, it shuffles and prints them.

The " @{[shuffle @F]} " construct creates an array reference to the contents of " shuffle @F " and " @ { ... } " dereferences it. This is a tricky way to execute code inside quotes. It was needed to get the values of shuffled @F separated by a space when printing them out.

Another way to do the same is join the elements of @F by a space, but it's longer:

perl -MList::Util=shuffle -alne 'print join " ", shuffle @F'

25. Find the minimum element on a line.

perl -MList::Util=min -alne 'print min @F'

This one-liner uses the "min" function from "List::Util". It's similar to all the previous ones. After the line has been automatically split by " -a ", the "min" function finds minimum element and prints it.

26. Find the minimum element over all the lines.

perl -MList::Util=min -alne '@M = (@M, @F); END { print min @M }'

This one-liner is a combination of the previous one and the #23.

The "@M = (@M, @F)" construct is the same as "push @M, @F". It appends the contents of @F to the array @M.

This one-liner stores all the data in memory. If you run it on a 10 terabyte file, it will die. Therefore it's better to keep the running minimum element in memory and print it out at the end:

perl -MList::Util=min -alne '$min = min @F; $rmin = $min unless defined $rmin && $min > $rmin; END { print $rmin }'

It finds the minimum of each line and stores in $min, then it checks if $min is smaller than the running minimum. Once the input ends, it prints the running minimum, which is the smallest value over all input.

27. Find the maximum element on a line.

perl -MList::Util=max -alne 'print max @F'

This is the same as #25, except "min" has been replaced with "max".

28. Find the maximum element over all the lines.

perl -MList::Util=max -alne '@M = (@M, @F); END { print max @M }'

This is the same as #26.

Or:

perl -MList::Util=max -alne '$max = max @F; $rmax = $max unless defined $rmax && $max < $rmax; END { print $rmax }'

29. Replace each field with its absolute value.

perl -alne 'print "@{[map { abs } @F]}"'

This one-liner auto-splits the line by " -a " command line option. The split fields, as I already explained, end up in the @F variable. Next it calls the absolute value function "abs" on each field by the help of "map" function. Finally it prints it joins all the fields by the help of array interpolation in double quotes.

The " @{ ... } " construct was explained in one-liner #24.

30. Find the total number of fields (words) on each line.

perl -alne 'print scalar @F'

This one-liner forces to evaluate the @F in scalar context, which in Perl means "the number of elements in @F." Therefore this one-liner prints out the number of elements on each line.

31. Print the total number of fields (words) on each line followed by the line.

perl -alne 'print scalar @F, " $_"'

This is exactly the same as #30, except " $_ " is added at the end that prints out the whole line. (Remember that " -n " option caused each line to be put in the $_ variable.)

32. Find the total number of fields (words) on all lines.

perl -alne '$t += @F; END { print $t}'

Here we just keep adding the number of fields on each line to variable " $t ", and at the end we print it out. The result is number of words on all lines.

33. Print the total number of fields that match a pattern.

perl -alne 'map { /regex/ && $t++ } @F; END { print $t }'

This one-liner uses the " map " function that applies some operation on each of the elements in @F array. In this case the operation checks if each element matches /regex/ and if it does, it increments variable $t. At the end it prints this variable $t that contains the number of fields that matched /regex/ pattern.

A better way to do it is by looping:

perl -alne '$t += /regex/ for @F; END { print $t }'

Each element in @F is tested against regex. If it matches, /regex/ returns 1 (true), which gets added to variable $t. This way the number of matches get counted in $t.

The best way is to use grep in scalar context:

perl -alne '$t += grep /regex/, @F; END { print $t }'

Grep in scalar context returns the number of matches. This number gets accumulated in $t.

34. Print the total number of lines that match a pattern.

perl -lne '/regex/ && $t++; END { print $t }'

The /regex/ evaluates to true if the current line of input matches this regular expression. Writing /regex/ && $t++ is the same as if ($_ =~ /regex/) { $t++ }, which increments variable $t if the line matched the pattern. Finally in the END block the variable $t contains the total number of pattern matches and it gets printed out.

35. Print the number PI to n decimal places.

perl -Mbignum=bpi -le 'print bpi(21)'

The bignum package exports bpi function that calculates constant PI to wanted accuracy. This one-liner prints PI to 20 decimal places.

The bignum library also exports constant PI alone to 39 decimal places:

perl -Mbignum=PI -le 'print PI'

36. Print the number E to n decimal places.

perl -Mbignum=bexp -le 'print bexp(1,21)'

The bignum library also exports bexp function that takes two arguments - the power to raise e to and accuracy. This one-liner prints the constant e to 20 decimal places.

You can print the value of e^2 to 30 decimal places this way:

perl -Mbignum=bexp -le 'print bexp(2,31)'

Just the same as with PI, bignum exports the constant e alone to 39 decimal places:

perl -Mbignum=e -le 'print e'

37. Print UNIX time (seconds since Jan 1, 1970, 00:00:00 UTC).

perl -le 'print time'

The built-in function "time" returns seconds since the epoch.

38. Print GMT (Greenwich Mean Time) and local computer time.

perl -le 'print scalar gmtime'

The "gmtime" function is a Perl built-in function. If used in scalar context, it prints the time localized to Greenwich time zone.

perl -le 'print scalar localtime'

The "localtime" built-in function acts the same way as "gmtime", except it prints the computer's local time.

In array context both "gmtime" and "localtime" return a 9 element list (struct tm) with the following elements.

($second,             [0]
$minute,              [1]
$hour,                [2]
$month_day,           [3]
$month,               [4]
$year,                [5]
$week_day,            [6]
$year_day,            [7]
$is_daylight_saving   [8]
)

You may slice this list, or print individual elements if you need just some part of this information.

For example, to print H:M:S, slice elements 2, 1 and 0 from localtime:

perl -le 'print join ":", (localtime)[2,1,0]'

39. Print yesterday's date.

perl -MPOSIX -le '@now = localtime; $now[3] -= 1; print scalar localtime mktime @now'

Remember that localtime returns a 9-list (see above) of various date elements. The 4th element in the list is current month's day. If we subtract one from it we get yesterday. The "mktime" function constructs a Unix epoch time from this modified 9-list. And "scalar localtime" construct prints out the new date, which is yesterday.

The POSIX package was needed because it exports mktime function. It's supposed to normalize negative values.

40. Print date 14 months, 9 days and 7 seconds ago.

perl -MPOSIX -le '@now = localtime; $now[0] -= 7; $now[4] -= 14; $now[7] -= 9; print scalar localtime mktime @now'

This one-liner modifies 0th, 4th, and 7th elements of @now list. The 0th is seconds, the 4th is months and 7th is days (see the table of 9 element time list above).

Next, mktime creates Unix time from this new structure, and localtime, evaluated in scalar context, prints out the date that was 14 months, 9 days and 7 seconds ago.

41. Calculate factorial.

perl -MMath::BigInt -le 'print Math::BigInt->new(5)->bfac()'

This one-liner uses bfac() function from Math::BigInt module that is in the Perl core (no need to install).

Math::BigInt->new(5) construction creates a new Math::BigInt object with value 5, then a method bfac() is called on the newly created object to calculate the factorial of 5. Change 5 to any number you wish to find factorial for the value you are interested in.

Another way to calculate factorial is by just multiplying numbers from 1 to n together:

perl -le '$f = 1; $f *= $_ for 1..5; print $f'

Here we initially set $f to 1. Then do a loop from 1 to 5 and multiply $f by each of the values. The result is 1*2*3*4*5, which is the factorial of 5.

42. Calculate greatest common divisor.

perl -MMath::BigInt=bgcd -le 'print bgcd(@list_of_numbers)'

Math::BigInt has several other useful math functions. One of them is bgcd that calculates the greatest common divisor of a list of numbers.

For example, to find gcd of (20, 60, 30), you'd execute the one-liner this way:

perl -MMath::BigInt=bgcd -le 'print bgcd(20,60,30)'

Surely, you can also use Euclid's algorithm. Given two numbers $n and $m, this one-liner finds the gcd of $n and $m. The result is stored in $m.

perl -le '$n = 20; $m = 35; ($m,$n) = ($n,$m%$n) while $n; print $m'

43. Calculate least common multiple.

Another function from Math::BigInt is lcm - the least common multiplicator. This one-liner finds lcm of (35, 20, 8):

perl -MMath::BigInt=blcm -le 'print blcm(35,20,8)'

If you know some number theory, then you'll recall that there is a connection between gcd and lcm. Given two numbers $n and $m, their lcm is $n*$m/gcd($n,$m), therefore one-liner follows:

perl -le '$a = $n = 20; $b = $m = 35; ($m,$n) = ($n,$m%$n) while $n; print $a*$b/$m'

44. Generate 10 random numbers between 5 and 15 (excluding 15).

perl -le '$n=10; $min=5; $max=15; $, = " "; print map { int(rand($max-$min))+$min } 1..$n'

You can modify this one-liner by changing variables $n, $min, $max. The variable $n stands for how many random numbers to generate, and [$min,$max) is the generation range.

The variable $, gets set to a space because it's the output field separator for print and it's undef by default. If we didn't set it to a space, the numbers would get printed concatenated together.

45. Find and print all permutations of a list.

perl -MAlgorithm::Permute -le '$l = [1,2,3,4,5]; $p = Algorithm::Permute->new($l); print @r while @r = $p->next'

This one-liner uses the object-oriented interface of Algorithm::Permute module to find the permutations (all ways to rearrange items).

The constructor of Algorithm::Permute takes an array reference to an array of elements to permute. In this particular one-liner the elements are numbers 1, 2, 3, 4, 5.

The next object function returns the next permutation. Calling it repeatedly iterates over all permutations. Each permutation is put in @r array and is then printed.

Please note that the output list gets large really quickly. There are n! permutations for a list of n elements.

Another way to print out all permutations is to use the exported permute subroutine:

perl -MAlgorithm::Permute -le '@l = (1,2,3,4,5); Algorithm::Permute::permute { print "@l" } @l'

46. Generate the power set.

perl -MList::PowerSet=powerset -le '@l = (1,2,3,4,5); for (@{powerset(@l)}) { print "@$_" }'

Here I use the List::PowerSet module from CPAN.

It exports the powerset function, which takes a list of elements and returns a reference to a list containing references to subset lists.

In the for() loop, I call the powerset function, pass it the list of elements in @l. Next I dereference the return value of powerset, which is a reference to a list of subsets. Next, I dereference each individual subset @$_ and print it.

For a set of n elements, there are exactly 2n subsets in the powerset.

47. Convert an IP address to unsigned integer.

perl -le '$i=3; $u += ($_<<8*$i--) for "127.0.0.1" =~ /(\d+)/g; print $u'

This one-liner converts the IP address 127.0.0.1 into unsigned integer (which happens to be 2130706433).

It does it by first doing a global match of (\d+) on the IP address. Doing a for loop over a global match iterates over all the matches. These matches are the four parts of the IP address.

Next the matches are added together in the $u variable, with first being bit shifted 8*3 = 24 places, the second being shifted 8*2 = 16 places, the third 8 places and the last just getting added to $u.

But this one-liner doesn't do any error checking on the format of an IP address. You may use a more sophisticated regular expression to add checking, such as /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/g.

I had a discussion about this with a friend and we came up with several more one-liner:

perl -le '$ip="127.0.0.1"; $ip =~ s/(\d+)\.?/sprintf("%02x", $1)/ge; print hex($ip)'

This one-liner utilizes the fact that 127.0.0.1 can be easily converted to hex as 7f000001 and then converted to decimal from hex by the hex Perl function.

Another way is to use unpack:

perl -le 'print unpack("N", 127.0.0.1)'

This one-liner is probably as short as it can get. It uses the vstring literals (version strings) to express the IP address. A vstring forms a string literal composed of characters with the specified ordinal values. Next, the newly formed string literal is unpacked into a number from a string in Network byte order (Big-Endian order) and it gets printed.

If you have a string with an IP (and not a vstring), then you first have to convert the string with the function inet_aton to byte form:

perl -MSocket -le 'print unpack("N", inet_aton("127.0.0.1"))'

Here inet_aton converts the string " 127.0.0.1 " to the byte form (which is the same as pure vstring 127.0.0.1) and next it unpacks it as the same was as in previous one-liner.

If you want a reference of pack and unpack templates (such as "N" for Network order), get my Perl pack/unpack cheat sheet!

48. Convert an unsigned integer to an IP address.

perl -MSocket -le 'print inet_ntoa(pack("N", 2130706433))'

Here the integer 2130706433 first gets packed into a number in Big-Endian and then it gets passed to inet_ntoa function that converts a number back to an IP address.

Another way to do it is by bit shifting and printing one byte at a time:

perl -le '$ip = 2130706433; print join ".", map { (($ip>>8*($_))&0xFF) } reverse 0..3'

And by the way, join "." can be replaced by the special variable $, that acts as a value separator for print statement:

perl -le '$ip = 2130706433; $, = "."; print map { (($ip>>8*($_))&0xFF) } reverse 0..3'

See my Perl special variable cheat sheet for the list of all variables.

Perl one-liners explained e-book

I've now written the "Perl One-Liners Explained" e-book based on this article series. I went through all the one-liners, improved explanations, fixed mistakes and typos, added a bunch of new one-liners, added an introduction to Perl one-liners and a new chapter on Perl's special variables. Please take a look:

Have Fun!

Have fun with these one-liners for now. The next part is going to be about string and array creation.

Can you think of other calculations that I did not include here?

update: 2009.11.07 added printing PI and E (one liners 35 and 36).
update: 2009.11.15 added date calculations (one liners 37, 38, 39, 40).
update: 2009.12.14 added factorial, gcd, lcm, random numbers, (one liners 41, 42, 43, 44).
update: 2009.12.26 added permutations, power sets (one liners 45, 46).
update: 2009.12.27 added ip address calculations (one liners 47, 48).

This article is part of the article series "Perl One-Liners Explained."
<- previous article next article ->

Comments

Daniel Permalink
November 03, 2009, 20:38

Hi Peter,

The prime number one liner with the regexp is very interesting and surprisingly short.

I'm not a perl programmer, but at first glance I thought the regexp solution would be very slow (because the "base 1" representation would take a lot of space) and I thought I could write something slightly longer, but faster.

So, I came up with this thing:

perl -MList::Util=max -lne 'foreach (max(2,max(keys %p)+1)..$_) {$i=$_;scalar(grep(($i % $_) == 0, keys %p)) == 0 and $p{$i}=1}; $p{$_} and print "$_ is prime"'

which is more like two full lines on my terminal, but uses a set of already found primes (%p) and only does computations if the input line contains a number larger than all previous (so it should be better, right?..).

Well, guess what -- when testing with larger numbers, your regexp returned results instantly while my code needed 5 seconds for 10000 and about half a minute for 20000!..

So, maybe in Perl shorter does mean faster, too.

MAK Permalink
November 03, 2009, 21:10

That regex prime check is much faster in practice than I might have thought. Thanks for a great article.

November 03, 2009, 21:31

The concept of one line of code needing to be explained doesn't sound like a good thing to me.

ohlolsyswritenow Permalink
November 26, 2013, 12:59

too right

November 03, 2009, 23:27

Daniel, good reporting. I did not benchmark it or write any other prime testing algorithm. Good to know that it's pretty fast.

MAK, you're welcome!

mebigfatguy, I explain it to teach anyone who doesn't know this and wants to learn.

Chii Permalink
November 04, 2009, 07:29

Unfortunately, the prime check onliner doesn't work for primes that are too large (eg, any primes on this page http://primes.utm.edu/lists/small/small3.html ). I suspect the backtracker overflows?

G_REMY Permalink
November 05, 2009, 00:08

That's really great. This style of writing reminds me APL or J (Jay).
For example I wrote this in less than 5 minutes in J:
(0 -.~ ] * 1 - ] e. [: ~. [: , */~ ) 2 + i.100
This short program gives the first 100 prime numbers. I have just some questions : is it easy to write one-line codes in Perl ? fast ? useful ?

November 05, 2009, 07:47

WHY THE HELL DO YOU HAVE TO BE SO DAMN COOL/SMART P! I LOVE YOU AND ALL YOUR PERLS.

Your friend,

-dfloss

November 05, 2009, 14:43

Hello Peteris,

The prime check is nice. It's been around since early 2000, see http://www.perlmonks.org/?node_id=21580

November 05, 2009, 17:05

I forgot to mention that if you need to test primality of a number you use Math::Primality from CPAN. The mentioned regex is(IMHO) just for fun.

November 06, 2009, 10:16

What a pity you don't write it with awk instead of perl.

November 07, 2009, 08:22

Hi Peteris!

Thanks for this post.

A few notes about your solutions:

1. Your primality check may be short but it has awful complexity:

shlomi:~$ time factor 5612912
5612912: 2 2 2 2 53 6619
0.00user 0.00system 0:00.00elapsed 50%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+242minor)pagefaults 0swaps
shlomi:~$ time perl -le '$_=shift; (1x$_) !~ /^1?$|^(11+?)\1+$/ && print "$_ is prime"' 5612912
2.76user 0.02system 0:02.79elapsed 99%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+3919minor)pagefaults 0swaps
shlomi:~$

See this post to Hackers-IL.

perl -MList::Util=sum -alne 'push @S,@F END { print sum @S }'

You do realise you are constructing an incredibly large array (and arrays in Perl have a lot of memory overhead), and then summing it all at once? Furthermore you are missing a semicolon between "@F" and "END":

shlomi:~$ perl -MList::Util=sum -alne 'push @S,@F END { print sum @S }'
Bareword found where operator expected at -e line 1, near "@F END"
        (Missing operator before END?)
syntax error at -e line 1, near "@F END "
syntax error at -e line 1, near ";}"
Execution of -e aborted due to compilation errors.

I suggest the following instead:

perl -MList::Util=sum -alne '$s += sum @F; END { print $s }'

Though one should consider using a big numbers package here.

I'm posting what I have now so I'll be sure the comment will be OK. (Got no comment preview after all).

November 07, 2009, 14:09

Hi Shlomi Fish. I updated the article with your corrections and comments. Thanks! I also just added two new one-liners for calculating the constants PI and E.

Mauro Permalink
December 27, 2009, 14:43

Peter, item 33 and item 34 have the same description!

December 27, 2009, 14:55

Mauro, wow. I don't know how that happened. Going to fix it right now. Thanks!

Update: fixed! :)

Maxim Yemelyanov Permalink
November 20, 2011, 04:33

I have these shell aliases to quickly convert between IP and integer representations of the IP:

alias ip_to_number='perl -le '\''print unpack "N", pack "C4", split /\./, shift'\'''
alias number_to_ip='perl -le '\''print join ".", unpack "C4", pack "N", shift'\'''

ohlolsyswritenow Permalink
November 26, 2013, 13:01

nice.

September 17, 2014, 21:58

Hi..., This is confusing to me. But you seemingly so easily and generously willing to explain this. You are very genius. ultimate herpes protocol review. I had to learn a lot to you about this programming language.

September 26, 2014, 01:31

another great post harga tablet advan from this post

September 29, 2014, 05:58

Current and latest today trending news & topics in world. It includes all the latest trending news which are trending on google, facebook, yahoo and twitter.

Leave a new comment

(why do I need your e-mail?)

(Your twitter name, if you have one. (I'm @pkrumins, btw.))

Type the word "quake3": (just to make sure you're a human)

Please preview the comment before submitting to make sure it's OK.

Advertisements