...
is a 3-in-one power house
...
..
goes by many names, but it is actually 3 fairly neat things.
perldoc
In void context ...
is the "yada yada". It is fatal and dies with "unimplemented".
You can use this while planning out a module. You can quickly throw down a
sub write_me_later { ... }
... and when you call it (say from your tests) perl
will remind you that you haven't written that sub yet.
In scalar context it returns true after the thing on the left is true, until the thing on the right becomes true.
This is the set/reset behaviour of a "flip flop", and it is really helpful when parsing lines of a file.
while(<>) {
if (/flip/.../flop/) {
# every line between a flip and a flop (including the lines that do)
say "flipped: $_"
}
}
Simply /start-of-range/../end-of-range/
is true between those two matches.
Unsurprisingly in list context ...
gives a list, it works with similar rules to ++
, so:
- 1..10
gives you back the numbers you expect.
- "OWF1".."OWF9"
gives you back a list that includes "OWF6"
It's particularly handy for avoiding off-by-one's when looping over arrays:
for (0..$#array) { # No < @array... or is it <= @array?
printf "I'm at index %s, which is: %s\n", $_, $array[$_]
}
I think it's very cool that 2 or 3 characters can do all of these things, and that their meaning comes entirely from the context.
I just think that's neat.
Come on man, it's an emergency, and we need to fix this right away, so ...
I mean far be it from me to suggest that you're making that up.
I believe we should give people the benifit of the doubt.
People wouldn't simply lie during a code review to get me to approve a thing and then just ... never follow up...
Fundimentally, we have to assume we're working with good faith actors, right?
So it seems fair to just approve the PR, once they show a pair of commits where:
You can neatly put all your logic into a signle class, and then have a nice little factory that looks at @ARGV
.
If your class has prod configs as defaults, then your crontab looks like this:
* * * * * perl -MMyApp::Script::CleanupExpiredEntries \
-E'MyApp::Script::CleanupExpiredEntries->new_from_args->run'
Because you don't have any code in a bin/run-me
script, you can neatly write tests for all your functions
by either sub-classing your ::Script
to replace your database access logic or by
by straight-up replacing the method for the duration of your sub-test with
{
local *MyApp::Script::CleanupExpiredEntries::fetch_from_db = sub { ...}
...
}
The most notable downside is that some day someone might introduce some code that uses Log::Log4perl
, and a
config config that references $0
:
log4perl.appender.LOGFILE.filename=sub {
my $name = ( map s{ [.]pl $ }{}xr, split m{/}, $0 )[-1];
"/var/log/$name/$name.log" }
which means you'll get a mail a minute saying:
Can't sysopen /var/log/-e/-e.log (No such file or directory)
at /usr/share/perl5/Log/Log4perl/Appender/File.pm line 140.
which might not be the kind of thing you want to see after taking a couple of days off.
TL;DR
: I think it's chill that perl has both lexical and dynamic scoping.
for my $thing (@things) {
local $ThingFinder::LogLevel = 9;
print "I have $thing";
if (my $thing = $thing->get_more_specific_things()) {
print "I have found this thing: $thing";
}
}
Variables who are only declared / visible within their scope.
my $item = Thing->new(); # <- declare a lexical variable
# and shove an object in there
#
for my $thing ($item->fetch_things) { # <- $thing only exists within the loop
#
my $item = $thing>get_item(); # <- this $item masks the $item above
$item->do_stuff(); # <- do_stuff can't see either $item
# (though the inner one will be "self" in the call)
#
} # <- inner $item is DESTROYed here.
Lexicals are the power house of the closure:
sub thing_maker {
my $thing = shift; # <- $thing will live past the end of the
# call to thing_maker
return sub { # <- return a code-ref
"I made this thing: $thing" # <- This reference here keeps $thing alive.
}
}
my $code = thing_maker("very good thing here");
print $code->() # "I made this thing: very good thing here"
These are the kind of variables you get if you don't use strict 'vars'
.
#!perl
$half = $ARGV[1] / 2; # <- $half isn't declared with my/our/state
# so it's ours
print "Half of that is $half\n" # "Half of that is 5" (or whatever)
These are kind of pesky because the stuff you assign to them is only DESTROYed when the variable is assigned over or the interpreter shuts down.
package
You have a variable in a package:
package ThingFinder;
our $LogLevel = 4;
1
... and you can see the values in it:
use ThingFinder;
sub show_loglevel {
say "It's $ThingFinder::LogLevel"; # It's 4
}
... sadly anyone can write to it too:
# Set the value for everyone forever:
$ThingFinder::LogLevel = 'just any old garbage';
These variables are super helpful for configuration when your package exports functions.
local
$ThingFinder::LogLevel = 4; # <- Set the value for everyone forever
ThingFinder->show_loglevel() # "It's 4"
{
local $ThingFinder::LogLevel = 9; # <- Set the value until the end of the scope
ThingFinder->show_loglevel() # "It's 9"
}
local
works on other stuff, like the symbol table, or just regular old hashes, which is also chill.
I just think that's neat.
Write a function that takes 'aaaabbbcca' and returns [('a', 4), ('b', 3), ('c', 2), ('a', 1)]
I'm not sure why an interviewer would hate someone enough to ask a question like this, but this seems like a fair response:
% echo aaaabbbcca | perl -nlE '
s/((.)\2*)/push @things,sprintf "(%s,%s)", $2,length $1 /ge;
$"=","; say "[@things]"
'
[(a,4),(b,3),(c,2),(a,1)]
I mean, it'd be easier if perl had tuples, but that's what you get for answering a python question in perl when nobody asked you to
Please don't do this kind of thing in a production code base
“Perfection is achieved, not when there is nothing more to add, but when there are no TODO comments left to take away.”