NAME

perlsnippets - A collection of Perl idioms or short pieces of Perl code

VERSION

This document describes version 0.007 of perlsnippets (from Perl distribution perlsnippets), released on 2020-02-11.

DESCRIPTION

This distribution catalogs (in its POD pages) various idioms or short pieces of code that a Perl programmer usually uses. You can also copy-paste them to your code.

The pieces of code are often too small to refactor into modules, or sometimes they have been refactored as modules but you still want to "inline" them to avoid adding a dependency. The pieces of code are often Perl idioms (patterns of code that are specific to Perl programming), but not always.

Keywords: idiom, snippet, pattern

ARY (Arrays)

ARY/PICK (Arrays / Picking Array Elements)

ARY/PICK/1 (Check whether a value exists in an array, without any module)

my $foo_exists = grep { $_ eq 'foo' } @ary;

Another alternative is using smartmatch:

my $foo_exists = 'foo' ~~ @ary;

but smartmatch is now in experimental status and might be removed in the future. Plus the above is not exactly equivalent to the snippet and might be "too smart" for your needs (read perlop for more details).

Another alternative is using List::Util's first:

my $foo_exists = first { $_ eq 'foo' } @ary;

The benefit of the above alternative is that it short-circuits (finishes after it finds the first match) while grep evaluates the whole array even though it already finds a match (although the speed difference might not matter except for big arrays). But this alternative requires a module (albeit a core one). And beware of traps like:

my $foo_exists = first { $_ == 0 } @ary; # wil return false even though array contains 0

ARY/PICK/2 (Check whether a value exists in an array, without any module, with short-circuiting)

my $foo_exists; $_ eq 'foo' ? ($foo_exists++, last) : 0 for @ary;
my $foo_exists = do { my $res; $_ eq 'foo' ? ($res++, last) : 0 for @ary; $res };

Since grep is not short-circuiting, to short-circuit we need to employ a loop (e.g. for + last, while). This snippet offers a few styled variations. Some require multiple statements (declare variable + set it) while other offer single statement but requires a do {} block and double declaration. All are ugly enough that you might as well use first() for clarity.

ARY/PICK/3 (Pick a random element from array)

my $elem = $ary[rand @ary];

If you need to random-pick multiple elements from an array, you can use samples from List::MoreUtils. If you cannot use a module for some reason, you can copy-paste the implementation from List::MoreUtils::PP.

ARY/SORT (Arrays / Sorting Arrays)

ARY/SORT/1 (Schwartzian transform)

my @sorted = map  { $_->[0] }
             sort { $a->[1] <=> $b->[1] }
             map  { [$_, gen_key_for($_)] }
             @ary;

This is the infamous Schwartzian transform named after Randal Schwartz who popularized it during the early days of Perl 5 in 1994. Basically, it's a one-statement way to sort an array using a key. Examples of functions to generate a key: length($_) (to sort by string length), lc($_) (to do case-insensitive sorting).

Related documentation: https://en.wikipedia.org/wiki/Schwartzian_transform

ARY/UNIQ (Arrays / Removing duplicates from array)

Of course, the obvious way is to use List::Util's uniq (or uniqnum):

use List::Util 'uniq';
my @uniq = uniq(@ary);

But the snippets below might be useful in cases where you cannot or do not want use a module. Most (if not all) of the techniques described below will involve the use of a hash.

ARY/UNIQ/1 (Removing duplicates from array, losing order)

my %hash = map {$_=>1} @ary;
my @uniq = keys %hash;

Single statement variant:

my @uniq = do { my %hash = map {$_=>1} @ary; keys %hash };

Using this snippet, you will lose the original order of array elements, which is often undesirable.

ARY/UNIQ/2 (Removing duplicates from array, maintaining order)

my (%hash, @uniq);
for (@ary) { push @uniq, $_ unless $hash{$_}++ }

Single statement variant:

my @uniq = do { my %seen; grep { !$seen{$_}++ } @ary };

HASH (Hashes)

IO (I/O)

IO/FILE (I/O / File I/O)

IO/FILE/1 (Slurp a file content into a string)

my $content = do { local $/; <$fh> };

The above snippet slurps the whole file (which has been opened with filehandle $fh) into memory (scalar variable). The do {} block localizes the effect of $/. If you start from a filename and want to open it first:

IO/FILE/2 (Slurp a file content into a string)

my $content = do { local $/; open my $fh, "<", $filename; <$fh> };

IO/FILE/3 (Slurp a file content into array of strings)

my @lines = do { local $/; open my $fh, "<", $filename; <$fh> };

Like the previous snippet but you get the content as an array of lines. Each line still has their terminating newlines.

IO/FILE/4 (Slurp a file content into array of strings)

chomp(my @lines = do { local $/; open my $fh, "<", $filename; <$fh> });

Like the previous snippet, but the lines no longer contain newlines because they are chomp()-ed.

Related modules: File::Slurper.

Related documentation: $/ in perlvar.

MOD (Modules)

MOD/EXPORT/1 (Export requested symbols)

sub import {
    # NOTE: make sure 'no strict "refs"' is in effect
    my $pkg = shift;
    my $caller = caller;
    for my $sym (@_) {
        if ($sym eq 'foo' || $sym eq 'bar') { *{"$caller\::$sym"} = \&{$sym} }
        else { die "$sym is not exported!" }
    }
}

This is for (the rare) cases when you want to avoid using Exporter and do your own exporting. The above snippet can export from a fixed list of exportable subroutines.

MOD/LOAD/1 (Require a module by name from variable)

{ (my $mod_pm = "$mod.pm") =~ s!::!/!g; require $mod_pm }

You have a module name in $mod (e.g. "Foo::Bar") and want to load/require it. You cannot just use require $mod because require expects its non-bareware argument to be in the form of "Foo/Bar.pm". So the above snippet converts $mod to that form.

This is safer than eval "use $mod" or eval "require $mod" which work but necessitates you to check that $mod does not contain arbitrary and dangerous code.

Related modules: Module::Load

Related documentation: require in perlfunc.

MOD/LOAD/2 (Require a module and importing from it)

require Foo::Bar; Foo::Bar->import("baz", "qux");

The above snippet loads Foo::Bar module and imports things from the module. It is the run-time equivalent of use Foo::Bar "baz", "qux";. require Foo::Bar; itself is the run-time equivalent of use Foo::Bar ();, i.e. loading a module without importing anything from it.

PORT (Portability-related)

PORT/POSIX/1 (Checking if we are on a platform that is POSIX-compatible)

$^O =~ /(?^:\A(?:aix|beos|cygwin|darwin|dragonfly|freebsd|gnu|gnukfreebsd|haiku|hpux|interix|iphoneos|irix|linux|midnightbsd|minix|mirbsd|msys|netbsd|openbsd|sco|sco_sv|solaris|sunos|svr4|svr5|unicos|unicosmk)\z)/

(regexp taken from $RE_OS_IS_POSIX from Perl::osnames 0.121).

PORT/UNIX/1 (Checking if we are on Unix platform)

$^O =~ /(?^:\A(?:aix|android|bsdos|bitrig|dgux|dynixptx|cygwin|darwin|dragonfly|freebsd|gnu|gnukfreebsd|hpux|interix|iphoneos|irix|linux|machten|midnightbsd|mirbsd|msys|netbsd|next|nto|openbsd|qnx|sco|sco_sv|solaris|sunos|svr4|svr5|unicos|unicosmk)\z)/

(regexp taken from $RE_OS_IS_UNIX from Perl::osnames 0.121). You can also use Perl::OSType.

PROC (Process Management)

PROC/CHLD (Process / Child Process)

Some bit-fiddling and logic is needed to extract exit code from $? ($CHILD_ERROR). Process::Status makes things easier by presenting you with an object that you can query, but if you just want an exit code:

PROC/CHLD/1 (Extract information from $?)

my ($exit_code, $signal, $core_dump) = ($? < 0 ? $? : $? >> 8, $? & 127, $? & 128);

This snippet extracts all the information contained in $?: exit code (which can be -1 to mean there is no child process being created due to an execution error, e.g. system "non-existent-command"), what signal the child process dies from, and whether the child process dumps core.

PROC/CHLD/2 (Extract exit code from $?)

my $exit_code = $? < 0 ? $? : $? >> 8.

This snippets just extracts the exit code of child process (which can be -1 to mean that there is no child process being created due to an execution error, e.g. system "non-existent-command").

Related modules: Process::Status, Proc::ChildError.

Related documentation: $? in perlvar.

OBJ (Objects)

REF (References)

SUB (Subroutines)

SUB/ARG (Subroutines / Subroutine Arguments)

SUB/ARG/1 (Assign hash arguments)

my %args = @_;

SUB/ARG/2 (Assign hash arguments in method)

my ($self, %args) = @_;

SUB/ARG/3 (Assign hash arguments in method, shift $self)

my ($self, %args) = (shift, @_);

This variant shifts $self from @_ first, so you can do this later:

$self->yet_other_method(...);
$self->SUPER::method(@_);

SUB/CODE (Subroutines / Code In Subroutines)

SUB/CODE/1 (Run code in subroutine only once)

my $code_run;
sub yoursub {
    unless ($code_run) {
        ... # blah
        $code_run++;
    }
    ...
}

When yoursub() is first called, code in # blah will be run. On subsequent calls, the code will not be run again. This can be done to do initialization, DIY caching, etc.

SUB/CODE/2 (Run code in subroutine only once, variant 2)

Another variant for "SUB/CODE/1" is to (mis)use state variable:

use feature 'state';
sub yoursub {
    state $init = do {
        ... # blah
    };
    ...
}

State variables require perl 5.10, which all systems should already have. But you still have to say use feature 'state'; or at least use 5.010;.

SUB/DEFINED/1 (Check if a function is defined)

say "Function foo is defined" if defined &foo;
say "Function MyPackage::foo is defined" if defined &MyPackage::foo;

This includes routines imported from another package:

use Data::Dump; # exports dd()
say "Function dd is defined" if defined &dd; # prints that dd is defined

SUB/LIST/1 (List functions defined in a package)

The obvious choice is to use something like Package::Stash (Package::Stash::XS or Package::Stash::PP), but when you cannot or do not want to use a module, there's some symbol table manipulation you can do yourself. But heed the warning in Package::Stash documentation: "Manipulating stashes (Perl's symbol tables) is occasionally necessary, but incredibly messy, and easy to get wrong. This module hides all of that behind a simple API."

my @subs;
my $symtbl = \%{"$pkg\::"};
for my $key (keys %$symbl) {
    my $val = $symtbl->{$key};
    push @subs, $key if ref $val eq 'CODE' || # perl >= 5.22
        defined *$val{CODE};
}

This will include routines imported from another package.

HOMEPAGE

Please visit the project's homepage at https://metacpan.org/release/perlsnippets.

SOURCE

Source repository is at https://github.com/perlancar/perl-perlsnippets.

BUGS

Please report any bugs or feature requests on the bugtracker website https://rt.cpan.org/Public/Dist/Display.html?Name=perlsnippets

When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature.

SEE ALSO

Common Perl idioms (2004)

The Idioms subchapter in the Modern Perl book. http://modernperlbooks.com/

Perl Cookbook, 2nd edition. http://shop.oreilly.com/product/9780596003135.do

perlsecret

AUTHOR

perlancar <perlancar@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2020, 2019 by perlancar@cpan.org.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.