There's More Than One Way To Do It
#!/usr/bin/perl -c
Please, listen to, my $words;
seek love, joy, happiness for everything;
study hard and sleep longer if able;
# Perl Poetry, The Sentiment
# (c) http://www.perlmonks.org/?node_id=882536
The desert ...
Proud and majestic stands at the Perl desert. He began his journey back in 1987, bypassing thousands of deserts over many summers, he saw here and there wonderful, oasis caressing the oases full of water, as the rainy seasons began, alternating with prolonged drought. Deserts are different, more than once misfortune happened to him, when an imprudent rider led him deeper and deeper into the arid desert where there was no place even hope. On the contrary, there were cases when in the middle of the desert whole cities appeared full of life and people, it seemed like a mirage, but no - it was a reality. Perl never ceased to be amazed at the creative talent of people. He did not impose a path on man, trusting his will, he went into the depths of the desert to the unknown. He was disappointed 1"Wild" riders - people from big cities are used to traveling in complex cars. Here, in the desert, even the most attractive spine cannot make them move forward at least a meter. Out of habit, they take a lot of trash with them, and constantly push them, making them run faster and faster. The beauty of the desert, its philosophy, sweeps past them, but not many of them reach the goal, leaving behind the whole lifeless desert. On the contrary, there were other people who perceived the desert as something new, free, unlimited space for creativity. Traveling with such companions has always been a great pleasure. The trail led them through the bizarre, full of life islands of the desert. Sometimes a group of such people could collect entire caravans of camels, slowly wandering through the desert. The desert was intoxicating, and somehow in a special way, acted magically on the not strong young minds of riders. Perl often saw the poor people go crazy starting to build sand castles that were not destined to become anything more. Others drew scribbles not even clear to him in the sand. They were also ruined by mirages that never really existed. Unfortunately, Perl could not help these poor fellows, nor could he refuse to travel, because Perl felt great during the trip, and for this he was born.
And here he is in front of you, joyfully kicking him offering to take a fascinating walk through this still lifeless desert, back to the distance where you think you should go.
... camel 2
(*) Perl's philosophy is that it would be easy to solve simple problems, while maintaining the ability to solve difficult ones. These are the tasks that we solve day by day. No need to say a lot of preliminary words before saying what you are going to do. Perl is traditionally included in the delivery of almost all * nix distributions, which makes starting especially easy. In fact, after installing the operating system, Perl is ready to work (of course we are talking about * nix).
(*) When developing each new version of Perl, special attention is paid to maintaining backward compatibility. Which is very important, considering how much is written in Perl.
(*) CPAN . Module distribution network for Perl, something like ( maven , ivy ) / Java,RubyGems , npm /Node.JS. Cloth self-assembly, a storehouse of ready-made solutions for all occasions. At the moment, one of the largest among languages (more than 121,000 modules). Thanks to CPAN, many complex tasks are solved by the trivial search for a suitable module. However, the comparison with maven is somewhat strained, CPAN is much easier in terms of dependency management. It does not require prior configuration magic, it is more like apt from Debian / Ubuntu.
(*) Perl compiles text programs into an intermediate representation before interpreting them. That allows, albeit not often, to detect errors in the program at the compilation stage. First of all, this includes checking the correctness of syntactic constructions, checking the correctness of the names and arguments of subprograms (only if there is a prototype), checking the correctness of calls to the fields of the class. Of course this is not a C ++ compiler, but for a dynamic language it is not bad at all. It is interesting, but even the C ++ compiler can leave something behind, so the Perl compiler can detect incorrect calls to mathematical operations (for example: a root from a negative number causes a compilation error). Here are some examples:
#!/usr/bin/perl -w
use strict;
my Dog $aDog = new Dog; # Ошибка: нет класса Dog
$aDog->{wings} = '?'; # Ошибка (см. ООП): обычно собаки без крыльев
feed $aDog; # Ошибка: прототип функции не объявлен
sqrt -2; # Ошибка: корень из отрицательного числа
Perl always follows its principle , and it costs nothing to turn off most of these checks, but more on that later.
(*) Function prototypes allow you to pre-declare subprograms and a list of their arguments, something similar to forward declaration in C ++, if there is a prototype, the compiler checks the correctness of calling subprograms, and in addition, does not require pair brackets when calling a subprogram.
sub feed($$);
feed 'Sharik', 'Fruit'; # Ok
feed 'Sharik'; # Ошибка: не достаточно аргументов
feed 'Sharik', 'Apple', 'Fruit'; #Ошибка: много аргументов
sub feed($$) {
print shift, ' eat ', shift, "\n";
}
(*) $ @% . Perl separates the variables according to their purpose: ' $ ' scalar (number, string, link), ' @ ' array (sequence of scalars), ' % ' hash (associative array of scalars). As you can see, the classic primitive types are represented by a scalar. However, they are strictly separated from the containers, which allows the compiler to perform additional checks, saving us from the hassle. Perl does not allow to enter its types (for OOP concessions are introduced - any of the built-in types can be blessed in a class). Due to such a clear separation, in the same area you can have non-conflicting variables of all types:
#!/usr/bin/perl -w
use strict;
my $solarSystem = 'Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune';
my @solarSystem = qw(Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune);
my %solarSystem = (haveLife => 1, havePerl => 1);
sub solarSystem() {
print 'Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune';
}
Each operation in Perl is not separable associated with the execution context. Perl understands three types of context: empty (no context), scalar and list (for example, assigning a value to a scalar variable forms a scalar context, and assigning to an array or hash a list context). Perl routines are able to determine the context of execution and, accordingly, can behave differently in different contexts, which gives room for creativity.
There is no pointer arithmetic in the language, but there are links. Arguments to subprograms are passed by reference, but the assignment operator copies the value of the variable itself (not only scalars, but also containers are copied). Thus, passing arguments to the subroutine by value is easily implemented. Moreover, the classical subroutine writing in Perl implies passing by value, i.e. It does not introduce side effects to its arguments.
#!/usr/bin/perl -w
use strict;
sub upper {
my $value = $_[0]; #копирование значения в переменную $value
$value = uc $value; #снаружи ничего не изменилось
$_[0] = $value; #теперь снаружи строка в верхнем регистре
}
my $bigLetters = 'big-letters';
upper $bigLetters;
print $bigLetters, "\n"; # печатает BIG-LETTERS
(*) Overloading operators (for rare cases when it is needed). You can overload almost everything except spaces, commas, and perhaps I have not heard that someone was able to overload the comment. The only language (known to me) that allows you to overload constants (numbers, letters, words). This can be used, for example, to replace all numbers entered as constants with equivalent objects representing complex numbers. Or use numbers of unlimited value:
# Файл BigMath.pm
package BigMath;
use overload;
use Math::BigInt;
sub import {
overload::constant integer => sub {
Math::BigInt->new(shift)
};
}
1;
# Файл BigMath.pl
{
use BigMath;
my $bigInt = 2123; #создается объект BigInt
print $bigInt->as_bin(); #печатается 0b100001001011
}
{
my $int = 2123;
print $int->as_bin(); #ошибка нет метода as_bin()
}
Perl allows you to overload standard language routines.
(*) Regular expressions are deeply integrated in Perl. In addition to the usual regular expression patterns, Perl supports its extensions. For example, Perl allows you to use direct Perl code in search patterns and replacement patterns:
my $math = '100 + 200 = 300';
$math =~ s/(\d+)/$1 * 2/ge; # регулярное выражения умножает все числа на 2
print $math, "\n"; #печатает 200 + 400 = 600
Regular expressions support Unicode properties and are even allowed to enter their own character properties. You can enter your own abbreviations for complex patterns (like \ s, \ w, etc.) Naturally, regular expressions in Perl support both greedy and non-greedy searches.
(*) Perl, in the absence of the return keyword in the subroutine, returns the last evaluated expression (it seems to be fashionable now). In addition, when calling subroutines, you can omit the parentheses if you understand what you are doing.
(*) Blocks in Perl. A block is a pair of curly braces {...} , such constructions as while , if , foraccept a block, although a block may exist on its own. The most interesting thing is that any block supports the next , last and redo operators . Those. the entry is quite legal:
{
print "Yeah\n";
last;
print "Unreachable code\n";
}
{
print "Forever loop\n";
redo;
}
In addition, subroutines can accept blocks as arguments, which allows you to richly expand the language with its syntactic constructs. This makes it possible to write beautiful, self-documented programs in Perl, of course, imagination is required.
use strict;
#sub transaction(&$);
#sub update($$$);
#sub set(+);
#sub where(+);
#sub rollback(&);
#sub throw($$);
transaction {
update 'user', set { name => 'caiiiycuk' }, where { id => '1' };
} rollback {
throw 500, "rollback exception";
}
Perl does not support the try / catch / finally block ; however, it is easily implemented through receiving block routines. Precisely because if / else accepts a block, the syntax for if / else if / else cannot exist in Perl , if / elsif / else is used instead . Similarly, there is no switch / case , it is implemented trivially.
(*) The while loop can define a continue section that runs regardless of whether next was called, last
while (1) {
next;
} continue {
print "Yet another loop done \n";
}
(*) Three scopes for variables: global, lexical and local. The local scope extends the value of the variable to the current block and to all subroutine calls from this block, useful for overriding the values of global variables in a specific block. Local scope is determined at runtime, and lexical at compilation time (this is the most common scope within a block).
use strict;
our $a = 'global scope'; #глобальная область видимости
sub printLocal {
local $a = 'local scope'; #локальная область видимости (внутри блока и в подпрограммах)
printGlobal();
}
sub printGlobal {
print $a, "\n";
}
printGlobal(); #печатает 'global scope'
printLocal(); #печатает 'local scope'
printGlobal(); #печатает 'global scope'
(*) Perl implements closures, glad that the syntax for declaring an anonymous subroutine is no different from a regular declaration, as happens in some other languages.
(*) Already since 2002, Perl has supported the mechanism for calculating constant routines, and replacing their places of call with the values of the calculated constant ( constexpr ).
(*) Convenient file verification operators (- rwxoRWXOezsfdlpSbctugkTBMAC ).
use strict;
if (-e '/tmp' and -d '/tmp') {
print '/tmp is directory', "\n";
}
(*) The operator <> . It performs several functions. Firstly, it can be used to search for files by template in the current directory (by the way, the search method can be changed, glob is used by default ).
print join ',', <*.pl>, "\n"; #печатает имена файлов *.pl
Secondly, it is an operator for reading data from a file descriptor. Moreover, in a scalar context, it reads data line by line, and in a list context, it completely reads the lines of the file into an array.
open F, '; # считать все строки из файла
close F;
And finally, the empty <> operator reads from files whose names were passed in the program startup arguments. It does not do this explicitly, as if you are working with one large file. If the program was launched without arguments, it reads from STDIN . This is very convenient for developing pipeline programs that will be combined through |, since you do not need to worry about opening and closing files. Applying this operator, the analogue of the cat command is implemented in one line.
print while (<>);
(*) Useful default variable $ _ . Most standard Perl routines and operators are able to work with it, suggest its use if the input argument of the function is omitted. Sometimes this allows you to write more concise code.
use strict;
while (<>) {
print if (s/^print\s*(.*)/$1/);
}
# эквивалентно
while ($_ = <>) {
print $_ if ($_ =~ s/^print\s*(.*)/$1/);
}
(*) In Perl, an assignment result is an lvalue . Therefore, in Perl it is possible to change the value during assignment.
($new = $old) =~ s/bad/good/g;
##
chomp($answer = );
##
if ( ($k, $v) = $string =~ m/(\w+)=(\w+)/ ) {
print "key: $k value: $v", "\n";
}
(*) The increment operator applied to the string returns the next permutation from the character set.
my $a = 'aa';
print ++$a, "\n"; # печатает ab
(*) Automatically expand hashes and arrays to arbitrary depth. Those. when accessing a hash by a key that has not yet been added, this element is automatically created. And Perl guesses the type of element that you want to create, for example, it can create an anonymous hash, an anonymous array, or a scalar.
my $hash = {};
$hash->{first}->{second}->{third} = 5;
print $hash->{first}->{second}->{third}, "\n"; # 5
An interesting feature is the behavior of negative indices in arrays. A negative index selects elements from the end of the array.
(*) Strings and quotation marks. It's nice that Perl supports multi-line input in one assignment:
my $doc1 =
'There’s More Than One Way To Do It
--
2012';
my $doc2 =<<"_DOC2_";
There’s More Than One Way To Do It
--
2012
_DOC2_
print<<"_DOC3_";
There’s More Than One Way To Do It
--
2012
_DOC3_
The use of the operator = << allows you to conveniently perform operations with a multi-line document on the same line as the assignment. It is also convenient to use this operator for transferring documents to functions.
use strict;
sub printAll(@) {
print join '', @_;
}
(my $quote =<<'__QUOTE__')=~s/^\s+//gm;
The Road goes ever on and on,
down from the door where it began.
__QUOTE__
printAll(<<'__GOOD__',<<'__BAD__', $quote);
There’s More Than One Way To Do It
__GOOD__
TIMTOWTDI
__BAD__
Perl distinguishes between two ways of entering strings - with and without interpretation. If you use single quotes ' , then the string is assigned to the variable as is (without changes) even the characters \ n are not converted to line breaks. On the other hand, if you use double quotes " , the string is interpreted - Perl detects and replaces the variable names with their values, and does it very skillfully, you can even call the function directly from the string.
use strict;
my $car = {
brend => 'Lada',
name => 'Kalina',
drive => sub {
"WEEEEEE!\n";
}
};
# Печатает Do you want to drive on $car->{brend} $car->{name}?\n
print 'Do you want to drive on $car->{brend} $car->{name}?\n', "\n";
# Печатает Do you want to drive on Lada Kalina?
print "Do you want to drive on $car->{brend} $car->{name}?\n";
# Печатает WEEEEEE!
print "@{[ $car->{drive}->() ]}";
# Выходим
print "@{[ exit ]}";
However, Perl gives you the right to choose a character to escape strings and regular expressions.
print q {
Мама мыла раму!
};
(my $text = "a b c") =~ s{\s}{}g;
Another very useful feature of Perl is the behavior of strings when using `as a string escape character. In this case, Perl executes this line in the shell and returns the result of the command.
print `uptime`; # печатает время работы
print << `EOC`; # выполнить команды
pwd
ls -l
EOC
(*) Perl supports an interesting subroutine attribute mechanism. You can draw an analogy with annotations from Java. Attributes are specified in the subroutine declaration through a colon. You can declare as many multiuser attributes as you like. There are also built-in attributes method and lvalue . The remarkable lvalue attribute allows a subroutine to participate in operations as an lvalue . So, for example, you can implement the get and set methods in the same routine.
my $_name = '?';
sub name(;$): lvalue {
$_name = shift || $_name
}
name = 'caiiiycuk';
print name(), "\n";
(*) The remarkable AUTOLOAD mechanism allows you to process calls to undeclared routines, which is often used to dynamically load a program in parts. It means that if Perl does not find the subroutine declaration, it calls the AUTOLOAD call with the function signature and only if it fails, it will throw an error. Using AUTOLOAD , for example, you can achieve a clearer call to system functions, as if they themselves are Perl statements (as the Shell module does ).
use strict;
sub AUTOLOAD {
my $program = our $AUTOLOAD;
$program =~ s/.*:://;
system($program, @_);
}
sub uptime();
sub echo(@);
uptime;
echo "AUTOLOAD";
(*) tie - a mechanism for replacing the internal implementation of scalars, arrays and hashes. Using this mechanism, you can associate an array or hash directly with the database in such a way that any reading and writing from / to it will automatically be displayed on the database. Another possible application of this technology may be the allocation of resources.
(*) Perl supports two multithreading models: a thread model and a process model. The process model involves the creation of a new system process with a complete copy of the data of the parent process for parallel data processing. This is done with the fork command.By heroic efforts, the process model is fully supported on Windows, although this parallelization model is more common for * nix. Using the bare fork function is not quite convenient, CPAN is full of modules for easier use of fork , for example, the code might look like this:
use strict;
use Proc::Fork;
run_fork {
child {
print "Process 1\n" while 1;
}
parent {
print "Process 2\n" while 1;
}
}
In contrast to the process model, the thread model stands out, these streams have shared access to all data resources, such streams are more similar to Java streams. The thread support module is called Thread , it provides a very convenient async function that helps you write clear code.
use Thread 'async';
my $thread1 = async {
sleep 5;
return 1;
};
my $thread2 = async {
sleep 2;
return 2;
};
print $thread1->join(), "\n";
print $thread2->join(), "\n";
Mechanisms for synchronizing threads are supported: mutexes, semaphores, falling asleep and waking threads by condition.
(*) Powerful pack / unpack functions allow you to convert Perl structures to byte array and vice versa. A very useful thing when reading binary data and for their arbitrary conversion.
(*)OOP and Perl, perhaps this is the only thing that makes me mixed feelings. This is very similar to what we have in JavaScript, as such, there is no special support for OOP features in the language, however, all OOP paradigms are realizable and quite viable. However, sometimes a feeling of some kind of crutches comes. An object can be any scalar, array, or hash data structure. Using a special command, a reference to a data structure is blessed into an object ( bless), after this action, Perl knows that this link is an object. The methods of this object should be sought in the package in which the link was blessed. Accordingly, there is no classical concept of a constructor; a constructor can be any subroutine that returns blessed data structures. Perl supports an indirect form of calling methods of an object; in this form, the method comes first, then a reference to the object, and only then the arguments of the method. Because of this, calling the new method may look as you expect, but when calling ordinary methods, this form of writing may seem strange.
{
package Dog;
sub new {
my $class = shift;
return bless { name => shift }, $class;
}
sub gav {
my $self = shift;
print "$self->{name}: gav\n";
}
}
{
my $dog = Dog->new('Mona');
$dog->gav();
}
{
my $dog = new Dog 'Sharik';
gav $dog;
}
OOP in Perl supports multiple inheritance. When using the use fields pragma, it is possible to check calls to class fields at the compilation stage using pseudo hashes, as well as implement private class data.
{
package Foo;
use fields qw(foo);
sub new {
my Foo $self = shift;
$self = fields::new($self);
$self->{foo} = 10;
return $self;
}
}
my Foo $var = Foo->new;
$var->{foo} = 42;
# Ошибка: обращение к несуществующему полю
$var->{zap} = 42;
Unfortunately, class methods cannot have prototypes, so we lose the ability to verify the correctness of method calls and passing arguments, which is unfortunate. Perl supports object destructors.
(*) A special label __DATA__ indicates the end of the script, all text after this label is available to the program through a special file descriptor DATA. This is very convenient for writing small tests when the data and the test itself are not separable. However, the entire testing system in Perl is more than convenient, which is inherent in all dynamic languages.
use strict;
my @content = ;
print join '', @content;
__DATA__
A
B
C
(*) A huge number of Perl command flags allow writing as short as possible single-line files for processing files. For example, the -n flag wraps the script in a while (<>) loop , which we examined earlier. That is, the analogue of cat can be written like this:
perl -ne "print;" file1 file2 ... fileN
(*) A very interesting opportunity to write code generators. After compiling the code, the Perl program is a graph of statements. This graph is transferred to the Perl interpreter during normal execution, however, you can change this behavior by performing your actions with this graph. For example, you can simply serialize the entire graph into a file, and then read it on another computer and pass it to the Perl interpretation by omitting the compilation phase. A more complex application of this technology is the generation of C or C ++ code from a Perl program. Generators B :: C and B :: CCaimed at just that, when using Perl, the program is converted to source codes in the target language, after which they are compiled into machine codes and can be run without a Perl interpreter. In addition to code generators, Perl also has code filters, they allow you to arbitrarily convert the source code of the program before compilation and interpretation, you can write your own language completely if you want.
(*)Tagged data mode. While in this mode, Perl takes special precautions to avoid obvious and hidden security holes. The principle is simple: you can not use the data obtained from outside the program to influence anything else that is outside of it. Any data entering the program from outside becomes “marked” as data of dubious origin. Labeled data cannot be used directly or indirectly in any operation that causes a "shell", as well as in any operations that modify files, directories or processes. Therefore, many security problems are avoided. The use of labeled data mode is a good form when running scripts on the server. There are modules that go even further in improving security, for example, the Safe module.
#!/usr/bin/perl -T
use strict;
print "Enter file/directory name to delete: \n";
my $file = ;
# Ошибка: режим меченных данных, в оболочку передаются
# не безопасные аргументы
`rm -rf $file`;
(*) PSGI / Plack is a specification designed to separate the web server environment from the web framework code. Simply put, this is something like a container servlet from the Java world. We write a web application using this specification and run it on any of the servers supporting PSGI, of which there are a great many. More details .
(*) And Perl is a sea of pleasant emotions, which is just a set of ACME modules. For example, the ACME :: EyeDrops module allows you to convert Perl programs to ASCII art that works. There is a buffy fan module , using which programs can only be written using the word buffy. Well, the ACME module :: ChuckNorrisspeaks for itself. In addition, there is Perl poetry - this is a valid syntax for Perl's point of view, and poetry from a human point of view is one of those in the title of this article.
Have a good trip!
1 So my Perl thinks, yours might think differently TIMTOWTDI
2 Interesting in my opinion Perl features