= Увод в Perl 6 Naoum Hankache ; Красимир Беров :description: Общо въведение в Perl 6 :keywords: perl6, perl 6, въведение, perl6intro, въведение в perl 6, урок по пърл 6, увод в perl 6 :Revision: 1.0 :icons: font :source-highlighter: pygments //:pygments-style: manni :source-language: perl6 :pygments-linenums-mode: table :toc: left :toc-title: Съдържание :doctype: book :lang: bg Предназначението на това писание е да ви въведе набързо в езика за програмиране Perl 6. Ако за първи път се сблъсквате с езика, то ще ви даде начален тласък в използуването и изучаването му. В някои от разделите ще намерите препратки към други, по-пълни и точни части от http://docs.perl6.org[документацията на Perl 6]. Така ще можете да четете направо от източника, в случай че се нуждаете от повече информация по определена тема. В този урок ще намерите примери по най-обсъжданите теми. За да ги разберете по-добре, отделяйте време да правите всеки от примерите. .Лиценз Тази работа е лицензирана под "Признание - Споделяне на Споделеното 4.0 Международен Лиценз". За да видите лиценза, посетете * https://creativecommons.org/licenses/by-sa/4.0/. .Принос Ако желаете да допринесете за подобряването на това писание, отидете на * https://github.com/hankache/perl6intro .Отзиви Всякакви отзиви са добре дошли: * naoum@hankache.com * berov@cpan.org Ако тази работа ви харесва, отбележете хранилището със звезда на следния адрес: link:https://github.com/hankache/perl6intro[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 :sectnums: == Увод === Какво е Perl 6 Perl 6 е език от високо ниво, с общо предназначение и постепенна типизация на променливите. Perl 6 е многопарадигмен. Той поддържа процедурно, обектно-ориентирано и функционално програмиране. .Девизи на Perl 6: * ИННН Има няколко начина да се направи. TMTOWTDI (Произнася се Tim Toady): There is more than one way to do it. * Лесните неща трябва да са лесни, трудните да стават по-лесни, а невъзможните - трудни. === Понятия * *Perl 6*: Това е спецификация за език за програмиране с набор от тестове. Реализации, които изпълняват тестовете без грешка, могат да се нарекат Perl 6. * *Rakudo*: Е компилатор за Пърл 6. * *Rakudobrew*: Е програма за управление инсталациите на Ракудо. * *Zef*: Е инсталатор на модули за Пърл 6. * *Rakudo Star*: Е вързоп: Ракудо, Зеф, набор от модули за Пърл 6 и документация. === Инсталиране на Пърл 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 .OSX Имате четири възможности: * Следвайте същите стъпки като в Линукс * Инсталирайте с хоумбрю: `brew install rakudo-star` * Инсталирайте с МакПортс: `sudo port install rakudo` * Свалете най-новия инсталатор (файл с разширение .dmg) от http://rakudo.org/downloads/star/ .Windows . Свалете най-новия инсталатор (файл с разширение .msi) от http://rakudo.org/downloads/star/ Ако архитектурата на системата ви е 32-битова, свалете файла с x86 в името; ако е 64-битова, свалете файла съдържащ x86_64 в името. . След инсталацията се уверете, че `C:\rakudo\bin` се намира в системната променлива PATH. .Docker . Вземете официалното изображение за Docker `docker pull rakudo-star` . След това стартирайте контейнер с изображението `docker run -it rakudo-star` === Изпълнение на код, написан на 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]. [TIP] -- Rakudo Star върви с едноредов редактор, който се използува в интерактивната конзола (REPL). Ако сте инсталирали обикновен Rakudo, вместо Rakudo Star, най-вероятно интерактивната конзола не ви дава възможност да редактирате текущия ред, да ползвате стрелка нагоре и надолу (за да извиквате предишни команди и да ги променяте) или да ползвате табулация (клавишът TAB) за допълване на частично въведени низове. Изпълнете една от следните команда и сте готови. * `zef install Linenoise` ще работи в Windows, Linux и OSX * `zef install Readline` - ако сте на Линукс и предпочитате библиотеката _Readline_ -- === Редактори Тъй като през повечето време ще записваме програмите си във файлове, ни е нужен приличен текстов редактор, който разпознава синтаксиса на Пърл 6. Аз лично използувам и препоръчвам https://atom.io/[Atom]. Това е модерен редактор и поддържа синтаксиса на Пърл 6. https://atom.io/packages/language-perl6fe[Perl6 FE] е допълнителен пакет за оцветяване на кода на Пърл 6 за Атом. Той произхожда от оригиналния пакет, който идва с Атом, но съдържа много подобрения и поправени грешки. Други членове на общността използуват също http://www.vim.org/[Vim], https://www.gnu.org/software/emacs/[Emacs] или http://padre.perlide.org/[Padre]. По-новите версии на Vim идват по подразбиране с поддръжка на синтаксиса на Пърл 6. Emacs и Padre изискват инсталиране на допълнителни пакети. === Здравей, Свят! Ще започнем с ритуала `hello world`. [source,perl6] say 'Здравей, Свят!'; Това може да бъде написано и така: [source,perl6] 'Здравей, Свят!'.say; === Преглед на синтаксиса Пърл 6 е *свободна форма*: Свободни сте (през повечето време) да използувате колкото ви е угодно празни пространства (за разлика от Питон - бел. прев.). *Твърденията* са обикновено логически ред код. Те завършват с точка и запетая. + `say "Здрасти" if True;` *Изразите* са специален тип твърдение, което връща стойност: `1+2` ще върне `3` Изразите се състоят от *Членове* и *Оператори*. *Членове*. Те са: * *Променливи*: Съдържат стойност, която може да бъде променяна. * *Буквални стойности (литерали)*: Непроменяема, буквална стойност - число или низ. *Оператори*. Те са няколко типа: |=== | *Тип* | *Обяснение* | *Пример* | Представка | Преди члена. | `++1` | Вставка | Между членовете | `1+2` | Наставка | След члена | `1++` | Ограждащ | Около члена | `(1)` | Ограждаща наставка | След един член и ограждащ друг член | `Array[1]` |=== ==== Идентификатори (Имена) Идентификаторите представляват имена, дадени на членовете. .Правила: * Трябва да започват с буква или знак за подчертавка. * Могат да съдържат числа (ако не са първия знак в името на променливата). * Могат да съдържат тирета или апострофи (ако не са първи или последен знак). От дясната страна на тирето или апострофа винаги трябва да има буква. |=== | *Правилно* | *Неправилно* | `var1` | `1var` | `var-one` | `var-1` | `var'one` | `var'1` | `var1_` | `var1'` | `_var` | `-var` |=== .Честo използувани начини за именуване (конвенции): * КамилоОбразно: `variableNo1` * шиш-кебап: `variable-no1` * змие_видно: `variable_no1` Можете да именувате променливите си както искате, но е добра практика да се спрете на един вариант и да го следвате. Като използувате смислени имена, ще улесните живота на всички - и вашият, и на вашите колеги. * `var1 = var2 * var3` е правилно синтактично, но безсмислено. * `monthly-salary = daily-rate * working-days` тези са по-смислени имена за променливи. ==== Коментари Коментарът е текст, който се пропуска от компилатора, и се ползва като бележка или пояснение. Коментарите са три типа: * Едноредови: + [source,perl6] # Това е едноредов коментар * Вложен/вмъкнат: + [source,perl6] say #`(Това е вмъкнат коментар) "Hello World." * Многоредови: + [source,perl6] ----------------------------- =begin comment Това е многоредов коментар. Първа бележка Второ пояснение =end comment ----------------------------- ==== Кавички Низовете се ограждат с двойни или единични кавички. Използувайте двойни кавички, когато: * низът ви съдържа апостроф; * низът ви съдържа променливи. [source,perl6] ----------------------------------- 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 ----------------------------------- == Оператори === Често използувани оператори Следващата таблица изрежда най-често използуваните оператори. [cols="^.^5m,^.^5m,.^20,.^20m,.^20m", options="header"] |=== | Оператор | Тип | Описание | Пример | Резултат | + | Вставка | Събиране | 1 + 2 | 3 | - | Вставка | Изваждане | 3 - 1 | 2 | * | Вставка | Умножение | 3 * 2 | 6 | ** | Вставка | Степенуване | 3 ** 2 | 9 | / | Вставка | Деление | 3 / 2 | 1.5 | div | Вставка | Деление на цели числа (закръгля надолу) | 3 div 2 | 1 | % | Вставка | Деление до остатък | 7 % 4 | 3 .2+| %% .2+| Вставка .2+| Делимост | 6 %% 4 | False <| 6 %% 3 <| True | gcd | Вставка | Най-голям общ делител | 6 gcd 9 | 3 | lcm | Вставка | Най-малко общо кратно | 6 lcm 9 | 18 | == | Вставка | Цифрово равенство | 9 == 7 | False | != | Вставка | Цифрово неравенство | 9 != 7 | True | < | Вставка | По-малко | 9 < 7 | False | > | Вставка | По-голямо | 9 > 7 | True | \<= | Вставка | По-малко или равно | 7 \<= 7 | True | >= | Вставка | По-голямо или равно | 9 >= 7 | True | eq | Вставка | Еднаквост между низове | "John" eq "John" | True | ne | Вставка | Низовете не са еднакви | "John" ne "Jane" | True | = | Вставка | Присвояване | my $var = 7 | Присвояване на стойността `7` на променливата `$var` .2+| ~ .2+| Вставка .2+| Свързване на низове | 9 ~ 7 | 97 'London', Germany => 'Berlin'); say %capitals; ---- Някои от методите, които могат да се извикват върху хешове, са: [source,perl6] .`Скрипт` ---- 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 Paris Germany Berlin UK London) (France Germany UK) (Paris Berlin London) The capital of France is: Paris ---- .Обяснение `.push: (key \=> 'Value')` добавя нова двойка ключ/стойност. + `.kv` връща списък, съдържащ всички ключове и стойности. + `.keys` връща списък, съдържащ всички ключове. + `.values` връща списък, съдържащ всички стойности. + Можем да достъпим отделна стойност в хеша, като укажем нейния ключ `%hash` NOTE: За да видите пълния справочник за хешовете, посетете https://docs.perl6.org/type/Hash === Типове В примерите досега не задавахме типа стойност, който да съдържа променливата. TIP: `.WHAT` Ще върне типа на стойността, съдържаща се в променливата. [source,perl6] ---- my $var = 'Text'; say $var; say $var.WHAT; $var = 123; say $var; say $var.WHAT; ---- Както виждате от горния пример, типът на стойността в променливата `$var` първо беше (Str), а след това (Int). Този начин на програмиране се нарича динамично типизиране. Динамично означава, че променливите могат да съдържат стойности от *Всякакъв* (Any) тип. Сега опитайте да изпълните следния пример. Обърнете внимание на използуването на `Int` пред името на променливата. [source,perl6] ---- 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 е *постепенно типизиран*; позволява и *статично*, и *динамично* типизиране. .Масивите и хешовете също могат да бъдат статично типизирани: [source,perl6] ---- 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; ---- .Ето списък с най-често използуваните типове: Най-вероятно никога няма да използувате първите два, но са упоменати, да ги знаете. [cols="^.^1m,.^3m,.^2m,.^1m, options="header"] |=== | *Тип* | *Описание* | *Пример* | *Резултат* | Mu | Коренът на йерархията на типовете в Перл 6 | | | Any | Подразбиращият се родителски клас за нови класове и за повечето стандартни класове | | | Cool | Стойност, която може да бъде ползвана като низ и число едновременно | my Cool $var = 31; say $var.flip; say $var * 2; | 13 62 | Str | Низ от знакове | my Str $var = "NEON"; say $var.flip; | NOEN | Int | Цяло число (случайна точност) | 7 + 7 | 14 | Rat | Рационално число (ограничена точност) | 0.1 + 0.2 | 0.3 | Bool | Булева стойност (Истина или Лъжа) | !True | False |=== === Самонаблюдение Самонаблюдение (Introspection) е действието по взимане на информация за даден обект, например какъв е типът му. + В един от предишните примери използувахме `.WHAT`, за да върнем типа на променливата. [source,perl6] ---- 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`. === Видимост Преди да използувате променлива за първи път, трябва да я обявите. В Пърл 6 се използуват няколко начина за обявяване на променливи. В примерите досега използувахме `my`. [source,perl6] my $var=1; Операторът `my` дава на променливата *словна* (*lexical*) видимост. Иначе казано, променливата ще бъде видима (използваема) само в блока от код, в който е обявена. В Пърл 6 блокът представлява всичко, намиращо се между двойка отваряща и затваряща фигурни скоби - `{ }`. Ако няма определен блок, променливата е достъпна в целия скрипт. [source,perl6] ---- { my Str $var = 'Text'; say $var; # е достъпна } say $var; # е недостъпна, връща грешка ---- Тъй като променливата е видима само в блока, в който е обявена, можете да ползвате същото име за друга променлива в друг блок. [source,perl6] ---- { my Str $var = 'Text'; say $var; } my Int $var = 123; say $var; ---- === Присвояване и Обвързване В предишните примери видяхме как да *присвояваме* стойности на променливи. + *Присвояването* се прави с помощта на оператора `=`. [source,perl6] ---- my Int $var = 123; say $var; ---- Можем да променим стойността, присвоена на променлива: [source,perl6] .Присвояване ---- my Int $var = 123; say $var; $var = 999; say $var; ---- .`Изход` ---- 123 999 ---- И напротив - когато *обвързваме* стойност с променлива, не можем да променим стойността. + *Обвързването* се извършва с помощта на оператора `:=`. [source,perl6] .Обвързване ---- my Int $var := 123; say $var; $var = 999; say $var; ---- .`Изход` ---- 123 Cannot assign to an immutable value Не може да се присвои към непроменяема стойност ---- [source,perl6] .Променливите могат да бъдат обвързвани и с други променливи: ---- my $a; my $b; $b := $a; $a = 7; say $b; $b = 8; say $a; ---- .`Изход` ---- 7 8 ---- Обвързването на променливи една с друга е двупосочно. + Резултатът от `$a := $b` и `$b := $a` е един и същ. NOTE: Повече за променливите ще научите на адрес https://docs.perl6.org/language/variables == Функции и мутатори Важно е да се прави разлика между функции и мутатори. + Функциите не променят състоянието на обектите, върху които са извикани. + Мутаторите (менячи - бел. прев.) променят състоянието на обекта. [source,perl6,linenums] .`Скрипт` ---- 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 от скрипта) == Цикли и условия Пърл 6 има много изрази за условия и цикли. === if Кодът се изпълнява, само ако условието е изпълнено. Иначе казано - ако изразът се изчисли като Истина (`True`). [source,perl6] ---- my $age = 19; if $age > 18 { say 'Welcome' } ---- В Пърл 6 можем да сменим местата на условието и кода. + Макар местата да са сменени, проверката на условието винаги се изпълнява първа. [source,perl6] ---- my $age = 19; say 'Добре дошъл' if $age > 18; ---- В случай че условието не се изпълни, можем да укажем алтернативни блокове код чрез: * `else` * `elsif` [source,perl6] ---- # изпълнение на различен код при различни стойности на променливата my $брой-места = 9; if $брой-места <= 5 { say 'Аз съм седан' } elsif $брой-места <= 7 { say 'Аз съм мини-ван' } else { say 'Аз съм ван' } ---- === unless Отрицанието на твърдението, проверявано чрез `if`, може да бъде изразено чрез `unless`. Следният код: [source,perl6] ---- my $чисти-обувки = False; if not $чисти-обувки { say 'Почисти си обувките!' } ---- може да бъде написан като: [source,perl6] ---- my $чисти-обувки = False; unless $чисти-обувки { say 'Почисти си обувките!' } ---- Отрицание на дадено твърдение се постига чрез `!` или `not`. `unless (условие)` се използува вместо `if not (условие)`. `unless` не може да има съответстваща `else` клауза. === with `with` е като `if`, но проверява дали променливата има присвоена стойност. [source,perl6] ---- my Int $var=1; with $var { say 'Hello' } ---- Ако изпълните кода, без да сте присвоили стойност на променливата, нищо няма да се изведе на екрана. [source,perl6] ---- my Int $var; with $var { say 'Hello' } ---- `without` е обратното на `with`. Същото, каквото е `unless` за `if`. Ако първото `with` условие не е изпълнено, може да укажете друго условие с `orwith`. + `with` и `orwith` са подобни на `if` и `elsif`. === for Цикълът `for` повтаря действието върху множество стойности. [source,perl6] ---- my @array = [1,2,3]; for @array -> $array-item { say $array-item * 100 } ---- Забележете, че създадохме променливата `$array-item` и приложихме действието `*100` върху всеки елемент от масива. === given `given` в Пърл 6 е същото като `switch` в другите езици, но много по-мощно. [source,perl6] ---- my $var = 42; given $var { when 0..50 { say 'По-малко или равно на 50' } when Int { say "е Int" } when 42 { say 42 } default { say "к'во?" } } ---- При успешно съвпадение, процесът на търсене на удовлетворяване на условието, следващо `when`, се прекратява. Ако обаче добавите `proceed` в блока за изпълнение, процесът на търсене на съвпадение продължава. [source,perl6] ---- my $var = 42; given $var { when 0..50 { say 'По-малко или равно на 50'; proceed } when Int { say "е Int"; proceed } when 42 { say 42 } default { say "к'во?" } } ---- === loop `loop` е друг начин за писане на `for` цикъл. Всъщност `loop` е начинът, по който се пишат `for` циклите в езиците, подобни на C. Пърл 6 принадлежи към това семейство. [source,perl6] ---- loop (my $i = 0; $i < 5; $i++) { say "Текущото число е $i" } ---- NOTE: За да научите повече за циклите и условните изрази, погледнете https://docs.perl6.org/language/control == I/O В Пърл 6 най-често използуваните _входно-изходни_ интерфейси са _терминалът_ и _файловете_. === Основни действия в терминал ==== say `say` пише в стандартния изход. Добавя нов ред в края. С други думи, следният код [source,perl6] ---- say 'Hello Mam.'; say 'Hello Sir.'; ---- ще изведе текста в кавичките на два отделни реда. ==== print `print` има подобно поведение като `say`, но не добавя нов ред. Заместете `say` с `print` и сравнете изхода от действията. ==== get `get` се използува за прихващане на входни данни от терминала. [source,perl6] ---- my $name; say "Hi, what's your name?"; $name = get; say "Dear $name welcome to Perl 6"; ---- При изпълнение на горния код, терминалът ще чака да въведете името си. Въведете го и натиснете [Enter]. След това ще видите поздрава. ==== prompt `prompt` е съчетание от `print` и `get`. Горният пример може да бъде написан така: [source,perl6] ---- my $name = prompt "Hi, what's your name? "; say "Dear $name welcome to Perl 6"; ---- === Изпълнение на външни команди Две подпрограми могат да се използуват за извикване на външни команди: * `run` Изпълнява външна команда (програма) без посредничеството на системната обвивка. * `shell` Изпълнява команда през системната обвивка. Тя е зависима от операционната система и от обвивката ѝ. Всички мета-знаци на обвивката се интерпретират от нея - включително `|`, пренасочването на променливите на обкръжението и т.н. [source,perl6] .Изпълнете следното, ако сте в Linux/OSX ---- my $name = 'Neo'; run 'echo', "hello $name"; shell "ls"; ---- [source,perl6] .Изпълнете следното в Windows ---- shell "dir"; ---- `echo` и `ls` са познати команди от обвивката в Linux: + `echo` отпечатва текст в терминала (същото като `print` в Perl 6) + `ls` показва списък от файлове и папки в текущата папка `dir` е същото като `ls`, но в Windows. === Писане в и четене от файлове ==== slurp `slurp` се използува за четене на данни от файл наведнъж. Създайте файл със следното съдържание: .datafile.txt ---- John 9 Johnnie 7 Jane 8 Joanna 7 ---- [source,perl6] ---- my $data = slurp "datafile.txt"; say $data; ---- ==== spurt `spurt` се използува за запис на данни във файл наведнъж. [source,perl6] ---- my $newdata = "New scores: Paul 10 Paulie 9 Paulo 11"; spurt "newdatafile.txt", $newdata; ---- С изпълнението на горния код ще се създаде нов файл, именуван _newdatafile.txt_. Той ще съдържа данните от `$newdata`. === Работа с файлове и папки Perl 6 може да покаже списък от папки и файлове без помощта на системни команди (например като използвате `ls`). [source,perl6] ---- say dir; # Показва списък със съдържанието на текущата папка say dir "/Documents"; # Показва списък със съдържанието на указаната папка - ---- Освен това, можете да създавате и триете папки. [source,perl6] ---- mkdir "newfolder"; rmdir "newfolder"; ---- `mkdir` създава нова папка. + `rmdir` изтрива празна папка. Връща грешка, ако не е празна. Също така можете да проверявате дали указаният път съществува, дали е файл или папка: В папката, където ще изпълните долния скрипт, създайте празна папка `folder123` и празен файл с разширение pl6 `script123.pl6` [source,perl6] ---- 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` проверява дали указаният път е папка. WARNING: Потребителите на Windows могат да използуват `/` или `\\` за разделител + `C:\\rakudo\\bin` + `C:/rakudo/bin` + NOTE: За повече информация, свързана с входно-изходните операции, вижте https://docs.perl6.org/type/IO == Подпрограми === Създаване *Подпрограмите* (наричани също *функции*) са начин да се събере накуп набор от действия (функционалност) и да се използва по-късно. + За да създадете подпрограма, напишете ключовата дума `sub`, последвана от името на подпрограмата. След това подпрограмата може да бъде извиквана чрез изписване на името ѝ. + Разгледайте примера: [source,perl6] ---- sub alien-greeting { say "Hello earthlings"; } alien-greeting; ---- В този пример е показана подпрограма, която не изисква никакви входни данни. === Списък с параметри Подпрограмите могат да изискват входни данни. Тези данни се предоставят чрез подаване на *параметри*. Една подпрограма може да няма никакви или да има няколко *параметъра*. Броят и типът на параметрите на една подпрограма се наричат *сигнатура* (от лат. signatura — обозначение, бел. прев.). Следващата подпрограма приема низ като параметър. [source,perl6] ---- sub say-hello (Str $name) { say "Hello " ~ $name ~ "!!!!" } say-hello "Paul"; say-hello "Paula"; ---- === Многократност Може да създадете няколко подпрограми с едно и също име, но различен списък от параметри (многократно). Когато подпрограмата бъде извикана, средата за изпълнение ще реши коя от тях да изпълни, в зависимост от типа и броя на подадените параметри. Този тип подпрограми се създават както обикновено, но като използувате ключовата дума `multi` вместо `sub`. [source,perl6] ---- multi greet($name) { say "Good morning $name"; } multi greet($name, $title) { say "Good morning $title $name"; } greet "Johnnie"; greet "Laura","Mrs."; ---- NOTE: Оригиналното заглавие на секцията е "Multiple Dispatch". Множествено разпределение или "много-методи" е свойство на някои програмни езици, при което една функция или метод може да бъде динамично избрана за изпълнение в зависимост от типа и броя на подадените ѝ аргументи. От Уикипедия: https://en.wikipedia.org/wiki/Multiple_dispatch (Бел. Прев.) === Незадължителни и подразбиращи се параметри Ако сте създали подпрограма, приемаща един параметър и я извикате, без да ѝ подавате нищо, изпълнението ще се провали. Пърл 6 ни дава възможност да създаваме подпрограми с: * Незадължителни параметри * Параметри със стойност по подразбиране Незадължителните параметри се задават, като в края на името на параметъра (променливата) се добави `?`. [source,perl6] ---- sub say-hello($name?) { with $name { say "Hello " ~ $name } else { say "Hello Human" } } say-hello; say-hello("Laura"); ---- Ако потребителят не подаде параметър, той може да приема стойност по подразбиране. + Това се постига, като присвоим стойността, когато създаваме подпрограмата. [source,perl6] ---- sub say-hello($name="Matt") { say "Hello " ~ $name; } say-hello; say-hello("Laura"); ---- === Връщане на стойности Всички подпрограми дотук *правят нещо*, показват някакъв текст в терминала. Понякога обаче, може да поискаме подпрограмата да *върне* някаква стойност, която да използуваме по-късно в приложението. По подразбиране, резултатът от изпълнението на последния ред в нашата подпрограма е стойността, която тя връща. [source,perl6] .Подразбиращо се връщане на стойност ---- sub squared ($x) { $x ** 2; } say "7 squared is equal to " ~ squared(7); ---- За по-голяма яснота, е добра идея _изрично_ да укажем какво връщаме. Това се прави с ключовата дума `return`. [source,perl6] .Изрично връщане на стойност ---- sub squared ($x) { return $x ** 2; } say "7 squared is equal to " ~ squared(7); ---- ==== Ограничаване на връщаните стойности В един от предишните примери видяхме, че можем да зададем определен тип на приемания параметър. Същото може да бъде направено с връщаната стойност. За да ограничим типа на връщаната стойност, използуваме _отличителя_ `returns` или стрелка `-\->` в сигнатурата. [source,perl6] .Използуване на отличителя `returns` ---- sub squared ($x) returns Int { return $x ** 2; } say "1.2 на квадрат е " ~ squared(1.2); ---- [source,perl6] .Използуване на стрелката ---- 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) ---- [TIP] ==== Ограниченията по тип могат да задават не само типа на връщаната стойност, но и това, дали е дефинирана стойността. В предишните примери указвахме, че типа на връщаната стойност трябва да бъде `Int`. Можехме и да укажем изрично, дали върнатият `Int` трябва да бъде определен (defined) (стойността да не е `Nil`) или не. Това се постига с използуването на следните сигнатури: + `--> Int:D` and `--> Int:U` И така, да се използуват тези ограничения е добра практика. + Ето долу новата версия на предния пример, където е използувано `:D` (defined), за да задължи връщаната стойност от тип `Int` да бъде определена (а не неопределена – `U` (undefined)). [source,perl6] ---- sub squared ($x --> Int:D) { return $x ** 2; } say "1.2 squared is equal to " ~ squared(1.2); ---- ==== NOTE: За повече информация относно подпрограмите и функциите, вижте https://docs.perl6.org/language/functions == Функционално Програмиране В тази глава ще разгледаме част от възможностите на езика, които улесняват функционалното програмиране. === Функциите са граждани първа класа Функциите/подпрограмите са граждани първа класа: * Могат да се подават като параметри * Могат да бъдат връщани от други функции * Могат да бъдат присвоявани като стойност на променливи Прекрасен пример е функцията `map`. + `map` е *функция от високо ниво*, тя приема друга функция като параметър. [source,perl6] .Скрипт ---- my @array = <1 2 3 4 5>; sub squared($x) { $x ** 2 } say map(&squared,@array); ---- .Изход ---- (1 4 9 16 25) ---- .Обяснение Създадохме подпрограма, наречена `squared`, която повдига на квадрат всяко подадено ѝ число. + След това използувахме `map`, функция от високо ниво, и подадохме два параметъра - функция и масив. + Изходът е списък от елементите на масива, повдигнати на квадрат. Забележете, че когато подаваме функция като параметър, трябва да поставим пред името ѝ знака `&`. === Безименни функции *Безименната функция* се нарича още *ламбда*. + Безименната функция не е обвързана с идентификатор (тя няма име). Нека пренапишем примера с `map`, като използуваме безименна функция. [source,perl6] ---- my @array = <1 2 3 4 5>; say map(-> $x {$x ** 2},@array); ---- Забележете, че вместо да обявяваме подпрограма и да я подаваме като параметър на `map`, ние я създадохме направо в безименната подпрограма `\-> $x {$x ** 2}`. На жаргона на Пърл 6 наричаме това записване *остър блок* [source,perl6] .Остър блок може да се използува също за присвояване на функции на променливи: ---- my $squared = -> $x { $x ** 2 } say $squared(9); ---- === Верижност (Chaining) В Пърл 6 методите могат да бъдат извиквани верижно, така че да не се налага да подавате изхода от един метод на друг. NOTE: Всяка вградена функция може да се използува и като метод върху обект. (Бел. прев.) Ето едно неверижно решение: [source,perl6] ---- 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`. Но можем да се възползваме от възможността за *верижно извикване* на методи и да запишем примера по следния начин: [source,perl6] ---- 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; ---- Вече виждате, че верижното извикване е _по-лесно за четене_. === Оператор за подаване *Операторът за подаване*, наричан _тръба_ в някои езици за програмиране, представя още по-добре верижното извикване. [source,perl6] .Подаване напред ---- 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 ---- Виждате, че последователността на извикване на методите е отгоре надолу. [source,perl6] .Подаване назад ---- 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; ---- .Обяснение Подаването назад е като подаването напред, но наобратно. + Последователността на извикване на методите е отдолу нагоре – от последната към първата стъпка. === Свръх-оператор *Свръх-операторът* (hyper operator) `>>.` ще извика даден метод върху всички елементи от един списък и ще върне списък с резултатите. [source,perl6] ---- 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`, за да обхождаме всяка стойност от масива. WARNING: Пърл 6 гарантира, че редът на изходите от работата на метода, извикан чрез свръх-оператора, ще е същият като на входните стойности. + Но *няма гаранция*, че Пърл 6 ще извика метода последователно, както е редът на елементите, нито че извикването ще е в същата нишка. + Така че, бъдете внимателни с методи, които имат странични ефекти като `say` (където страничният ефект е показването на подадената стойност). === Сливания *Сливането* (junction) е логическо съпоставяне на стойности. В израза долу `1|2|3` е сливане. [source,perl6] ---- my $var = 2; if $var == 1|2|3 { say "Променливата има стойност 1 или 2 или 3" } ---- Използуването на сливания предизвиква *автоматично създаване на нишки (autothreading)*; операцията се извършва за всеки елемент от сливането, като всички резултати са събрани в ново сливане и върнати. // Да предложа на автора да се каже повече за сливанията. Да се обясни 'и', не само 'или'. === Мързеливи списъци *Мързелив списък* е този, който е изчислен мързеливо. + Мързеливо означава отлагане на изчислението на даден израз до момента, когато е необходимо, и избягване повторение на изчислението, като се съхранява резултата в паметта. Ползите са: * Нарастване на производителността чрез избягване на излишни изчисления; * Възможността да се създават при необходимост безкрайни структури от данни; * Възможността да се управлява изпълнението. За да построим мързелив списък, използуваме вмъкнатия оператор `...`. + Мързеливият списък има *начален(лни) елемент(и)*, *генератор* (начин за създаване на списъка – бел. прев.) и *край*. [source,perl6] .Примерен мързелив списък ---- my $lazylist = (1 ... 10); say $lazylist; ---- Началният елемент е 1, а крайният е 10. Не е определен генератор за създаване на списъка, така че се използува подразбиращият се генератор – последователно нарастване с единица (+1) + С други думи, този мързелив списък (ако е необходимо) ще върне следните елементи (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) [source,perl6] .Безкраен мързелив списък ---- my $lazylist = (1 ... Inf); say $lazylist; ---- Този списък може да върне (ако е необходимо) всяко цяло число между 1 и безкрайност, т.е. всяко цяло число. [source,perl6] .Мързелив списък, построен чрез изведен генератор ---- my $lazylist = (0,2 ... 10); say $lazylist; ---- Първоначалните елементи са 0 и 2, а крайният елемент – 10. Не е определен генератор, но Пърл 6 ще изведе генератора от първоначалните елементи (+2). Този мързелив списък може да върне (ако е необходимо) елементите (0, 2, 4, 6, 8, 10). [source,perl6] .Мързелив списък, построен чрез определен генератор. ---- my $мързел-списък = (0, { $_ + 3 } ... 12); say $мързел-списък; ---- В този пример определихме изрично генератор, ограден с `{ }`. + Този списък би върнал (ако е нужно) елементите (0, 3, 6, 9, 12). [WARNING] ==== Ако използуваме изрично зададен генератор, крайният елемент трябва да е стойност, която е възможно да бъде върната от генератора. + Ако възпроизведем примера горе с краен елемент 10 вместо 12, генераторът няма да спре. Генераторът ще _прескочи_ крайният елемент. Може да заместите `0 ... 10` с `0 ...^ * > 10` + Чете се по следния начин: От 0 до първата стойност по-голяма от 10 (но без нея). [source,perl6] .Това няма да спре генератора ---- my $lazylist = (0, { $_ + 3 } ... 10); say $lazylist; ---- [source,perl6] .Това ще спре генератора ---- my $lazylist = (0, { $_ + 3 } ...^ * > 10); say $lazylist; ---- ==== === Затваряния Всички съставени от код обекти в Пърл 6 са *затваряния* (closures). Това означава, че те могат да се обръщат към лексикални (частни) променливи от заобикалящия ги блок. [source,perl6] ---- 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`, който съществуваше, когато подпрограмата бе създадена. Да видим и по-интересен пример. [source,perl6] ---- 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`. == Класове и обекти В предишната глава научихме как Пърл 6 улеснява функционалното програмиране. В тази глава ще разгледаме обектно-ориентираното програмиране в Пърл 6. === Въведение _Обектно-ориентираното_ програмиране е една от широко използуваните парадигми напоследък. *Обектът* е набор от променливи и подпрограми, събрани заедно. Променливите се наричат *атрибути (член-променливи)*, а подпрограмите – *методи*. Атрибутите определят *състоянието*, а методите определят *поведението* на обекта. *Класът* е образец (шаблон) за създаване на *обекти*. + За да разберем взаимовръзката, нека разгледаме следния пример: + (Превеждам и програмния код на български, просто защото е възможно и той ще работи. Не е ли това прекрасно? Пробвайте го в конзолата. Бел. прев.) |=== | В една стая има четирима души. | *обекти* => 4 човека | Тези четирима души са Човеци | *Клас* => Човек | Те имат различни имена, възраст, пол и народност. | *атрибути* => име, възраст, пол, народност |=== На _обектно-ориентиран_ жаргон казваме, че обектите са *инстанции* (отделни случаи) на един клас. Да разгледаме следния скрипт: [source,perl6] ---- class Човек { has $.име; has $.възраст; has $.пол; has $.народност; } my $иван = Човек.new(име => 'Иван', възраст => 23, пол => 'М', народност => 'българин'); say $иван; ---- Ключовата дума `class` се използува за определяне на класа. + Ключовата дума `has` (има) се използува, за да определи член-променливите на класа. + Методът `.new()` се нарича *конструктор*. Той създава обекта като отделен случай на класа, върху който е извикан. В скрипта горе `$иван` съдържа указател към нов случай на "Човек", определен чрез `Човек.new()`. + Параметрите, подадени на метода `.new()`, се използуват за определяне членовете на новосъздадения обект. На класа може да се даде _лексикална видимост_, като се използува `my`: [source,perl6] ---- my class Human { } ---- === Капсулиране Капсулирането (Encapsulation) е понятие в обектно-ориентираното програмиране, което групира набор от данни и методи заедно. Данните (атрибути) трябва да са *частни*, тоест, достъпни само от вътрешността на обекта. За достъп до данните се използуват методи, наречени *аксесори* (от access – достъп, бел. прев.). Двата скрипта долу имат един и същ изход. .Непосредствен достъп до променливата: [source,perl6] ---- my $var = 7; say $var; ---- .Капсулиране: [source,perl6] ---- my $var = 7; sub sayvar { $var; } say sayvar; ---- Методът `sayvar` е аксесор. Той опосредства достъпа до променливата, без да имаме пряк достъп до нея. Капсулирането е улеснено в Пърл 6 чрез използуването на *втори знак* (*twigil*). Вторият знак е вторичен _сиджил_. Той се поставя между първия знак и името на атрибута. + Два вида втори знак се използуват в класовете: * `!` се използува за изрично указване, че член-променливата е частна. * `.` се използува за автоматично създаване на аксесор на член-променливата. По подразбиране всички член-променливи са частни, но е добър навик винаги да се ползва `!` като втори знак. Затова, трябва да пренапишем горния клас както следва: [source,perl6] ---- 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;` === Именувани и позиционни параметри В Пърл 6 всички класове наследяват готов конструктор `.new()`. + Той може да бъде използуван за създаване на обекти, като му се подават параметри. + На готовия конструктор могат да се подават само *именувани параметри*. + В предишния пример, ще видите, че всички параметри, подадени на `.new()`, са именувани. * `name \=> 'John'` * `age \=> 23` Какво, ако не искаме да подаваме името на всеки атрибут, когато създаваме обект? + Тогава трябва да създадем конструктор, който приема *позиционни параметри*. [source,perl6] ---- 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; ---- === Методи ==== Въведение Методите са _подпрограмите_ на обекта. + Както подпрограмите, те са средство за събиране на функционалност на едно място и именуването ѝ. Те приемат *параметри*, имат *сигнатура* и могат да бъдат създадени като *multi*. Методите се създават с помощта на ключовата дума `method`. + В общия случай методите се създават, за да извършват някакви действия върху атрибутите на обекта. Това спомага за капсулирането. Атрибутите могат да бъдат променяни само в обекта, чрез използуване на методи. Външният свят може да достъпва само методите и няма достъп до атрибутите. [source,perl6] ---- 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` представлява непосредствено извикване на член-променливата. ==== Частни методи Обикновено методите могат да се извикват от външното обкръжение на класа. *Частните методи* могат да се извикват само докато сме вътре в класа. + Възможен случай е метод, който извиква друг метод, за да извърши някакво специфично действие. Методът, който взаимодейства с външния свят, е публичен, докато този, който се извиква вътре в него, трябва да си бъде частен. Не искаме потребителите на нашия клас да го използуват непосредствено и затова го обявяваме като частен. За да обявим частен метод, използуваме знака `!` пред името му. + Частните методи се извикват чрез `!` вместо `.` [source,perl6] ---- method !азсъмчастен { # тук си пишем програмния код } method азсъмпубличен { self!азсъмчастен; # правим още нещо } ---- === Атрибути на клас *Клас-атрибутите* са такива, които принадлежат на класа, а не на обекта, създаден от него. + На тях могат да им се дават стойности при обявяването им. + Клас-атрибутите се обявяват с помощта на `my`, вместо `has`. + Те се извикват непосредствено върху класа, вместо върху обектите. [source,perl6] ---- 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; ---- === Тип на достъпа Дотук във всички примери използувахме атрибутите само за да *вземем* информация за обектите. Как да променим стойността на някой атрибут? + Трябва да го означим като променяем _за-писане/за-четене_ чрез ключовите думи `is rw`. [source,perl6] ---- 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`. === Наследяване ==== Въведение *Наследяването* е друго понятие в обектно-ориентираното програмиране. Когато създаваме класове, бързо установяваме, че някои атрибути и методи се повтарят в много от тях. + Трябва ли да дублираме код? + Не! Трябва да ползуваме *наследяване*. Нека си представим, че искаме да създадем два класа – един за Човек и един за Служител. + Човек има два атрибута: име и възраст. + Служител има четири атрибута: име, възраст, компания и заплата. Изкушени сте да създадете класовете така: [source,perl6] ---- class Human { has $.name; has $.age; } class Employee { has $.name; has $.age; has $.company; has $.salary; } ---- Въпреки че технически погледнато това е правилен код, той е беден като замисъл. По-добър би бил следния вариант: [source,perl6] ---- class Human { has $.name; has $.age; } class Employee is Human { has $.company; has $.salary; } ---- Ключовата дума `is` (3 л. ед. ч. на глагола "съм" – бел. прев.) определя наследяването. + На обектно-ориентиран жаргон казваме, че Employee е *дъщерен* клас на Human, и че Human е *родителски* за Employee. Всички дъщерни класове наследяват атрибутите и методите на родителския клас, така че няма нужда да ги създаваме наново. ==== Презаписване Класовете наследяват всички атрибути и методи от родителските класове. Има случаи обаче, когато искаме някой метод в дъщерния клас да има различно от наследеното поведение. За да постигнем това, ние го създаваме наново в дъщерния клас. + Това се нарича *презаписване* (*overriding*). В примера долу методът `introduce-yourself` е наследен от класа Employee. [source,perl6] ---- 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; ---- Презаписването се прави така: [source,perl6] ---- 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; ---- В зависимост от това, от кой клас е създаден обектът, ще бъде изпълнен съответният метод. ==== Подметоди *Подметодите* са такива методи, които не се наследяват от дъщерните класове. + Те са достъпни само в класа, в който са създадени. + Те се създават с помощта на ключовата дума `submethod`. === Множествено наследяване Пърл 6 поддържа множествено наследяване. Един клас може да наследява множество класове. [source,perl6] ---- 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`. [source,perl6] ---- 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] ---- === Роли *Ролите* са донякъде подобни на класовете, понеже също се състоят от методи и атрибути. Ролите се обявяват с помощта на ключовата дума `role`. Класовете, които искат да осъществят (имплементират) една роля, трябва да го направят, като използуват ключовата дума `does` (3 л. ед. ч. на глагола правя – бел. прев.). .Нека пренапишем примера за множественото наследяване, като използуваме роли: [source,perl6] ---- 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`. [source,perl6] ---- 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)) ---- .Обяснение Ако множество роли са приложени на един и същи клас и се появи противоречие, по време на компилиране ще бъде хвърлена грешка. Това е много по-сигурен подход, в сравнение с множественото наследяване, където такива противоречия не се смятат за грешка и биват разрешавани автоматично по време на изпълнение. Ролите ви предупреждават, че има противоречие. === Самонаблюдение *Самонаблюдение* (Introspection) е действието, при което вземаме информация за свойствата на един обект. Такива са неговите атрибути, методи или тип. [source,perl6] ---- 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_), ако обектът е създаден от класа, с който е сравняван или го наследява. [NOTE] -- За да научите повече за обектно-ориентираното програмиране в Пърл 6, вижте: * https://docs.perl6.org/language/classtut * https://docs.perl6.org/language/objects -- == Обработка на Изключения === Прихващане на Изключения *Изключенията* (Exceptions) представляват специално поведение, което се случва по време на изпълнение, когато нещо се обърка. + Казваме, че програмата ни _хвърля_ изключение. Да погледнем следния скрипт. Той работи както трябва. [source,perl6] ---- my Str $name; $name = "Joanna"; say "Hello " ~ $name; say "How are you doing today?" ---- .`Изход` ---- Hello Joanna How are you doing today? ---- Сега да видим този скрипт. Той хвърля изключение. [source,perl6] ---- 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 at exceptions.pl6:2 (Проверката за типа на $name е неуспешна; очакваше се Str, но се оказа Int в блок в exceptions.pl6:2) ---- Сигурно вече сте забелязали, че при грешка (в този случай присвояване на цяло число на променлива с тип Str) програмата винаги спира и следващите редове не се изпълняват, дори да са правилно написани. *Обработка на изключението* е действието, при което _прихващаме_ изключение, което е било _хвърлено_, за да продължи работата на програмата ни. [source,perl6] ---- 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` (пробвай-хвани). [source,perl6] ---- try { # тук пишете кода # ако нещо се обърка, скриптът ще влезе в блока CATCH долу # ако всичко е наред, блокът CATCH ще бъде пренебрегнат CATCH { default { # кодът, който се намира тук, ще бъде изпълнен само ако е хвърлено изключение } } } ---- Блокът, в който прихващаме изключението (`CATCH`), може да бъде обявен по същия начин както `given`. Това означава, че можем да _прихващаме_ и обработваме по различен начин много типове изключения. [source,perl6] ---- try { # тук пишете кода # ако нещо се обърка, скриптът ще влезе в блока CATCH долу # ако всичко е наред, блокът CATCH ще бъде пренебрегнат CATCH { when X::AdHoc { # да се направи нещо, в случай че е хвърлено изключение от тип X::AdHoc } when X::IO { # да се направи нещо, в случай че е хвърлено изключение от тип X::IO } when X::OS { # да се направи нещо, в случай че е хвърлено изключение от тип X::OS } default { # да се направи нещо, в случай че е хвърлено изключение от друг тип } } } ---- === Хвърляне на Изключения Пърл 6 ви дава възможност и изрично да хвърляте изключения. Могат да бъдат хвърляни два типа изключения: * случайни изключения * типови изключения [source,perl6] .случайно ---- my Int $age = 21; die "Error !"; ---- [source,perl6] .типово ---- my Int $age = 21; X::AdHoc.new(payload => 'Error !').throw; ---- Случайните изключения се хвърлят, като се използува вградената функция `die`, последвана от обяснително съобщение за грешката. Типовите изключения са обекти. Това обяснява и използуването на конструктора `.new()` в горния пример. + Основният клас на всички типови изключения е `X`. Ето няколко примера: + `X::AdHoc` е най-простият тип изключение + `X::IO` се използува за входно-изходни грешки + `X::OS` се използува за системни грешки + `X::Str::Numeric` бива хвърляно при неуспешни опити за превръщане на низове в числа NOTE: За да видите пълен списък с типовете изключения и свързаните методи, идете на https://docs.perl6.org/type-exceptions.html == Изрази за съвпадения Изразът за съвпадение или просто съвпадение (regular expression, _regex_) е последователност от знаци за намиране на съвпадение в текст. + Най-лесният начин за разбирането на тези изрази е да мислите за тях като за шаблони. NOTE: Позволявам си да преведа по нов начин наложилото се, но не носещо никакъв смисъл понятие "регулярни изрази" (бел. прев.) [source,perl6] ---- if 'просветление' ~~ m/ свет / { say 'Просветление съдържа корена "свет".'; } ---- В този пример проверяваме с помощта на оператора за умни съвпадения `~~` дали една дума съдържа корена "свет". + В думата "просветление" се търси съвпадение със "свет" `m/ свет /` === Обявяване на шаблон Шаблонът за търсене на съвпадение може да бъде обявен както следва: * `/свет/` * `m/свет/` * `rx/light/` Празното пространство няма значение, освен ако не е указано нещо друго. `m/light/` и `m/ light /` са едно и също нещо. === Намиране съвпадения на знаци Буквено-цифровите знаци и знакът за подчертаване `_` се пишат по обичайния начин. Всички други символи трябва да се избягват с обратно наклонена черта или да се ограждат с кавички. [source,perl6] .Обратно наклонена черта ---- if 'Температура: 13' ~~ m/ \: / { say "Предоставеният низ съдържа двоеточие : "; } ---- [source,perl6] .Единични кавички ---- if 'Age = 13' ~~ m/ '=' / { say "Низът съдържа знака за равенство = "; } ---- [source,perl6] .Двойни кавички ---- if 'name@company.com' ~~ m/ "@" / { say "Това е валиден адрес за електронна поща, защото съдържа знака @." } ---- === Намиране на знаци по категории Знаците могат да бъдат групирани в категории, а ние можем да търсим съвпадение по тях. Можем също така да търсим по обратното значение на категорията (всичко друго освен нея). |=== | *Категория* | *Израз* | *Обратно значение* | *Израз* | Знак за дума (буква, цифра или знак за подчертаване) | \w | Всеки друг знак освен знака за дума | \W | Цифра | \d | Всеки знак, който не е цифра | \D | Празно пространство | \s | Всеки знак, който не е празно пространство | \S | Водоравно празно пространство | \h | Всеки знак, който не е водоравно празно пространство | \H | Отвесно празно пространство | \v | Всеки знак, който не е отвесно празно пространство | \V | Табулация | \t | Всеки знак, който не е табулация | \T | Нов ред | \n | Всеки знак, без нов ред | \N |=== [source,perl6] ---- if "Иван123" ~~ / \d / { say "Това не е име. Имената не съдържат числа."; } else { say "Това е име." } if "Иван-Данов" ~~ / \s / { say "Този низ съдържа празно пространство."; } else { say "Този низ не съдържа празно пространство."; } ---- === Уникод-свойства Да се намира съвпадение чрез категории от знаци е удобно. Въпреки това, по-систематичен подход би бил да се използуват уникод-свойства. Уникод-свойствата са оградени с `<: >`. [source,perl6] ---- 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" } ---- === Заместващи знаци При търсене на съвпадения могат да се ползуват и заместващи знаци. Точката `.` дава съвпадение с всякакъв знак. [source,perl6] ---- 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"; } ---- === Количествени указатели Количествените указатели се поставят след знак и указват колко пъти се очаква появата на знака в текста. Въпросителният знак `?` означава нула или един път. [source,perl6] ---- if 'ac' ~~ m/ a?c / { say "Match"; } else { say "No Match"; } if 'c' ~~ m/ a?c / { say "Match"; } else { say "No Match"; } ---- Звездата `*` означава нула или повече пъти. [source,perl6] ---- 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"; } ---- `+` означава поне веднъж. [source,perl6] ---- 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"; } ---- === Резултати от съвпадението Когато се намери съвпадение на търсенето в някакъв текст, резултатът се съхранява в специалната променлива `$/`. [source,perl6] .Скрипт ---- 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` връща мястото в низа (цяло число), където съвпадението свършва. + TIP: По подразбиране празното пространство (в шаблон за намиране на съвпадение) няма значение. Ако искаме да намерим съвпадение по шаблон, съдържащ празно пространство, трябва да го укажем изрично. Като поставим `:s` в шаблона `m/:s Perl 6/` указваме празните пространства да се приемат буквално и да не се премахват при компилиране на шаблона. Иначе можехме да запишем израза като `m/ Perl\s6 /` и да ползуваме `\s`, което видяхме по-рано като заместител за празно пространство. Ако израз за съвпадение съдържа повече от едно празно пространство, с използуването на `:s` се оказваме по-ефективни, отколкото ако използуваме `\s` за всяко празно пространство. === Пример Нека проверим дали един адрес за електронна поща е валиден. + За целите на примера ще приемем, че адресът се състои от: + име [точка] фамилия [при] фирма [точка] (com/org/net) WARNING: Изразът в този пример за проверка на адреса не е много точен. Единствената му цел е да покаже възможностите в Пърл 6. Не го ползвайте за производствени цели. [source,perl6] .Скрипт ---- 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>+` [source,perl6] .Също така един израз може да бъде разбит на няколко именувани израза ---- 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 ~~ / / { say $/ ~ " is a valid email"; } else { say "This is not a valid email"; } ---- Синтаксисът за обявяване на именуван израз за съвпадение е: `my regex regex-name { regex definition }` + Синтаксисът за извикване на именуван израз за съвпадение е: `` NOTE: За повече информация относно изразите за съвпадение, вижте https://docs.perl6.org/language/regexes == Модули в Пърл 6 Пърл 6 е език с общо предназначение. Той е подходящ за всякакви задачи: обработка на текст, графика, уеб, бази данни, мрежови протоколи и т.н. Многократното използване на код е ключово понятие. Програмистите не трябва да преоткриват колелото с всяка нова задача. Пърл 6 ни дава възможност да създаваме и разпространяваме *модули*. Всеки модул представлява пакетиран набор от функционалност, която може да бъде инсталирана и използвана. _Зеф_ (_Zef_) е средство за управление на модули, което се разпространява с Rakudo Star. За да инсталирате отделен модул, напишете следната команда в терминал: `zef install "module name"` NOTE: Модулите за Пърл 6 се намират на адрес: https://modules.perl6.org/ === Използване на модули MD5 е хеш-функция от криптографията, която връща 128-битова стойност. + MD5 има много приложения, едно от които е криптиране на пароли, съхранявани в бази от данни. Когато се регистрира нов потребител, неговите данни не се записват като обикновен текст, а се _хешират_. Причината е, че ако до данните се осъществи неразрешен достъп, паролите няма да бъдат четими. За наше щастие вече има модул в Пърл 6, който имплементира алгоритъма MD5. Да го инсталираме: + `zef install Digest::MD5` Сега пуснете скрипта по-долу: [source,perl6] ---- use Digest::MD5; my $password = "password123"; my $hashed-password = Digest::MD5.new.md5_hex($password); say $hashed-password; ---- За да извикаме функцията `md5_hex()`, трябва да заредим модула, който я предоставя. + Ключовата дума `use` зарежда модула в скрипта. WARNING: В действителност превръщането на паролата в MD5-сума е недостатъчно, тъй като е податливо на т.нар. речникови атаки. + То трябва да бъде съчетано със "сол" (произволно генерирана стойност – сол към паролата; Бел. ред.) link:https://en.wikipedia.org/wiki/Salt_(cryptography)[https://en.wikipedia.org/wiki/Salt_(cryptography)]. == Уникод Уникод е стандарт за кодиране и представяне на текст на почти всички писмени системи в света. + UTF-8 е таблица със знаци, която може да кодира всички възможни знаци от Уникод. Знаците се състоят от: + *Графема*: Видимо представяне – как изглежда. + *Точка на кода*: Число (пореден номер), присвоено на знака. === Използване на Уникод .Да видим как можем да изобразяваме знаци чрез Уникод. [source,perl6] ---- say "Б"; say "\x0411"; say "\c[CYRILLIC CAPITAL LETTER BE]"; ---- Трите реда горе показват различни начини за изграждане на един знак: . Като просто напишем знака (графема) . Чрез въвеждане на `\x`, последван от точката на кода (в шестнадесетична бройна система) . Чрез въвеждане на `\c`, последван от името на точката на кода .Сега да изобразим усмихнато личице [source,perl6] ---- say "☺"; say "\x263a"; say "\c[WHITE SMILING FACE]"; ---- .Друг пример, съчетаващ две кодови точки [source,perl6] ---- say "á"; say "\x00e1"; say "\x0061\x0301"; say "\c[LATIN SMALL LETTER A WITH ACUTE]"; ---- Буквата `á` може да се напише: * като използваме нейната единствена кодова точка `\x00e1` * или като съчетание от две кодови точки – `a` и ляво ударение `\x0061\x0301` .Ето някои от методите, които могат да се използват върху знака (той е обект): [source,perl6] ---- say "á".NFC; say "á".NFD; say "á".uniname; ---- .`Изход` ---- NFC:0x<00e1> NFD:0x<0061 0301> LATIN SMALL LETTER A WITH ACUTE ---- `NFC` връща точката на кода. + `NFD` разглобява знака и връща кодовата точка на всяка негова част. + `uniname` връща името на кодовата точка. .Всяка буква от Уникод може да се използва като идентификатор: [source,perl6] ---- my $Δ = 1; $Δ++; say $Δ; ---- .Уникод знаци могат да се ползват при математически изчисления: [source,perl6] ---- my $var = 2 + ⅒; say $var; ---- == Успоредност, Съгласуваност и Неедновременност === Успоредност (Parallelism) В общия случай всички задачи в една програма се изпълняват последователно. Това може да си е съвсем наред, освен ако не отнема много време. За щастие Пърл 6 позволява да се изпълняват няколко задачи едновременно. Тук е важно да споменем, че успоредност може да означава едно от следните две неща: * *Успоредност на задачите*: Два (или повече) независими израза се изпълняват едновременно. * *Успоредност на данните*: Един израз се изпълнява едновременно върху списък от елементи. Да започнем с второто. ==== Успоредност на Данните [source,perl6] ---- 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` за множество елементи по едно и също време (успоредно): [source,perl6] ---- my @array = (0..50000); #Създаваме елементи в масива my @result = @array.race.map({ is-prime $_ }); #Извикваме is-prime върху всеки елемент say now - INIT now; #Показваме времето, което е отнела работата на скрипта ---- Обърнете внимание на `race` в израза. Този метод се грижи за едновременната (успоредна) обработка на елементите от масива. След като изпълните двата примера ( със и без `race`), сравнете времената им. [TIP] ==== `race` няма да запази реда на елементите. Ако искате това, ползвайте `hyper`. [source,perl6] .race ---- my @array = (1..1000); my @result = @array.race.map( {$_ + 1} ); .say for @result; ---- [source,perl6] .hyper ---- my @array = (1..1000); my @result = @array.hyper.map( {$_ + 1} ); .say for @result; ---- Ако изпълните двата примера, ще забележите, че елементите в изхода на втория са подредени, а в първия не са. ==== ==== Успоредност на задачите [source,perl6] ---- 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)} )` Двете действия, приложени на всеки от масивите, не зависят едно от друго. .Защо не ги изпълним едновременно? [source,perl6] ---- 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` ще получи хвърленото изключение. Проверете времето за изпълнение на всеки скрипт. WARNING: Успоредността на изпълнението винаги забавя скрипта, за да създаде нишките, в които се изпълняват задачите. Ако това забавяне не се компенсира при едновременното им изпълнение, скриптът ще изглежда по-бавен. Ето защо, използването на `race`, `hyper`, `start` и `await` за много прости скриптове, може всъщност да ги забави. === Съгласуваност и Неедновременност (Concurrency and Asynchrony) NOTE: За повече информация относно програмиране при съгласуване на задачите и управление на неедновременни задачи, вижте https://docs.perl6.org/language/concurrency == Общността * link:https://web.libera.chat/#raku[#perl6] IRC канал. Повечето обсъждания се случват в IRC. Там трябва да ходите по всякакви въпроси: https://perl6.org/community/irc * link:https://p6weekly.wordpress.com[p6weekly] седмичен преглед на промените в Пърл 6 и новините около него. * link:http://pl6anet.org[pl6anet] блог агрегатор. Следете статиите, свързани с Пърл 6. * link:https://www.reddit.com/r/perl6/[/r/perl6] Запишете се в канала, посветен на Пърл 6.