Предназначението на това писание е да ви въведе набързо в езика за програмиране Perl 6. Ако за първи път се сблъсквате с езика, то ще ви даде начален тласък в използуването и изучаването му.
В някои от разделите ще намерите препратки към други, по-пълни и точни части от документацията на Perl 6. Така ще можете да четете направо от източника, в случай че се нуждаете от повече информация по определена тема.
В този урок ще намерите примери по най-обсъжданите теми. За да ги разберете по-добре, отделяйте време да правите всеки от примерите.
Тази работа е лицензирана под "Признание - Споделяне на Споделеното 4.0 Международен Лиценз". За да видите лиценза, посетете
Ако желаете да допринесете за подобряването на това писание, отидете на
Всякакви отзиви са добре дошли:
Ако тази работа ви харесва, отбележете хранилището със звезда на следния адрес: Github.
-
Български: https://raku.guide/bg
-
Испански: https://raku.guide/es
-
Китайски: https://raku.guide/zh
-
Немски: https://raku.guide/de
-
Португалски: https://raku.guide/pt
-
Френски: https://raku.guide/fr
-
Холандски: https://raku.guide/nl
-
Японски: https://raku.guide/ja
-
Украински: https://raku.guide/uk
1. Увод
1.1. Какво е Perl 6
Perl 6 е език от високо ниво, с общо предназначение и постепенна типизация на променливите. Perl 6 е многопарадигмен. Той поддържа процедурно, обектно-ориентирано и функционално програмиране.
-
ИННН Има няколко начина да се направи. TMTOWTDI (Произнася се Tim Toady): There is more than one way to do it.
-
Лесните неща трябва да са лесни, трудните да стават по-лесни, а невъзможните - трудни.
1.2. Понятия
-
Perl 6: Това е спецификация за език за програмиране с набор от тестове. Реализации, които изпълняват тестовете без грешка, могат да се нарекат Perl 6.
-
Rakudo: Е компилатор за Пърл 6.
-
Rakudobrew: Е програма за управление инсталациите на Ракудо.
-
Zef: Е инсталатор на модули за Пърл 6.
-
Rakudo Star: Е вързоп: Ракудо, Зеф, набор от модули за Пърл 6 и документация.
1.3. Инсталиране на Пърл 6
-
Инсталирайте Ракудобрю: https://github.com/tadzik/rakudobrew
-
Инсталирайте Ракудо: Изпълнете следната команда в терминал
rakudobrew build moar
-
Инсталирайте Зеф: Изпълнете следната команда в терминал
rakudobrew build zef
-
Инсталирайте Task::Star. Това е мета-пакет, съдържащ модулите, които вървят с изданието Rakudo Star. Изпълнете следната команда в терминал
zef install Task::Star
За да видите други възможности за инсталация, посетете http://rakudo.org/how-to-get-rakudo/\#Installing-Rakudo-Star-Linux
Имате четири възможности:
-
Следвайте същите стъпки като в Линукс
-
Инсталирайте с хоумбрю:
brew install rakudo-star
-
Инсталирайте с МакПортс:
sudo port install rakudo
-
Свалете най-новия инсталатор (файл с разширение .dmg) от http://rakudo.org/downloads/star/
-
Свалете най-новия инсталатор (файл с разширение .msi) от http://rakudo.org/downloads/star/ Ако архитектурата на системата ви е 32-битова, свалете файла с x86 в името; ако е 64-битова, свалете файла съдържащ x86_64 в името.
-
След инсталацията се уверете, че
C:\rakudo\bin
се намира в системната променлива PATH.
-
Вземете официалното изображение за Docker
docker pull rakudo-star
-
След това стартирайте контейнер с изображението
docker run -it rakudo-star
1.4. Изпълнение на код, написан на Perl 6
Можете да изпълнявате код на Пърл 6, като използувате директно неговата
интерактивна конзола - REPL (Read-Eval-Print Loop). За да направите това,
отворете терминал, напишете perl6
в терминала и натиснете [Enter]. Това ще
отвори конзолата и в нея ще се появи >
. След това напишете някакъв програмен
код и натиснете [Enter]. На следващия ред в конзолата ще се появи резултатът от
изпълнението на кода. Въведете друг ред, съдържащ програмен код, или въведете
exit
и натиснете [Enter], за да напуснете конзолата (REPL).
Друг начин за изпълнение е, като въведете програмния код във файл, запишете го и
го изпълните. Препоръчва се за разширение на скриптовете, написани на Пърл 6, да
се използува .pl6
. Изпълнете файла, като напишете в терминал perl6
filename.pl6
и натиснете [Enter]. За разлика от интерактивната конзола
(REPL), всеки ред код ще се изпълни последователно, но резултатът не ще се
изпише на екрана автоматично. Кодът трябва да съдържа израз, използуващ командата
say
, за да изведе нещо на стандартния изход (екрана).
Интерактивната конзола се използува най-вече за пробване на специфични парченца код, обикновено едноредови изрази. За програми, състоящи се от повече редове, се препоръчва да се записват във файл и след това да се изпълняват.
Едноредови изрази могат да се изпробват и на командния ред без интерактиванта
конзола, като напишете perl6 -e 'your code here'
и натиснете [Enter].
Rakudo Star върви с едноредов редактор, който се използува в интерактивната конзола (REPL). Ако сте инсталирали обикновен Rakudo, вместо Rakudo Star, най-вероятно интерактивната конзола не ви дава възможност да редактирате текущия ред, да ползвате стрелка нагоре и надолу (за да извиквате предишни команди и да ги променяте) или да ползвате табулация (клавишът TAB) за допълване на частично въведени низове. Изпълнете една от следните команда и сте готови.
|
1.5. Редактори
Тъй като през повечето време ще записваме програмите си във файлове, ни е нужен приличен текстов редактор, който разпознава синтаксиса на Пърл 6.
Аз лично използувам и препоръчвам Atom. Това е модерен редактор и поддържа синтаксиса на Пърл 6. Perl6 FE е допълнителен пакет за оцветяване на кода на Пърл 6 за Атом. Той произхожда от оригиналния пакет, който идва с Атом, но съдържа много подобрения и поправени грешки.
По-новите версии на Vim идват по подразбиране с поддръжка на синтаксиса на Пърл 6. Emacs и Padre изискват инсталиране на допълнителни пакети.
1.6. Здравей, Свят!
Ще започнем с ритуала hello world
.
say 'Здравей, Свят!';
Това може да бъде написано и така:
'Здравей, Свят!'.say;
1.7. Преглед на синтаксиса
Пърл 6 е свободна форма: Свободни сте (през повечето време) да използувате колкото ви е угодно празни пространства (за разлика от Питон - бел. прев.).
Твърденията са обикновено логически ред код. Те завършват с точка и запетая.
say "Здрасти" if True;
Изразите са специален тип твърдение, което връща стойност:
1+2
ще върне 3
Изразите се състоят от Членове и Оператори.
Членове. Те са:
-
Променливи: Съдържат стойност, която може да бъде променяна.
-
Буквални стойности (литерали): Непроменяема, буквална стойност - число или низ.
Оператори. Те са няколко типа:
Тип |
Обяснение |
Пример |
Представка |
Преди члена. |
|
Вставка |
Между членовете |
|
Наставка |
След члена |
|
Ограждащ |
Около члена |
|
Ограждаща наставка |
След един член и ограждащ друг член |
|
1.7.1. Идентификатори (Имена)
Идентификаторите представляват имена, дадени на членовете.
-
Трябва да започват с буква или знак за подчертавка.
-
Могат да съдържат числа (ако не са първия знак в името на променливата).
-
Могат да съдържат тирета или апострофи (ако не са първи или последен знак). От дясната страна на тирето или апострофа винаги трябва да има буква.
Правилно |
Неправилно |
|
|
|
|
|
|
|
|
|
|
-
КамилоОбразно:
variableNo1
-
шиш-кебап:
variable-no1
-
змие_видно:
variable_no1
Можете да именувате променливите си както искате, но е добра практика да се спрете на един вариант и да го следвате.
Като използувате смислени имена, ще улесните живота на всички - и вашият, и на вашите колеги.
-
var1 = var2 * var3
е правилно синтактично, но безсмислено. -
monthly-salary = daily-rate * working-days
тези са по-смислени имена за променливи.
1.7.2. Коментари
Коментарът е текст, който се пропуска от компилатора, и се ползва като бележка или пояснение.
Коментарите са три типа:
-
Едноредови:
# Това е едноредов коментар
-
Вложен/вмъкнат:
say #`(Това е вмъкнат коментар) "Hello World."
-
Многоредови:
=begin comment Това е многоредов коментар. Първа бележка Второ пояснение =end comment
1.7.3. Кавички
Низовете се ограждат с двойни или единични кавички.
Използувайте двойни кавички, когато:
-
низът ви съдържа апостроф;
-
низът ви съдържа променливи.
say 'Hello World'; # Hello World
say "Hello World"; # Hello World
say "Don't"; # Don't
my $name = 'John Doe';
say 'Hello $name'; # Hello $name
say "Hello $name"; # Hello John Doe
2. Оператори
2.1. Често използувани оператори
Следващата таблица изрежда най-често използуваните оператори.
Оператор | Тип | Описание | Пример | Резултат |
---|---|---|---|---|
|
|
Събиране |
|
|
|
|
Изваждане |
|
|
|
|
Умножение |
|
|
|
|
Степенуване |
|
|
|
|
Деление |
|
|
|
|
Деление на цели числа (закръгля надолу) |
|
|
|
|
Деление до остатък |
|
|
|
|
Делимост |
|
|
|
|
|||
|
|
Най-голям общ делител |
|
|
|
|
Най-малко общо кратно |
|
|
|
|
Цифрово равенство |
|
|
|
|
Цифрово неравенство |
|
|
|
|
По-малко |
|
|
|
|
По-голямо |
|
|
|
|
По-малко или равно |
|
|
|
|
По-голямо или равно |
|
|
|
|
Еднаквост между низове |
|
|
|
|
Низовете не са еднакви |
|
|
|
|
Присвояване |
|
|
|
|
Свързване на низове |
|
|
|
|
|||
|
|
Повторение на низове |
|
|
|
|
|||
|
|
Умно съвпадение |
|
|
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
Увеличаване |
|
|
|
Увеличаване |
|
|
|
|
|
Намаляване |
|
|
|
Намаляване |
|
|
|
|
|
Свежда операнда до числова стойност |
|
|
|
|
|||
|
|
|||
|
|
Свежда операнда до числова стойност и връща отрицанието |
|
|
|
|
|||
|
|
|||
|
|
Свежда операнда до булева стойност |
|
|
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
Свежда операнда до булева стойност и връща отрицанието |
|
|
|
|
Създател на поредица |
|
|
|
|
Създател на поредица |
|
|
|
|
Създател на поредица |
|
|
|
|
Създател на поредица |
|
|
|
|
Създател на поредица |
|
|
|
|
Мързелив създател на списък |
|
|
|
|
Сплескване |
|
|
|
|
2.2. Обърнати Оператори
Добавяне на R
преди който и да е оператор ще обърне операндите му.
Нормална операция | Резултат | Обърнат Оператор | Резултат |
---|---|---|---|
|
|
|
|
|
|
|
|
2.3. Съкращаващи Оператори
Операторите за съкращения се прилагат върху списък от стойности.
Съставят се, като операторът се огради със []
Нормална операция | Резултат | Съкращаващ оператор | Резултат |
---|---|---|---|
|
|
|
|
|
|
|
|
За да видите пълния списък с оператори и приоритетите им, отидете на https://docs.perl6.org/language/operators |
3. Променливи
Променливите в Пърл 6 биват три типа - Скалари, Масиви и Хешове.
Променливите се различават по т.нар сиджил (означава "знак" на латински). Този знак се намира в началото на всяка променлива.
-
$
се използува за скалари -
@
се използува за масиви -
%
се използува за хешове
3.1. Скалари
Скаларът (Scalar) съдържа единична стойност или указател (reference).
Бел. прев.: Указателите в Пърл не са като указателите в C, и затова са познати повече като референции. Все пак по-подходящата дума е указател, а не референция. |
# Низ (String)
my $name = 'Перко Наумов';
say $name;
# Цяло число (Integer)
my $age = 99;
say $age;
В зависимост от стойността, която съдържа скаларната променлива (нейния тип), върху нея могат да се извършват различни действия.
my $name = 'Перко Наумов';
say $name.uc;
say $name.chars;
say $name.flip;
ПЕРКО НАУМОВ
12
вомуаН окреП
За да видите пълния списък с методите, приложими върху низове (скаларни променливи от тип Str), вижте https://docs.perl6.org/type/Str |
my $age = 17;
say $age.is-prime;
True
За да видите пълния списък с методите, приложими върху цели числа (скаларни променливи от тип Int), вижте https://docs.perl6.org/type/Int |
my $age = 2.3;
say $age.numerator;
say $age.denominator;
say $age.nude;
23
10
(23 10)
За да видите пълния списък с методите, приложими върху рационални числа (десетични дроби), вижте https://docs.perl6.org/type/Rat |
3.2. Масиви
Масивите (Arrays) са списъци, които съдържат множество стойности.
my @animals = 'камила','лама','сова';
say @animals;
От долния пример се вижда, че върху масивите могат да се извършват много операции:
Тилдата ~ се използува за свързване на низове.
|
Script
my @animals = 'camel','vicuña','llama';
say "В зоологическата градина има " ~ @animals.elems ~ " животни.";
say "Животните са: " ~ @animals;
say "Аз ще осиновя една сова";
@animals.push("owl");
say "Сега в градината ми има: " ~ @animals;
say "The first animal we adopted was the " ~ @animals[0];
@animals.pop;
say "Unfortunately the owl got away and we're left with: " ~ @animals;
say "We're closing the zoo and keeping one animal only";
say "We're going to let go: " ~ @animals.splice(1,2) ~ " and keep the " ~ @animals;
Изход
В зоологическата градина има 3 животни.
Животните са: camel vicuña llama
Аз ще осиновя една сова
Сега в градината ми има: camel vicuña llama owl
The first animal we adopted was the camel
Unfortunately the owl got away and we're left with: camel vicuña llama
We're closing the zoo and keeping one animal only
We're going to let go: vicuña llama and keep the camel
.elems
връща броя на елементите в масива.
.push()
добавя един или повече елементи към масива.
Можем да достъпим отделен елемент от масива като укажем мястото му @animals[0]
.
.pop
премахва последния елемент от масива и го връща.
.splice(a,b)
ще премахне b
елемента като започне от позиция a
.
3.2.1. Масиви с предопределен размер
Обикновено масив се обявява по следния начин:
my @array;
По подразбиране масивът има неопределен брой елементи, и затова го наричат автоматично-разширяващ се. Масивът ще приеме какъвто и да е брой стойности без ограничение.
Но можем да създаваме и масиви с определен брой елементи. В такива масиви не можете да достъпите или добавяте елементи извън определения размер на масива.
За да обявите масив с определен брой елементи, добавете броя на елементите в квадратни скоби веднага след името на масива.
my @array[3];
Този масив ще може да съдържа най-много три стойности с места от 0 до 2.
my @array[3];
@array[0] = "първа стойност";
@array[1] = "втора стойност";
@array[2] = "трета стойност";
Не можете да добавите четвърта стойност в този масив:
my @array[3];
@array[0] = "първа стойност";
@array[1] = "втора стойност";
@array[2] = "трета стойност";
@array[3] = "четвърта стойност";
Стойност 3 (четвърта стойност) е извън обхвата на масива (стойностите могат да са най-много три - от 0 до 2)
3.2.2. Многомерни масиви
Масивите, които видяхме до тук, са едномерни. За щастие, можем да създаваме и многомерни масиви в Пърл 6.
my @tbl[3;2];
Този масив е двумерен. Първото измерение може да съдържа най-много 3 стойности, а второто - най-много 2.
Представете си го като таблица с 3 реда и 2 колони.
my @tbl[3;2];
@tbl[0;0] = 1;
@tbl[0;1] = "x";
@tbl[1;0] = 2;
@tbl[1;1] = "y";
@tbl[2;0] = 3;
@tbl[2;1] = "z";
say @tbl
[[1 x] [2 y] [3 z]]
[1 x]
[2 y]
[3 z]
За да видите пълния справочник за масив (Array), посетете https://docs.perl6.org/type/Array |
3.3. Хешове
my %столици = ('UK','London','Germany','Berlin');
say %столици;
my %capitals = (UK => 'London', Germany => 'Berlin');
say %capitals;
Някои от методите, които могат да се извикват върху хешове, са:
Скрипт
my %capitals = (UK => 'London', Germany => 'Berlin');
%capitals.push: (France => 'Paris');
say %capitals.kv;
say %capitals.keys;
say %capitals.values;
say "The capital of France is: " ~ %capitals<France>;
Изход
(France Paris Germany Berlin UK London)
(France Germany UK)
(Paris Berlin London)
The capital of France is: Paris
.push: (key => 'Value')
добавя нова двойка ключ/стойност.
.kv
връща списък, съдържащ всички ключове и стойности.
.keys
връща списък, съдържащ всички ключове.
.values
връща списък, съдържащ всички стойности.
Можем да достъпим отделна стойност в хеша, като укажем нейния ключ %hash<key>
За да видите пълния справочник за хешовете, посетете https://docs.perl6.org/type/Hash |
3.4. Типове
В примерите досега не задавахме типа стойност, който да съдържа променливата.
.WHAT Ще върне типа на стойността, съдържаща се в променливата.
|
my $var = 'Text';
say $var;
say $var.WHAT;
$var = 123;
say $var;
say $var.WHAT;
Както виждате от горния пример, типът на стойността в променливата $var
първо
беше (Str), а след това (Int).
Този начин на програмиране се нарича динамично типизиране. Динамично означава, че променливите могат да съдържат стойности от Всякакъв (Any) тип.
Сега опитайте да изпълните следния пример. Обърнете внимание на използуването на
Int
пред името на променливата.
my Int $var = 'Text';
say $var;
say $var.WHAT;
Присвояването ще се провали и ще върне следната грешка:
Type check failed in assignment to $var; expected Int but got Str
Проверката за тип е неуспешна при присвояване на $var; очакваше се Int, но бе подаден Str
Този път указахме, че типа на променливата ще бъде (Int). Опитът да му присвоим низ (Str) не беше успешен.
Този начин на програмиране се нарича статично типизиране. Статично означава, че типа на променливите се указва предварително и не може да бъде променян.
Пърл 6 е постепенно типизиран; позволява и статично, и динамично типизиране.
my Int @array = 1,2,3;
say @array;
say @array.WHAT;
my Str @multilingual = "Здравей", "Hello","Salut","Hallo","您好","안녕하세요","こんにちは";
say @multilingual;
say @multilingual.WHAT;
my Str %capitals = (UK => 'London', Germany => 'Berlin');
say %capitals;
say %capitals.WHAT;
my Int %country-codes = (UK => 44, Germany => 49);
say %country-codes;
say %country-codes.WHAT;
Най-вероятно никога няма да използувате първите два, но са упоменати, да ги знаете.
|
|
|
|
|
|
||
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3.5. Самонаблюдение
Самонаблюдение (Introspection) е действието по взимане на информация за даден обект, например какъв е типът му.
В един от предишните примери използувахме .WHAT
, за да върнем типа на променливата.
my Int $var;
say $var.WHAT; # (Int)
my $var2;
say $var2.WHAT; # (Any)
$var2 = 1;
say $var2.WHAT; # (Int)
$var2 = "Hello";
say $var2.WHAT; # (Str)
$var2 = True;
say $var2.WHAT; # (Bool)
$var2 = Nil;
say $var2.WHAT; # (Any)
Типът на дадена променлива показва каква стойност може да съдържа.
Типът на твърдо обявена празна променлива е типът, с който е била обявена.
Типът на празна променлива, която не е твърдо обявена, е Всякакъв (Any)
За да изчистите стойността на променлива, присвойте ѝ Nil
.
3.6. Видимост
Преди да използувате променлива за първи път, трябва да я обявите.
В Пърл 6 се използуват няколко начина за обявяване на променливи. В примерите
досега използувахме my
.
my $var=1;
Операторът my
дава на променливата словна (lexical) видимост.
Иначе казано, променливата ще бъде видима (използваема) само в блока от код, в който е обявена.
В Пърл 6 блокът представлява всичко, намиращо се между двойка отваряща и затваряща фигурни скоби - { }
.
Ако няма определен блок, променливата е достъпна в целия скрипт.
{
my Str $var = 'Text';
say $var; # е достъпна
}
say $var; # е недостъпна, връща грешка
Тъй като променливата е видима само в блока, в който е обявена, можете да ползвате същото име за друга променлива в друг блок.
{
my Str $var = 'Text';
say $var;
}
my Int $var = 123;
say $var;
3.7. Присвояване и Обвързване
В предишните примери видяхме как да присвояваме стойности на променливи.
Присвояването се прави с помощта на оператора =
.
my Int $var = 123;
say $var;
Можем да променим стойността, присвоена на променлива:
my Int $var = 123;
say $var;
$var = 999;
say $var;
Изход
123
999
И напротив - когато обвързваме стойност с променлива, не можем да променим стойността.
Обвързването се извършва с помощта на оператора :=
.
my Int $var := 123;
say $var;
$var = 999;
say $var;
Изход
123
Cannot assign to an immutable value
Не може да се присвои към непроменяема стойност
my $a;
my $b;
$b := $a;
$a = 7;
say $b;
$b = 8;
say $a;
Изход
7
8
Обвързването на променливи една с друга е двупосочно.
Резултатът от $a := $b
и $b := $a
е един и същ.
Повече за променливите ще научите на адрес https://docs.perl6.org/language/variables |
4. Функции и мутатори
Важно е да се прави разлика между функции и мутатори.
Функциите не променят състоянието на обектите, върху които са извикани.
Мутаторите (менячи - бел. прев.) променят състоянието на обекта.
Скрипт
1
2
3
4
5
6
7
8
9
10
my @числа = [7,2,4,9,11,3];
@числа.push(99);
say @числа; #1
say @числа.sort; #2
say @числа; #3
@числа.=sort;
say @числа; #4
Изход
[7 2 4 9 11 3 99] #1
(2 3 4 7 9 11 99) #2
[7 2 4 9 11 3 99] #3
[2 3 4 7 9 11 99] #4
.push
е меняч (мутатор); той променя състоянието на масива (#1)
.sort
е функция; тя връща подреден масив, но не променя състоянието на масива, върху който се използува:
-
(#2) показва, че връща подреден масив.
-
(#3) показва, че състоянието на масива е все така непроменено.
За да накараме дадена функция да действа като мутатор, използуваме .=
вместо .
(#4) (ред 9 от скрипта)
5. Цикли и условия
Пърл 6 има много изрази за условия и цикли.
5.1. if
Кодът се изпълнява, само ако условието е изпълнено. Иначе казано - ако изразът се изчисли като Истина (True
).
my $age = 19;
if $age > 18 {
say 'Welcome'
}
В Пърл 6 можем да сменим местата на условието и кода.
Макар местата да са сменени, проверката на условието винаги се изпълнява първа.
my $age = 19;
say 'Добре дошъл' if $age > 18;
В случай че условието не се изпълни, можем да укажем алтернативни блокове код чрез:
-
else
-
elsif
# изпълнение на различен код при различни стойности на променливата
my $брой-места = 9;
if $брой-места <= 5 {
say 'Аз съм седан'
} elsif $брой-места <= 7 {
say 'Аз съм мини-ван'
} else {
say 'Аз съм ван'
}
5.2. unless
Отрицанието на твърдението, проверявано чрез if
, може да бъде изразено чрез unless
.
Следният код:
my $чисти-обувки = False;
if not $чисти-обувки {
say 'Почисти си обувките!'
}
може да бъде написан като:
my $чисти-обувки = False;
unless $чисти-обувки {
say 'Почисти си обувките!'
}
Отрицание на дадено твърдение се постига чрез !
или not
.
unless (условие)
се използува вместо if not (условие)
.
unless
не може да има съответстваща else
клауза.
5.3. with
with
е като if
, но проверява дали променливата има присвоена стойност.
my Int $var=1;
with $var {
say 'Hello'
}
Ако изпълните кода, без да сте присвоили стойност на променливата, нищо няма да се изведе на екрана.
my Int $var;
with $var {
say 'Hello'
}
without
е обратното на with
. Същото, каквото е unless
за if
.
Ако първото with
условие не е изпълнено, може да укажете друго условие с orwith
.
with
и orwith
са подобни на if
и elsif
.
5.4. for
Цикълът for
повтаря действието върху множество стойности.
my @array = [1,2,3];
for @array -> $array-item {
say $array-item * 100
}
Забележете, че създадохме променливата $array-item
и приложихме действието
*100
върху всеки елемент от масива.
5.5. given
given
в Пърл 6 е същото като switch
в другите езици, но много по-мощно.
my $var = 42;
given $var {
when 0..50 { say 'По-малко или равно на 50' }
when Int { say "е Int" }
when 42 { say 42 }
default { say "к'во?" }
}
При успешно съвпадение, процесът на търсене на удовлетворяване на условието,
следващо when
, се прекратява.
Ако обаче добавите proceed
в блока за изпълнение, процесът на търсене на
съвпадение продължава.
my $var = 42;
given $var {
when 0..50 { say 'По-малко или равно на 50'; proceed }
when Int { say "е Int"; proceed }
when 42 { say 42 }
default { say "к'во?" }
}
5.6. loop
loop
е друг начин за писане на for
цикъл.
Всъщност loop
е начинът, по който се пишат for
циклите в езиците, подобни на C.
Пърл 6 принадлежи към това семейство.
loop (my $i = 0; $i < 5; $i++) {
say "Текущото число е $i"
}
За да научите повече за циклите и условните изрази, погледнете https://docs.perl6.org/language/control |
6. I/O
В Пърл 6 най-често използуваните входно-изходни интерфейси са терминалът и файловете.
6.1. Основни действия в терминал
6.1.1. say
say
пише в стандартния изход. Добавя нов ред в края. С други думи, следният код
say 'Hello Mam.';
say 'Hello Sir.';
ще изведе текста в кавичките на два отделни реда.
6.1.2. print
print
има подобно поведение като say
, но не добавя нов ред.
Заместете say
с print
и сравнете изхода от действията.
6.1.3. get
get
се използува за прихващане на входни данни от терминала.
my $name;
say "Hi, what's your name?";
$name = get;
say "Dear $name welcome to Perl 6";
При изпълнение на горния код, терминалът ще чака да въведете името си. Въведете го и натиснете [Enter]. След това ще видите поздрава.
6.1.4. prompt
prompt
е съчетание от print
и get
.
Горният пример може да бъде написан така:
my $name = prompt "Hi, what's your name? ";
say "Dear $name welcome to Perl 6";
6.2. Изпълнение на външни команди
Две подпрограми могат да се използуват за извикване на външни команди:
-
run
Изпълнява външна команда (програма) без посредничеството на системната обвивка. -
shell
Изпълнява команда през системната обвивка. Тя е зависима от операционната система и от обвивката ѝ. Всички мета-знаци на обвивката се интерпретират от нея - включително|
, пренасочването на променливите на обкръжението и т.н.
my $name = 'Neo';
run 'echo', "hello $name";
shell "ls";
shell "dir";
echo
и ls
са познати команди от обвивката в Linux:
echo
отпечатва текст в терминала (същото като print
в Perl 6)
ls
показва списък от файлове и папки в текущата папка
dir
е същото като ls
, но в Windows.
6.3. Писане в и четене от файлове
6.3.1. slurp
slurp
се използува за четене на данни от файл наведнъж.
Създайте файл със следното съдържание:
John 9
Johnnie 7
Jane 8
Joanna 7
my $data = slurp "datafile.txt";
say $data;
6.3.2. spurt
spurt
се използува за запис на данни във файл наведнъж.
my $newdata = "New scores:
Paul 10
Paulie 9
Paulo 11";
spurt "newdatafile.txt", $newdata;
С изпълнението на горния код ще се създаде нов файл, именуван newdatafile.txt.
Той ще съдържа данните от $newdata
.
6.4. Работа с файлове и папки
Perl 6 може да покаже списък от папки и файлове без помощта на системни команди (например като
използвате ls
).
say dir; # Показва списък със съдържанието на текущата папка
say dir "/Documents"; # Показва списък със съдържанието на указаната папка
-
Освен това, можете да създавате и триете папки.
mkdir "newfolder";
rmdir "newfolder";
mkdir
създава нова папка.
rmdir
изтрива празна папка. Връща грешка, ако не е празна.
Също така можете да проверявате дали указаният път съществува, дали е файл или папка:
В папката, където ще изпълните долния скрипт, създайте празна папка folder123
и празен файл с разширение pl6 script123.pl6
say "script123.pl6".IO.e;
say "folder123".IO.e;
say "script123.pl6".IO.d;
say "folder123".IO.d;
say "script123.pl6".IO.f;
say "folder123".IO.f;
IO.e
проверява дали файлът съществува.
IO.f
проверява дали указаният път е файл.
IO.d
проверява дали указаният път е папка.
Потребителите на Windows могат да използуват / или \\ за разделителC:\\rakudo\\bin C:/rakudo/bin |
За повече информация, свързана с входно-изходните операции, вижте https://docs.perl6.org/type/IO |
7. Подпрограми
7.1. Създаване
Подпрограмите (наричани също функции) са начин да се събере накуп набор от
действия (функционалност) и да се използва по-късно.
За да създадете подпрограма, напишете ключовата дума sub
, последвана от името
на подпрограмата. След това подпрограмата може да бъде извиквана чрез изписване
на името ѝ.
Разгледайте примера:
sub alien-greeting {
say "Hello earthlings";
}
alien-greeting;
В този пример е показана подпрограма, която не изисква никакви входни данни.
7.2. Списък с параметри
Подпрограмите могат да изискват входни данни. Тези данни се предоставят чрез подаване на параметри. Една подпрограма може да няма никакви или да има няколко параметъра. Броят и типът на параметрите на една подпрограма се наричат сигнатура (от лат. signatura — обозначение, бел. прев.).
Следващата подпрограма приема низ като параметър.
sub say-hello (Str $name) {
say "Hello " ~ $name ~ "!!!!"
}
say-hello "Paul";
say-hello "Paula";
7.3. Многократност
Може да създадете няколко подпрограми с едно и също име, но различен списък от параметри
(многократно). Когато подпрограмата бъде извикана, средата за изпълнение ще реши коя от тях да
изпълни, в зависимост от типа и броя на подадените параметри. Този тип подпрограми се създават
както обикновено, но като използувате ключовата дума multi
вместо sub
.
multi greet($name) {
say "Good morning $name";
}
multi greet($name, $title) {
say "Good morning $title $name";
}
greet "Johnnie";
greet "Laura","Mrs.";
Оригиналното заглавие на секцията е "Multiple Dispatch". Множествено разпределение или "много-методи" е свойство на някои програмни езици, при което една функция или метод може да бъде динамично избрана за изпълнение в зависимост от типа и броя на подадените ѝ аргументи. От Уикипедия: https://en.wikipedia.org/wiki/Multiple_dispatch (Бел. Прев.) |
7.4. Незадължителни и подразбиращи се параметри
Ако сте създали подпрограма, приемаща един параметър и я извикате, без да ѝ подавате нищо, изпълнението ще се провали.
Пърл 6 ни дава възможност да създаваме подпрограми с:
-
Незадължителни параметри
-
Параметри със стойност по подразбиране
Незадължителните параметри се задават, като в края на името на параметъра
(променливата) се добави ?
.
sub say-hello($name?) {
with $name { say "Hello " ~ $name }
else { say "Hello Human" }
}
say-hello;
say-hello("Laura");
Ако потребителят не подаде параметър, той може да приема стойност по подразбиране.
Това се постига, като присвоим стойността, когато създаваме подпрограмата.
sub say-hello($name="Matt") {
say "Hello " ~ $name;
}
say-hello;
say-hello("Laura");
7.5. Връщане на стойности
Всички подпрограми дотук правят нещо, показват някакъв текст в терминала.
Понякога обаче, може да поискаме подпрограмата да върне някаква стойност, която да използуваме по-късно в приложението.
По подразбиране, резултатът от изпълнението на последния ред в нашата подпрограма е стойността, която тя връща.
sub squared ($x) {
$x ** 2;
}
say "7 squared is equal to " ~ squared(7);
За по-голяма яснота, е добра идея изрично да укажем какво връщаме. Това се
прави с ключовата дума return
.
sub squared ($x) {
return $x ** 2;
}
say "7 squared is equal to " ~ squared(7);
7.5.1. Ограничаване на връщаните стойности
В един от предишните примери видяхме, че можем да зададем определен тип на приемания параметър. Същото може да бъде направено с връщаната стойност.
За да ограничим типа на връщаната стойност, използуваме отличителя returns
или стрелка -->
в сигнатурата.
returns
sub squared ($x) returns Int {
return $x ** 2;
}
say "1.2 на квадрат е " ~ squared(1.2);
sub squared ($x --> Int) {
return $x ** 2;
}
say "1.2 squared is equal to " ~ squared(1.2);
Ако не върнем стойност от същия тип, програмата ни ще хвърли грешка.
Type check failed for return value; expected Int but got Rat (1.44)
Ограниченията по тип могат да задават не само типа на връщаната стойност, но и това, дали е дефинирана стойността. В предишните примери указвахме, че типа на връщаната стойност трябва да бъде
И така, да се използуват тези ограничения е добра практика.
|
За повече информация относно подпрограмите и функциите, вижте https://docs.perl6.org/language/functions |
8. Функционално Програмиране
В тази глава ще разгледаме част от възможностите на езика, които улесняват функционалното програмиране.
8.1. Функциите са граждани първа класа
Функциите/подпрограмите са граждани първа класа:
-
Могат да се подават като параметри
-
Могат да бъдат връщани от други функции
-
Могат да бъдат присвоявани като стойност на променливи
Прекрасен пример е функцията map
.
map
е функция от високо ниво, тя приема друга функция като параметър.
my @array = <1 2 3 4 5>;
sub squared($x) {
$x ** 2
}
say map(&squared,@array);
(1 4 9 16 25)
Създадохме подпрограма, наречена squared
, която повдига на квадрат всяко подадено ѝ число.
След това използувахме map
, функция от високо ниво, и подадохме два параметъра - функция и масив.
Изходът е списък от елементите на масива, повдигнати на квадрат.
Забележете, че когато подаваме функция като параметър, трябва да поставим пред името ѝ знака &
.
8.2. Безименни функции
Безименната функция се нарича още ламбда.
Безименната функция не е обвързана с идентификатор (тя няма име).
Нека пренапишем примера с map
, като използуваме безименна функция.
my @array = <1 2 3 4 5>;
say map(-> $x {$x ** 2},@array);
Забележете, че вместо да обявяваме подпрограма и да я подаваме като параметър на
map
, ние я създадохме направо в безименната подпрограма -> $x {$x ** 2}
.
На жаргона на Пърл 6 наричаме това записване остър блок
my $squared = -> $x {
$x ** 2
}
say $squared(9);
8.3. Верижност (Chaining)
В Пърл 6 методите могат да бъдат извиквани верижно, така че да не се налага да подавате изхода от един метод на друг.
Всяка вградена функция може да се използува и като метод върху обект. (Бел. прев.) |
Ето едно неверижно решение:
my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
my @final-array = reverse(sort(unique(@array)));
say @final-array;
Първо извикваме функцията unique
, като ѝ подаваме @array
. След това подаваме
изхода от нея на sort
и накрая подаваме изхода от подреждането на
reverse
.
Но можем да се възползваме от възможността за верижно извикване на методи и да запишем примера по следния начин:
my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
my @final-array = @array.unique.sort.reverse;
say @final-array;
Вече виждате, че верижното извикване е по-лесно за четене.
8.4. Оператор за подаване
Операторът за подаване, наричан тръба в някои езици за програмиране, представя още по-добре верижното извикване.
my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
@array ==> unique()
==> sort()
==> reverse()
==> my @final-array;
say @final-array;
Тръгваме от `@array`, след което връщаме списък с неповторими елементи
после ги подреждаме,
обръщаме реда им
и накрая съхраняваме изхода във @final-array
Виждате, че последователността на извикване на методите е отгоре надолу.
my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
my @final-array-v2 <== reverse()
<== sort()
<== unique()
<== @array;
say @final-array-v2;
Подаването назад е като подаването напред, но наобратно.
Последователността на извикване на методите е отдолу нагоре – от последната към
първата стъпка.
8.5. Свръх-оператор
Свръх-операторът (hyper operator) >>.
ще извика даден метод върху всички елементи от един
списък и ще върне списък с резултатите.
my @array = <0 1 2 3 4 5 6 7 8 9 10>;
sub is-even($var) { $var %% 2 };
say @array>>.is-prime;
say @array>>.&is-even;
Можем да използуваме свръх-оператора и с вградените методи в Пърл 6, например
is-prime
, който ни казва дали едно число е просто.
Също така, можем да създаваме нови подпрограми и да ги извикваме чрез
свръх-оператора. В този случай трябва да поставим &
пред името на метода,
например &is-even
.
Това е много практично, тъй като ни освобождава от писането на цикли for
, за
да обхождаме всяка стойност от масива.
Пърл 6 гарантира, че редът на изходите от работата на метода, извикан
чрез свръх-оператора, ще е същият като на входните стойности. Но няма гаранция, че Пърл 6 ще извика метода последователно, както е редът на елементите, нито че извикването ще е в същата нишка. Така че, бъдете внимателни с методи, които имат странични ефекти като say
(където страничният ефект е показването на подадената стойност).
|
8.6. Сливания
Сливането (junction) е логическо съпоставяне на стойности.
В израза долу 1|2|3
е сливане.
my $var = 2;
if $var == 1|2|3 {
say "Променливата има стойност 1 или 2 или 3"
}
Използуването на сливания предизвиква автоматично създаване на нишки (autothreading); операцията се извършва за всеки елемент от сливането, като всички резултати са събрани в ново сливане и върнати.
8.7. Мързеливи списъци
Мързелив списък е този, който е изчислен мързеливо.
Мързеливо означава отлагане на изчислението на даден израз до момента, когато е
необходимо, и избягване повторение на изчислението, като се съхранява резултата
в паметта.
Ползите са:
-
Нарастване на производителността чрез избягване на излишни изчисления;
-
Възможността да се създават при необходимост безкрайни структури от данни;
-
Възможността да се управлява изпълнението.
За да построим мързелив списък, използуваме вмъкнатия оператор …
.
Мързеливият списък има начален(лни) елемент(и), генератор (начин за
създаване на списъка – бел. прев.) и край.
my $lazylist = (1 ... 10);
say $lazylist;
Началният елемент е 1, а крайният е 10. Не е определен генератор за създаване
на списъка, така че се използува подразбиращият се генератор – последователно
нарастване с единица (+1)
С други думи, този мързелив списък (ако е необходимо) ще върне следните
елементи (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
my $lazylist = (1 ... Inf);
say $lazylist;
Този списък може да върне (ако е необходимо) всяко цяло число между 1 и безкрайност, т.е. всяко цяло число.
my $lazylist = (0,2 ... 10);
say $lazylist;
Първоначалните елементи са 0 и 2, а крайният елемент – 10. Не е определен генератор, но Пърл 6 ще изведе генератора от първоначалните елементи (+2). Този мързелив списък може да върне (ако е необходимо) елементите (0, 2, 4, 6, 8, 10).
my $мързел-списък = (0, { $_ + 3 } ... 12);
say $мързел-списък;
В този пример определихме изрично генератор, ограден с { }
.
Този списък би върнал (ако е нужно) елементите (0, 3, 6, 9, 12).
Ако използуваме изрично зададен генератор, крайният елемент трябва да е стойност,
която е възможно да бъде върната от генератора. Може да заместите Това няма да спре генератора
Това ще спре генератора
|
8.8. Затваряния
Всички съставени от код обекти в Пърл 6 са затваряния (closures). Това означава, че те могат да се обръщат към лексикални (частни) променливи от заобикалящия ги блок.
sub generate-greeting {
my $name = "John Doe";
sub greeting {
say "Good Morning $name";
};
return &greeting;
}
my $generated = generate-greeting;
$generated();
Ако изпълните горния код, той ще изпише Good Morning John Doe
в терминала.
Изходът от изпълнението е прост, но интересното в примера е, че вътрешната
подпрограма greeting
бива върната от външната, преди да е изпълнена.
$generated
е затваряне.
Затварянето е специален обект, съчетаващ две неща:
-
Подпрограма;
-
Обкръжението, в което подпрограмата е създадена.
Обкръжението се състои от всички локални променливи, които са били достъпни по
време на създаване на подпрограмата. В този случай $generated
е затваряне,
което включва в себе си подпрограмата greeting
и низа John Doe
, който
съществуваше, когато подпрограмата бе създадена.
Да видим и по-интересен пример.
sub greeting-generator($period) {
return sub ($name) {
return "Good $period $name"
}
}
my $morning = greeting-generator("Morning");
my $evening = greeting-generator("Evening");
say $morning("John");
say $evening("Jane");
В този пример създадохме подпрограма greeting-generator($period)
, която
приема един аргумент $period
и връща нова подпрограма. Върнатата подпрограма
приема един аргумент $name
и връща съставения поздрав.
Всъщност, greeting-generator
е "фабрика" за подпрограми. В този пример
използвахме greeting-generator
, за да създадем две подпрограми. Едната казва
Good Morning
, а другата – Good Evening
.
$morning
и $evening
са затваряния. Те са създадени по един и същи начин, но съхраняват различно обкръжение.
В обкръжението на $morning
$period
е Morning
. В обкръжението на $evening
$period
е Evening
.
9. Класове и обекти
В предишната глава научихме как Пърл 6 улеснява функционалното програмиране. В тази глава ще разгледаме обектно-ориентираното програмиране в Пърл 6.
9.1. Въведение
Обектно-ориентираното програмиране е една от широко използуваните парадигми напоследък. Обектът е набор от променливи и подпрограми, събрани заедно. Променливите се наричат атрибути (член-променливи), а подпрограмите – методи. Атрибутите определят състоянието, а методите определят поведението на обекта.
Класът е образец (шаблон) за създаване на обекти.
За да разберем взаимовръзката, нека разгледаме следния пример:
(Превеждам и програмния код на български, просто защото е възможно и той ще
работи. Не е ли това прекрасно? Пробвайте го в конзолата. Бел. прев.)
В една стая има четирима души. |
обекти ⇒ 4 човека |
Тези четирима души са Човеци |
Клас ⇒ Човек |
Те имат различни имена, възраст, пол и народност. |
атрибути ⇒ име, възраст, пол, народност |
На обектно-ориентиран жаргон казваме, че обектите са инстанции (отделни случаи) на един клас.
Да разгледаме следния скрипт:
class Човек {
has $.име;
has $.възраст;
has $.пол;
has $.народност;
}
my $иван = Човек.new(име => 'Иван', възраст => 23, пол => 'М', народност => 'българин');
say $иван;
Ключовата дума class
се използува за определяне на класа.
Ключовата дума has
(има) се използува, за да определи член-променливите на класа.
Методът .new()
се нарича конструктор. Той създава обекта като отделен
случай на класа, върху който е извикан.
В скрипта горе $иван
съдържа указател към нов случай на "Човек", определен чрез Човек.new()
.
Параметрите, подадени на метода .new()
, се използуват за определяне членовете на новосъздадения обект.
На класа може да се даде лексикална видимост, като се използува my
:
my class Human {
}
9.2. Капсулиране
Капсулирането (Encapsulation) е понятие в обектно-ориентираното програмиране, което групира набор от данни и методи заедно. Данните (атрибути) трябва да са частни, тоест, достъпни само от вътрешността на обекта. За достъп до данните се използуват методи, наречени аксесори (от access – достъп, бел. прев.).
Двата скрипта долу имат един и същ изход.
my $var = 7;
say $var;
my $var = 7;
sub sayvar {
$var;
}
say sayvar;
Методът sayvar
е аксесор. Той опосредства достъпа до променливата, без да имаме пряк достъп до нея.
Капсулирането е улеснено в Пърл 6 чрез използуването на втори знак
(twigil). Вторият знак е вторичен сиджил. Той се поставя между първия знак
и името на атрибута.
Два вида втори знак се използуват в класовете:
-
!
се използува за изрично указване, че член-променливата е частна. -
.
се използува за автоматично създаване на аксесор на член-променливата.
По подразбиране всички член-променливи са частни, но е добър навик винаги да се
ползва !
като втори знак.
Затова, трябва да пренапишем горния клас както следва:
class Human {
has $!name;
has $!age;
has $!sex;
has $!nationality;
}
my $john = Human.new(name => 'John', age => 23, sex => 'M', nationality => 'American');
say $john;
Добавете в скрипта следния израз: say $john.age;
Ще получим съобщение за грешка: Method 'age' not found for invocant of class
'Human'
– Методът 'age' не е намерен за извикващия на класа 'Human'
Причината е, че $!age
като частна може да бъде ползувана само вътре в обекта.
Опитвайки се да я достъпим отвън, получаваме грешка.
Сега заместете has $!age
с has $.age
и вижте изхода при изпълнение на say $john.age;
9.3. Именувани и позиционни параметри
В Пърл 6 всички класове наследяват готов конструктор .new()
.
Той може да бъде използуван за създаване на обекти, като му се подават параметри.
На готовия конструктор могат да се подават само именувани параметри.
В предишния пример, ще видите, че всички параметри, подадени на
.new()
, са именувани.
-
name => 'John'
-
age => 23
Какво, ако не искаме да подаваме името на всеки атрибут, когато създаваме обект?
Тогава трябва да създадем конструктор, който приема позиционни параметри.
class Human {
has $.name;
has $.age;
has $.sex;
has $.nationality;
# нов конструктор, който презаписва подразбиращия се.
method new ($name,$age,$sex,$nationality) {
self.bless(:$name,:$age,:$sex,:$nationality);
}
}
my $john = Human.new('John',23,'M','American');
say $john;
9.4. Методи
9.4.1. Въведение
Методите са подпрограмите на обекта.
Както подпрограмите, те са средство за събиране на функционалност на едно
място и именуването ѝ. Те приемат параметри, имат сигнатура и могат да
бъдат създадени като multi.
Методите се създават с помощта на ключовата дума method
.
В общия случай методите се създават, за да извършват някакви действия върху атрибутите на обекта.
Това спомага за капсулирането. Атрибутите могат да бъдат променяни само в обекта, чрез използуване на методи.
Външният свят може да достъпва само методите и няма достъп до атрибутите.
class Human {
has $.name;
has $.age;
has $.sex;
has $.nationality;
has $.eligible;
method assess-eligibility {
if self.age < 21 {
$!eligible = 'No'
} else {
$!eligible = 'Yes'
}
}
}
my $john = Human.new(name => 'John', age => 23, sex => 'M', nationality => 'American');
$john.assess-eligibility;
say $john.eligible;
След като създадем методите в даден клас, те могат да бъдат извиквани върху обект
на този клас чрез използуването на точка:
обект . метод, както е в горния пример: $john.assess-eligibility
Ако искаме да достъпим обекта в тялото на метода, за да извикаме друг метод,
ползуваме ключовата дума self
.
Ако искаме да достъпим член-променлива в тялото на метод, използуваме втория
знак !
, дори ако при създаването му сме ползували знака .
.
Това е така, защото .
създава атрибут с !
и автоматично създава негов аксесор.
В горния пример if self.age < 21
и if $!age < 21
правят едно и също нещо,
въпреки че чисто технически са различни:
-
self.age
извиква метода (аксесор).age
Може да бъде записан и като$.age
; -
$!age
представлява непосредствено извикване на член-променливата.
9.4.2. Частни методи
Обикновено методите могат да се извикват от външното обкръжение на класа.
Частните методи могат да се извикват само докато сме вътре в класа.
Възможен случай е метод, който извиква друг метод, за да извърши някакво
специфично действие. Методът, който взаимодейства с външния свят, е публичен,
докато този, който се извиква вътре в него, трябва да си бъде частен. Не искаме
потребителите на нашия клас да го използуват непосредствено и затова го
обявяваме като частен.
За да обявим частен метод, използуваме знака !
пред името му.
Частните методи се извикват чрез !
вместо .
method !азсъмчастен {
# тук си пишем програмния код
}
method азсъмпубличен {
self!азсъмчастен;
# правим още нещо
}
9.5. Атрибути на клас
Клас-атрибутите са такива, които принадлежат на класа, а не на обекта, създаден от него.
На тях могат да им се дават стойности при обявяването им.
Клас-атрибутите се обявяват с помощта на my
, вместо has
.
Те се извикват непосредствено върху класа, вместо върху обектите.
class Human {
has $.name;
my $.counter = 0;
method new($name) {
Human.counter++;
self.bless(:$name);
}
}
my $a = Human.new('a');
my $b = Human.new('b');
say Human.counter;
9.6. Тип на достъпа
Дотук във всички примери използувахме атрибутите само за да вземем информация за обектите.
Как да променим стойността на някой атрибут?
Трябва да го означим като променяем за-писане/за-четене чрез ключовите думи is rw
.
class Human {
has $.name;
has $.age is rw;
}
my $john = Human.new(name => 'John', age => 21);
say $john.age;
$john.age = 23;
say $john.age;
По подразбиране всички атрибути се обявяват като само за четене, но можете и
изрично да зададете is readonly
.
9.7. Наследяване
9.7.1. Въведение
Наследяването е друго понятие в обектно-ориентираното програмиране.
Когато създаваме класове, бързо установяваме, че някои атрибути и методи се повтарят в много от тях.
Трябва ли да дублираме код?
Не! Трябва да ползуваме наследяване.
Нека си представим, че искаме да създадем два класа – един за Човек и един за Служител.
Човек има два атрибута: име и възраст.
Служител има четири атрибута: име, възраст, компания и заплата.
Изкушени сте да създадете класовете така:
class Human {
has $.name;
has $.age;
}
class Employee {
has $.name;
has $.age;
has $.company;
has $.salary;
}
Въпреки че технически погледнато това е правилен код, той е беден като замисъл.
По-добър би бил следния вариант:
class Human {
has $.name;
has $.age;
}
class Employee is Human {
has $.company;
has $.salary;
}
Ключовата дума is
(3 л. ед. ч. на глагола "съм" – бел. прев.) определя наследяването.
На обектно-ориентиран жаргон казваме, че Employee е дъщерен клас на Human, и
че Human е родителски за Employee.
Всички дъщерни класове наследяват атрибутите и методите на родителския клас, така че няма нужда да ги създаваме наново.
9.7.2. Презаписване
Класовете наследяват всички атрибути и методи от родителските класове. Има
случаи обаче, когато искаме някой метод в дъщерния клас да има различно от наследеното
поведение. За да постигнем това, ние го създаваме наново в
дъщерния клас.
Това се нарича презаписване (overriding).
В примера долу методът introduce-yourself
е наследен от класа Employee.
class Human {
has $.name;
has $.age;
method introduce-yourself {
say 'Hi I am a human being, my name is ' ~ self.name;
}
}
class Employee is Human {
has $.company;
has $.salary;
}
my $john = Human.new(name =>'John', age => 23,);
my $jane = Employee.new(name =>'Jane', age => 25, company => 'Acme', salary => 4000);
$john.introduce-yourself;
$jane.introduce-yourself;
Презаписването се прави така:
class Human {
has $.name;
has $.age;
method introduce-yourself {
say 'Hi I am a human being, my name is ' ~ self.name;
}
}
class Employee is Human {
has $.company;
has $.salary;
method introduce-yourself {
say 'Hi I am a employee, my name is ' ~ self.name ~ ' and I work at: ' ~ self.company;
}
}
my $john = Human.new(name =>'John',age => 23,);
my $jane = Employee.new(name =>'Jane',age => 25,company => 'Acme',salary => 4000);
$john.introduce-yourself;
$jane.introduce-yourself;
В зависимост от това, от кой клас е създаден обектът, ще бъде изпълнен съответният метод.
9.7.3. Подметоди
Подметодите са такива методи, които не се наследяват от дъщерните класове.
Те са достъпни само в класа, в който са създадени.
Те се създават с помощта на ключовата дума submethod
.
9.8. Множествено наследяване
Пърл 6 поддържа множествено наследяване. Един клас може да наследява множество класове.
class bar-chart {
has Int @.bar-values;
method plot {
say @.bar-values;
}
}
class line-chart {
has Int @.line-values;
method plot {
say @.line-values;
}
}
class combo-chart is bar-chart is line-chart {
}
my $actual-sales = bar-chart.new(bar-values => [10,9,11,8,7,10]);
my $forecast-sales = line-chart.new(line-values => [9,8,10,7,6,9]);
my $actual-vs-forecast = combo-chart.new(bar-values => [10,9,11,8,7,10],
line-values => [9,8,10,7,6,9]);
say "Actual sales:";
$actual-sales.plot;
say "Forecast sales:";
$forecast-sales.plot;
say "Actual vs Forecast:";
$actual-vs-forecast.plot;
Изход
Actual sales:
[10 9 11 8 7 10]
Forecast sales:
[9 8 10 7 6 9]
Actual vs Forecast:
[10 9 11 8 7 10]
Класът combo-chart
ще съдържа два списъка със стойности – един за текущите
стойности, изобразени с колонки, и един за прогнозните стойности, изобразени с
линия.
Ето защо го създадохме като дъщерен клас на line-chart
и bar-chart
.
Сигурно забелязахте, че извикването на метода plot
върху combo-chart
не върна искания резултат.
Бе изобразен само един списък.
Защо се случи това?
combo-chart
наследява едновременно от line-chart
и bar-chart
. И двата
класа имат метод plot
. Когато извикаме този метод върху combo-chart
, Пърл 6
ще разреши противоречието, като избере един от наследените методи.
За да получим желаното поведение, трябва да презапишем метода plot
в combo-chart
.
class bar-chart {
has Int @.bar-values;
method plot {
say @.bar-values;
}
}
class line-chart {
has Int @.line-values;
method plot {
say @.line-values;
}
}
class combo-chart is bar-chart is line-chart {
method plot {
say @.bar-values;
say @.line-values;
}
}
my $actual-sales = bar-chart.new(bar-values => [10,9,11,8,7,10]);
my $forecast-sales = line-chart.new(line-values => [9,8,10,7,6,9]);
my $actual-vs-forecast = combo-chart.new(bar-values => [10,9,11,8,7,10],
line-values => [9,8,10,7,6,9]);
say "Actual sales:";
$actual-sales.plot;
say "Forecast sales:";
$forecast-sales.plot;
say "Actual vs Forecast:";
$actual-vs-forecast.plot;
Изход
Actual sales:
[10 9 11 8 7 10]
Forecast sales:
[9 8 10 7 6 9]
Actual vs Forecast:
[10 9 11 8 7 10]
[9 8 10 7 6 9]
9.9. Роли
Ролите са донякъде подобни на класовете, понеже също се състоят от методи и атрибути.
Ролите се обявяват с помощта на ключовата дума role
. Класовете, които искат
да осъществят (имплементират) една роля, трябва да го направят, като използуват
ключовата дума does
(3 л. ед. ч. на глагола правя – бел. прев.).
role bar-chart {
has Int @.bar-values;
method plot {
say @.bar-values;
}
}
role line-chart {
has Int @.line-values;
method plot {
say @.line-values;
}
}
class combo-chart does bar-chart does line-chart {
method plot {
say @.bar-values;
say @.line-values;
}
}
my $actual-sales = bar-chart.new(bar-values => [10,9,11,8,7,10]);
my $forecast-sales = line-chart.new(line-values => [9,8,10,7,6,9]);
my $actual-vs-forecast = combo-chart.new(bar-values => [10,9,11,8,7,10],
line-values => [9,8,10,7,6,9]);
say "Actual sales:";
$actual-sales.plot;
say "Forecast sales:";
$forecast-sales.plot;
say "Actual vs Forecast:";
$actual-vs-forecast.plot;
Пуснете скрипта и ще видите, че изходът е същият като от предишния скрипт.
И сега се питате: Ако ролите се държат като класове, каква полза от тях?
За да си отговорите на този въпрос, променете първия скрипт, в който показахме
множественото наследяване. Този, в който забравихме да презапишем метода plot
.
role bar-chart {
has Int @.bar-values;
method plot {
say @.bar-values;
}
}
role line-chart {
has Int @.line-values;
method plot {
say @.line-values;
}
}
class combo-chart does bar-chart does line-chart {
}
my $actual-sales = bar-chart.new(bar-values => [10,9,11,8,7,10]);
my $forecast-sales = line-chart.new(line-values => [9,8,10,7,6,9]);
my $actual-vs-forecast = combo-chart.new(bar-values => [10,9,11,8,7,10],
line-values => [9,8,10,7,6,9]);
say "Actual sales:";
$actual-sales.plot;
say "Forecast sales:";
$forecast-sales.plot;
say "Actual vs Forecast:";
$actual-vs-forecast.plot;
Изход
===SORRY!===
Method 'plot' must be resolved by class combo-chart because it exists in multiple roles (line-chart, bar-chart)
(Методът плот трябва да бъде "разрешен" в класа combo-chart, защото съществува
в повече от една роля (line-chart, bar-chart))
Ако множество роли са приложени на един и същи клас и се появи противоречие, по време на компилиране ще бъде хвърлена грешка. Това е много по-сигурен подход, в сравнение с множественото наследяване, където такива противоречия не се смятат за грешка и биват разрешавани автоматично по време на изпълнение.
Ролите ви предупреждават, че има противоречие.
9.10. Самонаблюдение
Самонаблюдение (Introspection) е действието, при което вземаме информация за свойствата на един обект. Такива са неговите атрибути, методи или тип.
class Human {
has Str $.name;
has Int $.age;
method introduce-yourself {
say 'Hi I am a human being, my name is ' ~ self.name;
}
}
class Employee is Human {
has Str $.company;
has Int $.salary;
method introduce-yourself {
say 'Hi I am a employee, my name is ' ~ self.name ~ ' and I work at: ' ~ self.company;
}
}
my $john = Human.new(name =>'John',age => 23,);
my $jane = Employee.new(name =>'Jane',age => 25,company => 'Acme',salary => 4000);
say $john.WHAT;
say $jane.WHAT;
say $john.^attributes;
say $jane.^attributes;
say $john.^methods;
say $jane.^methods;
say $jane.^parents;
if $jane ~~ Human {say 'Jane is a Human'};
Разполагаме със следните средства за самонаблюдение:
-
.WHAT
– връща класа, от който е създаден обектът. -
.^attributes
– връща списък с всички атрибути на обекта. -
.^methods
– връща всички методи, които могат да бъдат извикани върху обекта. -
.^parents
– връща всички родителски класове на обекта. -
~~
се нарича оператор за умни съвпадения. Той връща Истина (True), ако обектът е създаден от класа, с който е сравняван или го наследява.
За да научите повече за обектно-ориентираното програмиране в Пърл 6, вижте: |
10. Обработка на Изключения
10.1. Прихващане на Изключения
Изключенията (Exceptions) представляват специално поведение, което се случва по време на изпълнение, когато нещо се обърка.
Казваме, че програмата ни хвърля изключение.
Да погледнем следния скрипт. Той работи както трябва.
my Str $name;
$name = "Joanna";
say "Hello " ~ $name;
say "How are you doing today?"
Изход
Hello Joanna
How are you doing today?
Сега да видим този скрипт. Той хвърля изключение.
my Str $name;
$name = 123;
say "Hello " ~ $name;
say "How are you doing today?"
Изход
Type check failed in assignment to $name; expected Str but got Int
in block <unit> at exceptions.pl6:2
(Проверката за типа на $name е неуспешна; очакваше се Str, но се оказа Int в
блок <unit> в exceptions.pl6:2)
Сигурно вече сте забелязали, че при грешка (в този случай присвояване на цяло число на променлива с тип Str) програмата винаги спира и следващите редове не се изпълняват, дори да са правилно написани.
Обработка на изключението е действието, при което прихващаме изключение, което е било хвърлено, за да продължи работата на програмата ни.
my Str $name;
try {
$name = 123;
say "Hello " ~ $name;
CATCH {
default {
say "Can you tell us your name again, we couldn't find it in the register.";
}
}
}
say "How are you doing today?";
Изход
Can you tell us your name again, we couldn't find it in the register.
How are you doing today?
Обработката на изключението се извършва с помощта на блока try-catch
(пробвай-хвани).
try {
# тук пишете кода
# ако нещо се обърка, скриптът ще влезе в блока CATCH долу
# ако всичко е наред, блокът CATCH ще бъде пренебрегнат
CATCH {
default {
# кодът, който се намира тук, ще бъде изпълнен само ако е хвърлено изключение
}
}
}
Блокът, в който прихващаме изключението (CATCH
), може да бъде обявен по същия
начин както given
. Това означава, че можем да прихващаме и обработваме по
различен начин много типове изключения.
try {
# тук пишете кода
# ако нещо се обърка, скриптът ще влезе в блока CATCH долу
# ако всичко е наред, блокът CATCH ще бъде пренебрегнат
CATCH {
when X::AdHoc { # да се направи нещо, в случай че е хвърлено изключение от тип X::AdHoc }
when X::IO { # да се направи нещо, в случай че е хвърлено изключение от тип X::IO }
when X::OS { # да се направи нещо, в случай че е хвърлено изключение от тип X::OS }
default { # да се направи нещо, в случай че е хвърлено изключение от друг тип }
}
}
10.2. Хвърляне на Изключения
Пърл 6 ви дава възможност и изрично да хвърляте изключения. Могат да бъдат хвърляни два типа изключения:
-
случайни изключения
-
типови изключения
my Int $age = 21;
die "Error !";
my Int $age = 21;
X::AdHoc.new(payload => 'Error !').throw;
Случайните изключения се хвърлят, като се използува вградената функция die
,
последвана от обяснително съобщение за грешката.
Типовите изключения са обекти. Това обяснява и използуването на конструктора
.new()
в горния пример.
Основният клас на всички типови изключения е X
. Ето няколко примера:
X::AdHoc
е най-простият тип изключение
X::IO
се използува за входно-изходни грешки
X::OS
се използува за системни грешки
X::Str::Numeric
бива хвърляно при неуспешни опити за превръщане на низове в числа
За да видите пълен списък с типовете изключения и свързаните методи, идете на https://docs.perl6.org/type-exceptions.html |
11. Изрази за съвпадения
Изразът за съвпадение или просто съвпадение (regular expression, regex) е
последователност от знаци за намиране на съвпадение в текст.
Най-лесният начин за разбирането на тези изрази е да мислите за тях като за шаблони.
Позволявам си да преведа по нов начин наложилото се, но не носещо никакъв смисъл понятие "регулярни изрази" (бел. прев.) |
if 'просветление' ~~ m/ свет / {
say 'Просветление съдържа корена "свет".';
}
В този пример проверяваме с помощта на оператора за умни съвпадения ~~
дали
една дума съдържа корена "свет".
В думата "просветление" се търси съвпадение със "свет" m/ свет /
11.1. Обявяване на шаблон
Шаблонът за търсене на съвпадение може да бъде обявен както следва:
-
/свет/
-
m/свет/
-
rx/light/
Празното пространство няма значение, освен ако не е указано нещо друго. m/light/
и m/ light /
са едно и също нещо.
11.2. Намиране съвпадения на знаци
Буквено-цифровите знаци и знакът за подчертаване _
се пишат по обичайния начин. Всички
други символи трябва да се избягват с обратно наклонена черта или да се
ограждат с кавички.
if 'Температура: 13' ~~ m/ \: / {
say "Предоставеният низ съдържа двоеточие : ";
}
if 'Age = 13' ~~ m/ '=' / {
say "Низът съдържа знака за равенство = ";
}
if 'name@company.com' ~~ m/ "@" / {
say "Това е валиден адрес за електронна поща, защото съдържа знака @."
}
11.3. Намиране на знаци по категории
Знаците могат да бъдат групирани в категории, а ние можем да търсим съвпадение по тях. Можем също така да търсим по обратното значение на категорията (всичко друго освен нея).
Категория |
Израз |
Обратно значение |
Израз |
Знак за дума (буква, цифра или знак за подчертаване) |
\w |
Всеки друг знак освен знака за дума |
\W |
Цифра |
\d |
Всеки знак, който не е цифра |
\D |
Празно пространство |
\s |
Всеки знак, който не е празно пространство |
\S |
Водоравно празно пространство |
\h |
Всеки знак, който не е водоравно празно пространство |
\H |
Отвесно празно пространство |
\v |
Всеки знак, който не е отвесно празно пространство |
\V |
Табулация |
\t |
Всеки знак, който не е табулация |
\T |
Нов ред |
\n |
Всеки знак, без нов ред |
\N |
if "Иван123" ~~ / \d / {
say "Това не е име. Имената не съдържат числа.";
} else {
say "Това е име."
}
if "Иван-Данов" ~~ / \s / {
say "Този низ съдържа празно пространство.";
} else {
say "Този низ не съдържа празно пространство.";
}
11.4. Уникод-свойства
Да се намира съвпадение чрез категории от знаци е удобно. Въпреки това,
по-систематичен подход би бил да се използуват уникод-свойства.
Уникод-свойствата са оградени с <: >
.
if "John123" ~~ / <:N> / {
say "Contains a number";
} else {
say "Doesn't contain a number"
}
if "John-Doe" ~~ / <:Lu> / {
say "Contains an uppercase letter";
} else {
say "Doesn't contain an upper case letter"
}
if "John-Doe" ~~ / <:Pd> / {
say "Contains a dash";
} else {
say "Doesn't contain a dash"
}
11.5. Заместващи знаци
При търсене на съвпадения могат да се ползуват и заместващи знаци.
Точката .
дава съвпадение с всякакъв знак.
if 'abc' ~~ m/ a.c / {
say "Match";
}
if 'a2c' ~~ m/ a.c / {
say "Match";
}
if 'ac' ~~ m/ a.c / {
say "Match";
} else {
say "No Match";
}
11.6. Количествени указатели
Количествените указатели се поставят след знак и указват колко пъти се очаква появата на знака в текста.
Въпросителният знак ?
означава нула или един път.
if 'ac' ~~ m/ a?c / {
say "Match";
} else {
say "No Match";
}
if 'c' ~~ m/ a?c / {
say "Match";
} else {
say "No Match";
}
Звездата *
означава нула или повече пъти.
if 'az' ~~ m/ a*z / {
say "Match";
} else {
say "No Match";
}
if 'aaz' ~~ m/ a*z / {
say "Match";
} else {
say "No Match";
}
if 'aaaaaaaaaaz' ~~ m/ a*z / {
say "Match";
} else {
say "No Match";
}
if 'z' ~~ m/ a*z / {
say "Match";
} else {
say "No Match";
}
+
означава поне веднъж.
if 'az' ~~ m/ a+z / {
say "Match";
} else {
say "No Match";
}
if 'aaz' ~~ m/ a+z / {
say "Match";
} else {
say "No Match";
}
if 'aaaaaaaaaaz' ~~ m/ a+z / {
say "Match";
} else {
say "No Match";
}
if 'z' ~~ m/ a+z / {
say "Match";
} else {
say "No Match";
}
11.7. Резултати от съвпадението
Когато се намери съвпадение на търсенето в някакъв текст, резултатът се
съхранява в специалната променлива $/
.
if 'Rakudo is a Perl 6 compiler' ~~ m/:s Perl 6/ {
say "The match is: " ~ $/;
say "The string before the match is: " ~ $/.prematch;
say "The string after the match is: " ~ $/.postmatch;
say "The matching string starts at position: " ~ $/.from;
say "The matching string ends at position: " ~ $/.to;
}
The match is: Perl 6
The string before the match is: Rakudo is a
The string after the match is: compiler
The matching string starts at position: 12
The matching string ends at position: 18
$/
връща Обект на Съвпадението (низът, който съответства на търсения шаблон).
Следните методи могат да се извикат върху Обекта на съвпадението:
.prematch
връща низа преди съвпадението.
.postmatch
връща низа, следващ съвпадението.
.from
връща мястото в низа (цяло число), където съвпадението започва.
.to
връща мястото в низа (цяло число), където съвпадението свършва.
По подразбиране празното пространство (в шаблон за намиране на съвпадение)
няма значение. Ако искаме да намерим съвпадение по шаблон, съдържащ празно
пространство, трябва да го укажем изрично. Като поставим :s в шаблона m/:s
Perl 6/ указваме празните пространства да се приемат буквално и да не се
премахват при компилиране на шаблона. Иначе можехме да запишем израза като
m/ Perl\s6 / и да ползуваме \s , което видяхме по-рано като заместител за празно
пространство. Ако израз за съвпадение съдържа повече от едно празно
пространство, с използуването на :s се оказваме по-ефективни, отколкото ако
използуваме \s за всяко празно пространство.
|
11.8. Пример
Нека проверим дали един адрес за електронна поща е валиден.
За целите на примера ще приемем, че адресът се състои от:
име [точка] фамилия [при] фирма [точка] (com/org/net)
Изразът в този пример за проверка на адреса не е много точен. Единствената му цел е да покаже възможностите в Пърл 6. Не го ползвайте за производствени цели. |
my $email = 'john.doe@perl6.org';
my $regex = / <:L>+\.<:L>+\@<:L+:N>+\.<:L>+ /;
if $email ~~ $regex {
say $/ ~ " is a valid email";
} else {
say "This is not a valid email";
}
john.doe@perl6.org is a valid email
<:L>
съвпада с отделна буква
<:L>` съвпада с една или повече букви +
`\.` съвпада с един знак [точка] +
`\@` съвпада с един знак [при] +
`<:L:N>
съвпада с низ, състоящ се от една или повече букви и число в края
<:L+:N>+
съвпада с низ, състоящ се от един или повече знаци (букви и числа)
Изразът може да бъде разложен както следва:
-
име
<:L>+
-
[точка]
\.
-
фамилия
<:L>+
-
[при]
\@
-
име на фирма
<:L+:N>+
-
[точка]
\.
-
com/org/net
<:L>+
my $email = 'john.doe@perl6.org';
my regex many-letters { <:L>+ };
my regex dot { \. };
my regex at { \@ };
my regex many-letters-numbers { <:L+:N>+ };
if $email ~~ / <many-letters> <dot> <many-letters> <at> <many-letters-numbers> <dot> <many-letters> / {
say $/ ~ " is a valid email";
} else {
say "This is not a valid email";
}
Синтаксисът за обявяване на именуван израз за съвпадение е: my regex regex-name { regex definition }
Синтаксисът за извикване на именуван израз за съвпадение е: <regex-name>
За повече информация относно изразите за съвпадение, вижте https://docs.perl6.org/language/regexes |
12. Модули в Пърл 6
Пърл 6 е език с общо предназначение. Той е подходящ за всякакви задачи: обработка на текст, графика, уеб, бази данни, мрежови протоколи и т.н.
Многократното използване на код е ключово понятие. Програмистите не трябва да преоткриват колелото с всяка нова задача.
Пърл 6 ни дава възможност да създаваме и разпространяваме модули. Всеки модул представлява пакетиран набор от функционалност, която може да бъде инсталирана и използвана.
Зеф (Zef) е средство за управление на модули, което се разпространява с Rakudo Star.
За да инсталирате отделен модул, напишете следната команда в терминал:
zef install "module name"
Модулите за Пърл 6 се намират на адрес: https://modules.perl6.org/ |
12.1. Използване на модули
MD5 е хеш-функция от криптографията, която връща 128-битова стойност.
MD5 има много приложения, едно от които е криптиране на пароли, съхранявани в
бази от данни. Когато се регистрира нов потребител, неговите данни не се
записват като обикновен текст, а се хешират. Причината е, че ако до данните се
осъществи неразрешен достъп, паролите няма да бъдат четими.
За наше щастие вече има модул в Пърл 6, който имплементира алгоритъма MD5. Да
го инсталираме:
zef install Digest::MD5
Сега пуснете скрипта по-долу:
use Digest::MD5;
my $password = "password123";
my $hashed-password = Digest::MD5.new.md5_hex($password);
say $hashed-password;
За да извикаме функцията md5_hex()
, трябва да заредим модула, който я предоставя.
Ключовата дума use
зарежда модула в скрипта.
В действителност превръщането на паролата в MD5-сума е недостатъчно, тъй като е податливо на т.нар. речникови атаки. + То трябва да бъде съчетано със "сол" (произволно генерирана стойност – сол към паролата; Бел. ред.) https://en.wikipedia.org/wiki/Salt_(cryptography). |
13. Уникод
Уникод е стандарт за кодиране и представяне на текст на почти всички писмени
системи в света.
UTF-8 е таблица със знаци, която може да кодира всички възможни знаци от Уникод.
Знаците се състоят от:
Графема: Видимо представяне – как изглежда.
Точка на кода: Число (пореден номер), присвоено на знака.
13.1. Използване на Уникод
say "Б";
say "\x0411";
say "\c[CYRILLIC CAPITAL LETTER BE]";
Трите реда горе показват различни начини за изграждане на един знак:
-
Като просто напишем знака (графема)
-
Чрез въвеждане на
\x
, последван от точката на кода (в шестнадесетична бройна система) -
Чрез въвеждане на
\c
, последван от името на точката на кода
say "☺";
say "\x263a";
say "\c[WHITE SMILING FACE]";
say "á";
say "\x00e1";
say "\x0061\x0301";
say "\c[LATIN SMALL LETTER A WITH ACUTE]";
Буквата á
може да се напише:
-
като използваме нейната единствена кодова точка
\x00e1
-
или като съчетание от две кодови точки –
a
и ляво ударение\x0061\x0301
say "á".NFC;
say "á".NFD;
say "á".uniname;
Изход
NFC:0x<00e1>
NFD:0x<0061 0301>
LATIN SMALL LETTER A WITH ACUTE
NFC
връща точката на кода.
NFD
разглобява знака и връща кодовата точка на всяка негова част.
uniname
връща името на кодовата точка.
my $Δ = 1;
$Δ++;
say $Δ;
my $var = 2 + ⅒;
say $var;
14. Успоредност, Съгласуваност и Неедновременност
14.1. Успоредност (Parallelism)
В общия случай всички задачи в една програма се изпълняват последователно. Това може да си е съвсем наред, освен ако не отнема много време.
За щастие Пърл 6 позволява да се изпълняват няколко задачи едновременно. Тук е важно да споменем, че успоредност може да означава едно от следните две неща:
-
Успоредност на задачите: Два (или повече) независими израза се изпълняват едновременно.
-
Успоредност на данните: Един израз се изпълнява едновременно върху списък от елементи.
Да започнем с второто.
14.1.1. Успоредност на Данните
my @array = (0..50000); # Създаваме елементи в масива
my @result = @array.map({ is-prime $_ }); # Извикваме is-prime върху всеки елемент
say now - INIT now; # Показваме времето, което е отнела работата на скрипта
Извършваме само едно действие @array.map({ is-prime $_ })
Подпрограмата is-prime
се извиква последователно върху всеки елемент от масива:
is-prime @array[0]
, след това is-prime @array[1]
, после is-prime @array[2]
и т. н.
is-prime
за множество елементи по едно и също време (успоредно):my @array = (0..50000); #Създаваме елементи в масива
my @result = @array.race.map({ is-prime $_ }); #Извикваме is-prime върху всеки елемент
say now - INIT now; #Показваме времето, което е отнела работата на скрипта
Обърнете внимание на race
в израза.
Този метод се грижи за едновременната (успоредна) обработка на елементите от масива.
След като изпълните двата примера ( със и без race
), сравнете времената им.
race
hyper
Ако изпълните двата примера, ще забележите, че елементите в изхода на втория са подредени, а в първия не са. |
14.1.2. Успоредност на задачите
my @array1 = (0..49999);
my @array2 = (2..50001);
my @result1 = @array1.map( {is-prime($_ + 1)} );
my @result2 = @array2.map( {is-prime($_ - 1)} );
say @result1 eqv @result2;
say now - INIT now;
-
Създадохме два масива;
-
Приложихме различно действие върху всеки от тях и съхранихме изхода;
-
Накрая проверихме дали изходите са еднакви.
Скриптът изчаква @array1.map( {is-prime($_ + 1)} )
да завърши
и след това изчислява @array2.map( {is-prime($_ - 1)} )
Двете действия, приложени на всеки от масивите, не зависят едно от друго.
my @array1 = (0..49999);
my @array2 = (2..50001);
my $promise1 = start @array1.map( {is-prime($_ + 1)} ).eager;
my $promise2 = start @array2.map( {is-prime($_ - 1)} ).eager;
my @result1 = await $promise1;
my @result2 = await $promise2;
say @result1 eqv @result2;
say now - INIT now;
Функцията start
изпълнява кода и връща обект от тип обещание (promise) или просто обещание.
Ако програмният код се изпълни без грешка, обещанието ще бъде спазено.
Ако кодът хвърли изключение, обещанието ще бъде нарушено.
Функцията await
(чакай) чака обещание.
Ако то е спазено, тя ще вземе върнатите стойности.
Ако е нарушено, await
ще получи хвърленото изключение.
Проверете времето за изпълнение на всеки скрипт.
Успоредността на изпълнението винаги забавя скрипта, за да създаде
нишките, в които се изпълняват задачите. Ако това забавяне не се компенсира при
едновременното им изпълнение, скриптът ще изглежда по-бавен. Ето защо,
използването на race , hyper , start и await за много прости скриптове,
може всъщност да ги забави.
|
14.2. Съгласуваност и Неедновременност (Concurrency and Asynchrony)
За повече информация относно програмиране при съгласуване на задачите и управление на неедновременни задачи, вижте https://docs.perl6.org/language/concurrency |
15. Общността
-
#perl6 IRC канал. Повечето обсъждания се случват в IRC. Там трябва да ходите по всякакви въпроси: https://perl6.org/community/irc
-
p6weekly седмичен преглед на промените в Пърл 6 и новините около него.
-
pl6anet блог агрегатор. Следете статиите, свързани с Пърл 6.
-
/r/perl6 Запишете се в канала, посветен на Пърл 6.