26. Что такое soft failure в Perl 6

В Perl 6 есть понятие soft failure — это исключения, которые проявляются не сразу, а только тогда, когда они уже неизбежны.

Пример 1

Типичный пример такой ситуации — деление на ноль.

my $x = 42;
my $y = $x / 0;
say 'Okay?';

Запускаем:

$ perl6 div0.pl 
Okay?

Как видите, хотя мы и попытались разделить на ноль, программа не завершилась с ошибкой и, более того, продолжила работу как ни в чем не бывало.

Однако, если попытаться напечатать значение $y, ошибка проявится.

my $x = 42;
my $y = $x / 0;
say "\$y = $y";

В этом случае программа завершится с исключением:

$ perl6 div0.pl 
Attempt to divide 42 by zero using div
  in block <unit> at div0.pl line 4

Пример 2

Второй пример — открытие несуществующего файла. Вот простейшая программа:

my $f = open 'rubbish-name.txt';
say 'Okay?';

Если ее запустить, ничего страшного не случится:

$ perl6 file0.pl 
Okay?

Поскольку файлом не попытались воспользоваться, ошибки нет. Если же, например, начать из него читать, то мы сразу остановимся:

$ perl6 file0.pl 
Failed to open file /Users/ash/rubbish-name.txt: No such file or directory
  in block <unit> at file0.pl line 1

25. Альтернативы в регексах Perl 6

В регексах Perl 6 есть два вида альтернатив — варианты разделяются либо одной, либо двумя вертикальными чертами.

Одинарная вертикальная черта создает список альтернатив, из которых выигрывает наиболее длинная. Рассмотрим такой пример:

say 'abcd' ~~ / a | ab | abc /;

Программа печатает 「abc」, то есть совпала самая длинная строка, несмотря на то, что она была последней в списке.

Теперь в той же программе удвоим все вертикальные черты:

say 'abcd' ~~ / a || ab || abc /;

На печати окажется 「a」, то есть первый же совпавший вариант.

В Perl 6 к регексам или их частям можно добавить блок кода, который выполнится, если эта часть совпала. Модифицируем предыдущие примеры:

'abcd' ~~ / 
    | a   { say 'a'   }
    | ab  { say 'ab'  }
    | abc { say 'abc' }
/;

'abcd' ~~ /
    || a   { say 'a'   }
    || ab  { say 'ab'  }
    || abc { say 'abc' }
/;

Обратите внимание, что для красоты разрешается ставить еще одну (одинарную или двойную) вертикальную черту перед первой альтернативой. В этом случае пустота перед первой чертой как отдельный вариант не добавится.

Программа печатает две строки:

abc
a

То есть был выполнен только тот блок кода, который соответствует выбранной альтернативе. Во втором примере это очевидно, а в первом — хотя последовательно совпадают и a, и ab, выполняется только третий блок кода.

24. Приблизительное сравнение в Perl 6

В Perl 6 есть оператор приблизительного равенства (approximately-equal operator). Он существует в двух формах — ASCII =~= и юникодном .

Оператор возвращает истину, если относительная разность между операндами меньше величины $*TOLERANCE, которая по умолчанию равна 10–15.

say 3.14159265358979323 =~= pi; # True
say 3.14 =~= pi;                # False

Допуск хранится в переменной с динамической областью видимости, поэтому ее можно изменять, если потребуется сравнение с другой точностью:

{
    my $*TOLERANCE = 0.1;
    say pi =~= 3.14; # True
}

Пара слов о работе оператора. Если оба операнда отличны от нуля, происходит такое сравнение:

|$a - $b| / max(|$a|, |$b|) < $*TOLERANCE

(Здесь |$a| — абсолютное значение величины.)

Если же один из операндов — ноль, то в этом случае возвращается результат сравнения абсолютной величины второго операнда с нулем. То есть в этом случае второй операнд не должен превышать по модулю 10–15:

say 1E-14 =~= 0; # False
say 1E-16 =~= 0; # True

 

23. Встроенные математические константы в Perl 6

Сегодня мы посмотрим на математические константы, доступные в Perl 6 без загрузки дополнительных модулей.

Во-первых, число пи в двух вариантах: ASCII- и юникодном:

say pi; # 3.14159265358979
say π;

Для каких-то применений, возможно, будет чуть более удобна константа, равная двум пи — tau или τ:

say tau; # 6.28318530717959
say τ;

Наконец, число e, то, которое про экспоненту и логарифмы:

say e; # 2.71828182845905
say ?;

Во второй строке здесь стоит символ U+1D452.

Для обозначения бесконечности можно пользоваться либо Inf, либо :

say (100 ** 100) / ∞; # 0

22. Ввод данных с консоли

В интерактивных программах требуется читать вводимые пользователем строки. В Perl 6 для этого есть специальная функция prompt.

Она останавливает программу и ожидает, пока кто-нибудь что-нибудь напишет. Затем эта строка возвращается функцией в программу:

my $str = prompt;
say $str;

prompt с аргументом

Функция prompt принимает аргумент — строку, которая будет напечатана перед тем, как начнется ввод:

my $name = prompt('Как вас зовут? > ');
say "Вас зовут $name.";

$*IN.get

Реализация функции prompt очень простая — она вызывает метод get на объекте $*IN, который по умолчанию привязан к стандартному потоку ввода STDIN. Поэтому вместо prompt можно написать более криптографичненько:

my $str = $*IN.get();
say $str;

get

Наконец, есть и просто отдельностоящая функция get, которая делает то же самое:

my $str = get();
say $str;

21. Определение длины строки в Perl 6

Perl 6 сразу и без обиняков считает исходный текст программы сохраненным в UTF-8. Да и вообще, поддержка юникода в Perl 6 очен радует. Сегодня посмотрим, как узнать длину строки.

Решение в лоб, знакомое по опыту Perl 5, не работает. На попытку вызвать функцию length компилятор выдает инструкцию воспользоваться новыми возможностями:

Undeclared routine:
    length used at line 1. Did you mean 'elems', 'chars', 'codes'?

elems, chars и codes

Метод elems относится скорее к массивам, чем к строкам. А вот с chars и codes давайте разберемся.

Метод символов в строке:

say 'hello'.chars;  # 5
say 'café'.chars;   # 4
say 'привет'.chars; # 6
say '嗨'.chars;     # 1

Все отлично работает независимо от языка и наличия диакритических знаков.

Метод codes возвращает число кодовых позиций, необходимых для записи строки. Во многих случаях результат будет совпадать с тем, что возвращает chars. Если же юникодный символ собран из таких частей, которые невозможно воспроизвести в одиночном символе (например, буква плюс какой-то хитрый диакритический знак, который в известных языках не применяется с данной буквой), то Perl не сможет привести этот символ к каноническому виду и, соответственно, chars покажет 1, а codes — 2.

Длина в байтах

Если нужно определить длину строки в байтах, то просто вызвать метод, например, bytes, не получится. Хотя скорее всего вы имели в виду кодировку UTF-8, Perl 6 желает услышать это явно. Действительно, при разном кодировании одна и та же строка занимет разное число байтов. Вот как это делается:

say 'hello'.encode('UTF-8').bytes;  # 5
say 'café'.encode('UTF-8').bytes;   # 5
say 'привет'.encode('UTF-8').bytes; # 12
say '嗨'.encode('UTF-8').bytes;     # 3

В UTF-16, например, число байт будет отличаться:

say 'hello'.encode('UTF-16').bytes;  # 10
say 'café'.encode('UTF-16').bytes;   # 8
say 'привет'.encode('UTF-16').bytes; # 12
say '嗨'.encode('UTF-16').bytes;     # 2

Разумеется, при попытке посчитать байты в Latin-1, часть строк не сможет быть конвертирована:

$ perl6 -e'say "ю".encode("Latin-1")'
Error encoding Latin-1 string: could not encode codepoint 1102
  in block  at -e line 1