Monday, October 22, 2012

perl is beautiful - part 3

In the last post on the subject, I'll show a couple more perl tricks I use everyday. I'll touch on some not-so-basic regex concepts here, so feel free to take a look here if you don't know what some of them mean.

One thing you can do with the regex operators is using them in list context. Every operation in perl is done in a context. When you assign to a variable, the expression is evaluated in scalar context. When you assign to an array, you get list context: 
my @my_array = (2, 3, 4, 5); 
The '@' tells perl that your variable will be an array, and the rhs is thus executed in list context. Try this:
my @my_array = (2, 3, 4, 5);
my $my_var = @my_array;
Or, my personal favorite:
my @my_array = 2..5;
my $my_var = @my_array;
Both do the same thing. When you use an array on the rhs of a scalar assignment, you get the length of that array, which is what's happening here. The regex operators also work this way: 
my $scalar = m/my \$\w+/; 
will put a boolean value on $scalar if a match was or wasn't found (on $_, remember). This will (roughly) find all "use" directives on perl code. Since we are in scalar context, the result will be a boolean, which will be true if any match occurs. But try this:
my @array = m/my \$(\w+)/g;
This will create an array with all the matches found. There are some new things here which need a better explanation. First, the regex modifier g (from global) will make the regex be applied repeatedly, instead of stopping on the first result.

Since we are on a list context, the result is no longer a boolean, but a list of all the matches, here meaning the values captured on $1, $2, and so on. The output for a sample file:
use warnings;
use strict;

local $/;
$_ = <>;
$, = "\n";
$\ = "\n";

my $scalar = m/use (\w+);/;
my @array = m/use (\w+);/g;

print $scalar;
print @array;

# Output
1
warnings
strict
Here we can see, when we incept the code through itself, that the variable gets a value of true, while the array is populated with all the results found. Just to show you another nice feature: if we add some parenthesis around "use":
my @array = m/(use) (\w+);/g;
we get four elements on the array. That's because, for each "pass", all values from ($1, $2, $3, ...) are stored.

Well, I could go on and on writing about all the cool things you can do with perl, but I'm going to stop here. If this short introduction did leave you interested to learn more, there are several sources on-line. Two I use constantly are perldoc and the web version of Programming Perl. If you want to learn more about regular expressions, I highly recommend O'Reilly's Mastering Regular Expressions (but please, don't buy the Brazillian Portuguese translation, just don't).

And as a last note, if you are a vim user, here's something really useful. You can select some text (using v, V or ctrl-v) and press ':'. You will automatically get a filter for the selected text. From there, you can type 'w !' (note the space), which will write the selected text to the standard input of the command. Combining all this with what we learned, we can, for example, use the regex to find python functions, on a single vim command: 
:'<,'>w !perl -ne 'print if m/^\s*def \w+/'
And get a listing of the functions inside the selected text. Or you can do some bizarre things, like sending the output to a file, or changing the name of the functions, changing indentation... Your sanity is the limit.

No comments:

Post a Comment