Особенности работы с переменными и литералами в Perl6

в 18:19, , рубрики: perl, perl6, контексты, Песочница, типы данных, метки: , ,

Не так давно я решил начать изучать Perl6, даже не смотря на то, что фактически полностью работающего компилятора ещё нету. Подумал что можно смотреть Synopsis'ы, смотреть что из написанного в них уже работает, и изучать как именно это работает на различных примерах. Этим я и начал заниматься, попутно записывая себе в блокнот результаты различных манипуляций с переменными.
И вот в качестве своего первого поста я решил поделиться своими познаниями: тем, что обычно авторы материала оставляют на самостоятельную «проработку» — ответы на вопросы по типу «а что будет если ...» или «а что из себя это представляет в языке».
В этой статье Я опишу какие основные типы данных есть в этом языке и частично затрону вопрос о контекстах — одной из основных особенностей языка Perl.

Виды переменных и типы их возможных значений.
В Perl6 имеется 3 основных вида переменных: Скаляры, Массивы, Хэши.
Для объявления какой либо переменной используется ключевое слово my — объявление локальной переменной, our — для обяъвления глобальной переменной (Это не единственные способы объявления переменных, но пока что хватит).

  • Скаляры.
    Скаляры, это переменные, которые могут содержать единственное значение: Число, строку, булево значение, ссылку.
    Скалярные переменные объявляются с использование знака '$'. Переменные a, b и c объявляются как $a, $b и $c соотвественно. Так же переменные можно сразу же инициализировать значением.
    Например

    my $scalarVar;
    my ($var1, $var2, $var3);
    my $newStr = "Это строка";
    my $bigNumber = 192958238719732641028368452872938741029834612983412038471293847123641892364;
    my ($a, $b, $c) = 1, 2, 3; или my ($a, $b, $c) = (1, 2, 3);
    

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

    my $scalarVar;
    $scalarVar = 'Строка';
    $scalarVar = 25;
    $scalarVar = True;
    

    Основные типы данных:

    • Целые числа.
      Целые числа представляются типом Int(). Если число по количеству разрядов превышает максимальное количество разрядов типа Int() то оно автоматически приводится к типу Num(), которое может без округлений держать целое число любой длины (Я предполагаю что любой длины, так как один раз ради эксперимента я получил число, длиною в 300 тысяч цифр). Для записи слишком больших чисел можно использовать символ '_' для визуального отделения по несколько цифр в числе. Например 1_000_000, 1_0_0_0; 1_00_0, однако лучше последние два варианта не использовать;
    • Дробные числа
      Дробное число представляется типом Rat() или Num() в зависимости от их значения. Числа могут иметь вид 42.01, 10e-5. При присвоении переменной слишком длинных чисел perl6 может округлять дробную часть, но целую часть держит без изменений, какой бы длинной она не была (Опять же я не уверен в этом, но мои эксперименты подсказывают что будет именно так). Если в в результате вычислений получается очень большое дробное число, то результат может быть возвращен как значение Inf, которое является значением типа Num().
    • Строки
      В качестве строки может быть любой набор uncode-символов, ограниченный одинарными кавычками (') или двойными ("). Разница между ними в том, что в первом случае не происходит подстановка переменных а так же спец символов (таких как n, t). Если строка записана как 'f ф n 1' то при выводе все эти символы будут показаны без изменений. Если же написать "f ф n 1" то на экране окажутся две строки: 'f ф ' и строка ' 1'.
      'n' Это символ перехода на новую строку.
    • Булево значение
      Это значения True и False (Обязательно с большой буквы!), имеющие тип Bool().
    • Ссылки
      Переменная данного типа указывает на существующий объект. С помощью ссылки можно проводить точно такие же действия как и с самим объектом, на которую она указывает (например обращение к элементу массива).
      Самое главное:

      $a = 10;
      $b = $a;
      

      В данном случае переменной $b присваивается ссылка на переменную $a. Если вывести на экран значение переменной $b, то по ссылки автоматически будет взято значение переменной $a, НО если переменной $b присвоить новое значение $b=5; то значение 5 будет записано вместо ссылки на переменную $a, а не в саму переменную $a. Т.е. теперь $b будет содержать не ссылку, а просто число.

    • Пара 'Ключ-значение'
      Этот тип имеет имя Pair(). Конструктором пар является '=>'. Например (1=>'a') создает пару, где ключем является 1, а значение по данному ключу — 'a'. Причем возможными значениями ключей являются строки ( в данном случае 1 была автоматически приведена к строковому типу), а значениями по ключу могут быть любые скаляры.

  • Массивы
    В Perl6 массивы динамические, т.е. при их инициализации не нужно указывать максимальный размер, т.к. память выделяется по мере добавления новых элементов. Элементами массива являются любые скаляры и скалярные переменные. В массивах индекс элемента зависит от позиции при объявлении. Конструктором массива является запятая. Например выражение ('a', 502, "str") конструирует массив размером в 3 элемента (круглые скобки не обязательны). Индексы у элементов начинаются с нуля. т.е. нулевым элементом массива является 'a', под индексом 1 находится число 502, под индексом 2 находится «str». Типы скаляров не обязательно должны быть одинаковыми. Для генерации ссылки на массив используются квадратные скобки. Например ['a', 502, "str"] возвращает ссылку на новый массив, которую можно присвоить скалярной переменной.
    Имя массива должно начинаться с символа '@'. Например

    @mass, @colors, @names

    . Причем $names и @names являются разными переменными.
    Массивы могут содержать любое количество элементов различного типа.
    Примеры использования:

    @arr = ('a', 502, "str");
    @arr2 = (10, False, True, $scalarVar);
    

    Для обращения к элементам массива используются квадратные скобки:
    Например выражение @arr2[0] вернет 10, а @arr2[3] вернет не переменную $scalarVar а её значение, которое было на момент создания массива. Чтобы получить значение переменной $scalarVar записывать в массив нужно было ссылку на эту переменную: @arr2 = (10, False, True, $scalarVar);
    Если изначальный размер массива 2: @arr = (1, 2); и мы хотим задать значение элемента под индексом 5 (Например @arr[5] = 'new elem') то массив будет расширен до 6 элементов, а все добавленные элементы кроме @arr[5] будут неинициализированы ( Тип этого значения — Any() ).

  • Хэши
    Хэши представляют собой таблицу соотвествий, когда каждому ключу ставится в соотвествии одно значение. Значениями элементов хэша являются любоые скаляры а ключами являются строки.
    Имя хэшей должно начинаться с символа '%':

    my %hash = ('key1'=>'value1', '2'=>200, 'key10'=>True);
    

    Если присвоить хэшу массив, то элементы массива будут попарно составлять элемент хэша:

    my @mas = ('a, 1, 'b', 2);
    my %hash = @mas;
    

    В результате этого в переменной %hash будет храниться ('a'=>1, 'b'=>2);
    Для обращения к элементам хэша используются фигурные скобки:
    %hash{'a'} = 'new value';
    Для добавления нового элемента хэша достаточно просто присвоить значение по новому ключу:
    %hash{'new elem'} = 255;
    Если в качестве ключа использовать число
    %hash{1000} = 20;
    то число конвертируется в строку, поэтому если в хэше уже есть элемент с ключем '1000', то при занесении значения по числовому ключу уже существующее значение перезапишется.
    Так же хэш можно конструировать из пар:

    $a = ('a'=>1);
    $b = ('b'=>2);
    %hash = $a, $b, 'c'=>3;
    

Контексты
Перейдем теперь к контекстам.
Perl 6 контекстно зависимый, это означает, что при различных условиях использования переменной, могут возвращаться различные значения. Прежде всего контекст определяется тем, какой именно переменной будет присваиваться значение:
$a =… — устанавливается скалярный контекст, @a =… устанавливается списочный контекст и т.д.
Имеются следующие контексты:

  • Скалярный контекст
    Скалярный контекст задается с помощью конструкции $(), где внутри скобок указывается выражение, результат которого будет интерпретироваться в скалярном контексте.
    При использовании массивов в скалярном контексте вместо самого массива возвращается ссылка на данный массив, которую уже можно использовать, как имя массива (Например использовать операцию получения элемента массива).
    При использование хэшей в скалярном контексте возвращается ссылка на хэш.
    Более конкретные виды контекстов это:

    • Числовой контекст
      Числовой контекст задается с помощью конструкции +(). Если в результате выражения, интерпретируемого в данном контексте, получается не числовой результат, то будет произведено приведение к числовому типу, и если в результате приведения появится ошибка, то работа скрипта останавливается.
    • Строковый контекст
      Строковый контекст задается с помощью конструкции ~(). Обычно к строковому типу можно привести любое значение, поэтому проблем с данным контекстом не предвидется
    • Логический контекст
      Логический контекст задается с помощью конструкции ?(). Результат выдается по правилам приведения к логическому типу.

  • Списочный контекст
    Списочный контекст задается с помощью конструкции @(). Пример создания массивов:

    @a1 = (1, 2);
    @a2 = (3, 4);
    @a3 = (5, 6);
    

    Однако если написать следующую строку

    @b = (@a1, @a2, @a3);

    то в результате, получится одномерный массив @b, который будет выглядить как (1, 2, 3, 4, 5, 6), а не двумерный массив, выглядящий как ([1, 2], [3, 4], [5, 6]). Получается это из-за того, что списочный контекст «сливает» все переданные ему массивы и хэши в один большой массив. Чтобы получить многомерный массив, необходимо передавать в конструктор не сами массивы, а ссылки на них:

    @b = (@a1, @a2, @a3)

    Если в списочном контексте используется скалярное значение, то в результате получается одноэлементный массив, состоящий из этого скаляра.
    Если в списочном контексте используется хэш, то в массив будут скопированы все ключи и значения из хэша в попарном виде:

    %a = (1=>'a', 2=>'b');
    @a = %a;  # (1, 'a', 2, 'b')
    

    Важно не путать, чем отличается результат выражения (1, 2) от результата выражения [1, 2]:
    в первом случае, получается массив. Если его присвоить скалярной переменной $a = (1, 2) то в итоге в этой переменной будет ссылка на массив, если написать @a = (1, 2) то @a будет массивом из двух элементов.
    Во втором случае, $a = [1, 2] так же будет содержать ссылку на массив, но @a = [1, 2] переменная @a будет массивом, состоящим из одного элемента — ссылки на массив (1, 2), т.к. ссылка является скалярным значением, которое и становится единственным элементом массива @a.

  • Контекст хэша
    В контексте хэша из скалярных переменных можно использовать только пары, или ссылки на массивы и хэши (про массивы ниже).
    Если в контексте хэша используется массив с нечетным количеством элементов, то скрипт останавливает работу, т.к. при конструировании хэша количество ключей и количество значений не будет совпадать.
    Если используется массив с четным количеством элементов, то в итоге получается хэш, в котором ключами являются четные элементы массива (под индексами 0, 2, 4), а значениями являются нечетные элементы массива (под индексами 1, 3, 5)
    Для задания контекста хэша используется конструкция %()
    В случае если написать

    $a = [1, 2];
    %a = $a;
    

    то в результате будет ошибка! — т.к. хэшу присваивается скалярное значение — ссылка, и получается, что количество ключей не соответсвует количеству значений.
    Если же написать:

    $a = [1, 2];
    %a = %($a);
    

    То получается цепочка: ссылка на массив $a используется в хэш-контексте, поэтому берется само значение массива. При приведении типа массива к типу хэша, создается новый хэш (1=>2), и он уже присваивается переменной %a;

Ну вот, на этом мой маленький опыт манипуляций с переменными пока что и ограничивается. Я надеюсь что вы сдесь увидели что-то новое для вас, и интересное. Удачного дня!

Автор: WarFair

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js