Robert P
Robert P

Reputation: 15968

What are some elegant features or uses of Perl?

What? Perl Beautiful? Elegant? He must be joking!

It's true, there's some ugly Perl out there. And by some, I mean lots. We've all seen it.

Well duh, it's symbol soup. Isn't it?

Yes there are symbols. Just like 'math' has 'symbols'. It's just that we programmers are more familiar with the standard mathematical symbols. We grew to accept the symbols from our mother languages, whether that be ASM, C, or Pascal. Perl just decided to have a few more.

Well, I think we should get rid of all the unnecessary symbols. Makes the code look better.

The language for doing so already exists. It's called Lisp. (and soon, perl 6.)

Okay, smart guy. Truth is, I can already invent my own symbols. They're called functions and methods. Besides, we don't want to reinvent APL.

Oh, fake alter ego, you are so funny! It's really true, Perl can be quite beautiful. It can be quite ugly, as well. With Perl, TIMTOWTDI.

So, what are your favorite elegant bits of Perl code?

Upvotes: 36

Views: 2590

Answers (14)

Kaoru
Kaoru

Reputation: 1570

My favourite elegant Perl feature is that it uses different operators for numerical values and string values.

my $string = 1 . 2;
my $number = "1" + "2";
my $unambiguous = 1 . "2";

Compare this to other dynamic languages such as JavaScript, where "+" is used for concatenation and addition.

var string = "1" + "2";
var number = 1 + 2;
var ambiguous = 1 + "2";

Or to dynamic languages such as Python and Ruby that require type coercion between strings and numberical values.

string = "1" + "2"
number = 1 + 2
throws_exception = 1 + "2"

In my opinion Perl gets this so right and the other languages get it so wrong.

Upvotes: 1

dls
dls

Reputation: 4146

This file parsing mechanism is compact and easy to customize (skip blank lines, skip lines starting with X, etc..).

open(H_CONFIG, "< $file_name") or die("Error opening file: $file_name! ($!)");
while (<H_CONFIG>)
{
   chomp;         # remove the trailing newline
   next if $_ =~ /^\s*$/; # skip lines that are blank
   next if $_ =~ /^\s*#/; # skip lines starting with comments
   # do something with the line
}

I use this type of construct in diverse build situations - where I need to either pre or post process payload files (S-records, etc..) or C-files or gather directory information for a 'smart build'.

Upvotes: 1

singingfish
singingfish

Reputation: 3167

I absolutely love Black Perl (link to version rewritten to compile under Perl 5). It compiles, but as far as I can tell it doesn't actually do anything.

That's what you get for a language written by a linguist from a pragmatic perspective rather than from a theoretical perspective.

Moving on from that, you can think about the Perl that people complain about as pidgin Perl (perfectly useful, but not expressive, and beware of trying to express anything complex in it), and the stuff that @pjf is talking about as "proper" Perl, the language of Shakespeare, Hemingway, Hume and so on. [edit: err, though easier to read than Hume and less dated than Shakespeare.] [re-edit and hopefully less alcoholic than Hemingway]

Upvotes: 3

Robert P
Robert P

Reputation: 15968

A favorite example of mine is Perl's implementation of a factorial calculator. In Perl 5, it looks like so:

use List::Util qw/reduce/;
sub factorial {
    reduce { $a * $b } 1 .. $_[0];
}

This returns false if the number is <= 1 or a string and a number if a number is passed in (rounding down if a fraction).

And looking forward to Perl 6, it looks like this:

sub factorial {
    [*] 1..$^x
}

And also ( from the blog in the link above ) you can even implement this as an operator:

sub postfix:<!>(Int $x) {
    [*] 1..($x || 1)
}

and then use it in your code like so:

my $fact5 = 5!;

Upvotes: 9

Robert P
Robert P

Reputation: 15968

If you have a comma separated list of flags, and want a lookup table for them, all you have to do is:

my %lookup = map { $_ => 1 } split /,/, $flags;

Now you can simply test for which flags you need like so:

if ( $lookup{FLAG} ) {
    print "Ayup, got that flag!";
}

Upvotes: 7

pjf
pjf

Reputation: 6009

My favourite pieces of elegant Perl code aren't necessarily elegant at all. They're meta-elegant, and allow you to get rid of all those bad habits that many Perl developers have slipped into. It would take me hours or days to show them all in the detail they deserve, but as a short list they include:

  • autobox, which turns Perl's primitives into first-class objects.
  • autodie, which causes built-ins to throw exceptions on failure (removing most needs for the or die... construct). See also my autodie blog and video).
  • Moose, which provide an elegant, extensible, and correct way of writing classes in Perl.
  • MooseX::Declare, which provides syntaxic aweseomeness when using Moose.
  • Perl::Critic, your personal, automatic, extensible and knowledgeable code reviewer. See also this Perl-tip.
  • Devel::NYTProf, which provides me the most detailed and usable profiling information I've seen in any programming language. See also Tim Bunce's Blog.
  • PAR, the Perl Archiver, for bundling distributions and even turning whole programs into stand-alone executable files. See also this Perl-tip.
  • Perl 5.10, which provides some stunning regexp improvements, smart-match, the switch statement, defined-or, and state variables.
  • Padre, the only Perl editor that integrates the best bits of the above, is cross-platform, and is completely free and open source.

If you're too lazy to follow links, I recently did a talk at Linux.conf.au about most of the above. If you missed it, there's a video of it on-line (ogg theora). If you're too lazy to watch videos, I'm doing a greatly expanded version of the talk as a tutorial at OSCON this year (entitled doing Perl right).

All the best,

Paul

Upvotes: 24

Chas. Owens
Chas. Owens

Reputation: 64909

The "or die" construct:

open my $fh, "<", $filename
    or die "could not open $filename: $!";

The use of qr// to create grammars:

#!/usr/local/ActivePerl-5.10/bin/perl

use strict;
use warnings;
use feature ':5.10';

my $non_zero         = qr{[1-9]};
my $zero             = qr{0};
my $decimal          = qr{[.]};
my $digit            = qr{$non_zero+ | $zero}x;
my $non_zero_natural = qr{$non_zero+ $digit*}x;
my $natural          = qr{$non_zero_natural | $zero}x;
my $integer          = qr{-? $non_zero_natural | $zero}x;
my $real             = qr{$integer (?: $decimal $digit)?}x;

my %number_types = (
    natural => qr/^$natural$/,
    integer => qr/^$integer$/,
    real    => qr/^$real$/
);

for my $n (0, 3.14, -5, 300, "4ever", "-0", "1.2.3") {
    my @types = grep { $n =~ $number_types{$_} } keys %number_types;
    if (@types) {
        say "$n is of type", @types == 1 ? " ": "s ", "@types";
    } else {
        say "$n is not a number";
    }
}

Anonymous subroutines used to factor out duplicate code:

my $body = sub {
    #some amount of work
};

$body->();
$body->() while $continue;

instead of

#some amount of work
while ($continue) {
    #some amount of work again
}

Hash based dispatch tables:

my %dispatch = (
    foo => \&foo,
    bar => \&bar,
    baz => \&baz
);

while (my $name = iterator()) {
    die "$name not implemented" unless exists $dispatch{$name};
    $dispatch{$name}->();
}

instead of

while (my $name = iterator()) {
    if ($name eq "foo") {
        foo();
    } elsif ($name eq "bar") {
        bar();
    } elsif ($name eq "baz") {
        baz();
    } else {
        die "$name not implemented";
    }
}

Upvotes: 11

Mark Canlas
Mark Canlas

Reputation: 9573

I'm surprised no one mentioned the Schwartzian Transform.

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

And in the absence of a slurp operator,

my $file = do { local $/; readline $fh };

Upvotes: 19

brunov
brunov

Reputation: 1003

Three-line classes with constructors, getter/setters and type validation:

{
    package Point;
    use Moose;

    has ['x', 'y'] => (isa => 'Num', is => 'rw');
}

package main;
my $point = Point->new( x => '8', y => '9' );

$point->x(25);

Upvotes: 9

Ali Afshar
Ali Afshar

Reputation: 41633

I am surprised no one has mentioned this. It's a masterpiece in my opinion:

#!/usr/bin/perl
                                           $==$';
                                         $;||$.| $|;$_
             ='*$ (                  ^@(%_+&~~;# ~~/.~~
         ;_);;.);;#)               ;~~~~;_,.~~,.* +,./|~
    ~;_);@-,  .;.); ~             ~,./@@-__);@-);~~,.*+,.
  /|);;;~~@-~~~~;.~~,.           /.);;.,./@~~@-;.;#~~@-;;
  ;;,.*+,./.);;#;./@,./        |~~~~;#-(@-__@-__&$#%^';$__
   ='`'&'&';$___="````"  |"$[`$["|'`%",';$~=("$___$__-$[``$__"|
              "$___"|       ("$___$__-$[.%")).("'`"|"'$["|"'#").
        '/.*?&([^&]*)&.*/$'.++$=.("/``"|"/$[`"|"/#'").(";`/[\\`\\`$__]//`;"
        |";$[/[\\$[\\`$__]//`;"|";#/[\\\$\\.$__]//'").'@:=("@-","/.",
       "~~",";#",";;",";.",",.",");","()","*+","__","-(","/@",".%","/|",
        ";_");@:{@:}=$%..$#:;'.('`'|"$["|'#')."/(..)(..)/".("```"|"``$["|
        '#("').'(($:{$'.$=.'}<<'.(++$=+$=).')|($:{$'.$=.'}))/'.("```;"|
        "``$[;"|"%'#;").("````'$__"|"%$[``"|"%&!,").${$[};`$~$__>&$=`;$_=
       '*$(^@(%_+&@-__~~;#~~@-;.;;,.(),./.,./|,.-();;#~~@-);;;,.;_~~@-,./.,
        ./@,./@~~@-);;;,.(),.;.~~@-,.,.,.;_,./@,.-();;#~~@-,.;_,./|~~@-,.
          ,.);););@-@-__~~;#~~@-,.,.,.;_);~~~~@-);;;,.(),.*+);;# ~~@-,
           ./|,.*+,.,.);;;);*+~~@-,.*+,.;;,.;.,./.~~@-,.,.,.;_)   ;~~~
             ~@-,.;;,.;.,./@,./.);*+,.;.,.;;@-__~~;#~~@-,.;;,.*   +);;
               #);@-,./@,./.);*+~~@-~~.%~~.%~~@-;;__,. /.);;#@-   __@-
                 __   ~~;;);/@;#.%;#/.;#-(@-__~~;;;.;_ ;#.%~~~~  ;;()
                      ,.;.,./@,.  /@,.;_~~@- ););,.;_   );~~,./  @,.
                      ;;;./@,./|  ~~~~;#-(@- __,.,.,.    ;_);~~~ ~@
                       -~~());;   #);@-,./@,  .*+);;;     ~~@-~~
                       );~~);~~  *+~~@-);-(   ~~@-@-_      _~~@-
                       ~~@-);;   #,./@,.;.,    .;.);@      -~~@-;
                       #/.;#-(   ~~@-@-__      ~~@-~~       @-);@
                       -);~~,    .*+,./       |);;;~        ~@-~~
                        ;;;.;     _~~@-@     -__);.         %;#-(
                        @-__@      -__~~;#  ~~@-;;          ;#,.
                        ;_,..         %);@-,./@,            .*+,
                        ..%,           .;.,./|)             ;;;)
                        ;;#~            ~@-,.*+,.           ,.~~
                       @-);            *+,.;_);;.~         ~););
                      ~~,.;         .~~@-);~~,.;.,         ./.,.;
                      ;,.*+        ,./|,.);  ~~@-         );;;,.(
                    ),.*+);                              ;#~~/|@-
                  __~~;#~~                                $';$;;

Upvotes: 6

Chris Lutz
Chris Lutz

Reputation: 75389

Adding to the love of map and grep, we can write a simple command-line parser.

my %opts = map { $_ => 1 } grep { /^-/ } @ARGV;

If we want, we can set each flag to it's index in @ARGV:

my %opts = map { $ARGV[$_] => $_ } grep { $ARGV[$_] =~ /^-/ } 0 .. $#ARGV;

That way, if a flag has an argument, we can get the argument like this:

if( defined( $opts{-e} ) ) {
  my $arg = $ARGV[ $opts{-e} ];
  # do -e stuff for $arg
}

Of course, some people will cry that we're reinventing the wheel and we should use getopt or some variant thereof, but honestly, this was a fairly easy wheel to reinvent. Plus, I don't like getopt.

If you don't like how long some of those lines are, you can always use intermediate variables or just convenient line breaks (hey, Python fanatics? You hear that? We can put one line of code across two lines and it still works!) to make it look better:

my %opts = map  { $ARGV[$_] => $_   }
           grep { $ARGV[$_] =~ /^-/ } 0 .. $#ARGV;

Upvotes: 2

Chris Lutz
Chris Lutz

Reputation: 75389

Have a list of files the user wants your program to process? Don't want to accidentally process a program, folder, or nonexistent file? Try this:

@files = grep { -T } @files;

And, like magic, you've weeded out all the inappropriate entries. Don't want to ignore them silently? Add this line before the last one:

warn "Not a file: $_" foreach grep { !-T } @files;

Prints a nice warning message for every file that it can't process to standard error. The same thing without using grep would look like this:

my @good;
foreach(@files) {
  if(-T) {
    push @good, $_;
  } else {
    warn "Not a file: $_";
  }
}

grep (and map) can be used to make code shorter while still keeping it very readable.

Upvotes: 16

chaos
chaos

Reputation: 124277

Perl facilitates the use of lists/hashes to implement named parameters, which I consider very elegant and a tremendous aid to self-documenting code.

my $result = $obj->method(
    flux_capacitance       => 23,
    general_state          => 'confusion',
    attitude_flags         => ATTITUDE_PLEASANT | ATTITUDE_HELPFUL,
);

Upvotes: 29

popcnt
popcnt

Reputation: 4645

Poorer typists like me who get cramps hitting the shift key too often and have an almost irrational fear of using a semicolon started writing our Perl code in python formatted files. :)

e.g.

>>> k = 5
>>> reduce(lambda i,j: i*j, range(1,k+1),1)
120
>>> k = 0
>>> reduce(lambda i,j: i*j, range(1,k+1),1)
1

Upvotes: -17

Related Questions