PascalABC.NET материалы · PascalABC.NET материалы

Эта страница написана в формате сайта Learn X in Y minutes где X = PascalABC.NET.

PascalABC.NET — это элегантный и типобезопасный компилируемый язык программирования, основное предназначение которого — образовательная сфера и научные исследования. PascalABC.NET позволяет создавать надёжные приложения на платформе .NET и поддерживает мультипарадигменный подход, объединяя структурный, объектно-ориентированный и функциональный стили программирования.

// Однострочные комментарии начинаются с //
{
Многострочные комментарии 
выглядят следующим образом
}
(*
Многострочные комментарии 
можно также делать так
*)
// Простейшая программа 
begin
  Print('Привет мир')
end.
// Простейшая программа в режиме без внешнего begin-end
##
Print('Привет мир')
// Программа с вводом-выводом
begin
  var a := ReadInteger('Введите a:');
  var b := ReadInteger('Введите b:');
  var sum := a + b;
  Println('Сумма =', sum)
end.
// Вариант:
begin
  var (a,b) := ReadInteger2('Введите a,b:');
  Println($'{a} + {b} = {a + b}') // $'' - интерполированная строка
  Println($'{a} * {b} = {a * b}') // $'' - Println - для перехода на новую строку
end.
// Print или Write
Print(1,2) // 1 2 - разделяет данные пробелом
Write('a','b') // ab - не разделяет данные пробелом
// Print и Println, Write и Writeln
Print(1,2); // без перехода на новую строку
Println(1,2); // с переходом на новую строку
// Аналогично Write и Writeln
// Print для вывода составных структур
var d := Dict('abc' to [(1,2),(3,4)], 'def' to [(5,6),(7,8)]);
Print(d); // Вывод: {(abc,[(1,2),(3,4)]),(def,[(5,6),(7,8)])} 
// Кортежи и объекты классов и записей выводятся в (), 
// Массивы, списки и последовательности выводятся в []
// Множества и словари выводятся в []
// Print и вывод больших данных
Print(Arr(1..200)); 
// Вывод обрезается до 99 элементов: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,...] 
// Print и вывод рекурсивных данных
type 
  Node = auto class
    x: integer;
    next: Node;
  end;
begin
  var n := new Node(Random(100),nil);
  loop 10 do
    n := new Node(Random(100),n);
  Print(n);
end.
// Вывод: (42,(19,(40,(39,(63,(46,(43,(83,(66,(8,(75,nil)))))))))))
// Описание переменной с указанием типа
var x: integer;
// Описание переменной с указанием типа и инициализацией
var x: real := 2;
// Описание переменной с инициализацией и автовыводом типа
var s := 'Hello';
// Описание нескольких переменных с автовыводом типа
var (s1,s2) := ('Hello','World');
// Символы описываются в апострофах как и строки
var c: char := 'z';
// s получит тип string
var s := $'z';
// Константы описываются только до блока begin - end программы или подпрограммы
const Pi = 3.14;{Переопределение существующей константы.
Константа Pi уже описана в PascalABC.NET со значением Pi = 3.14159265358979}
const n: integer = 100;
// Переменные могут описываться до блока begin-end если используются во всей программе (редко)
var 
  i1,i2: integer;
  r: real;
begin
  // В PascalABC.NET предпочтительно переменные описывать внутри блока
  var k: real;
  var n: real := 10; // предпочтительно инициализировать переменную при описании
  // var r: string; - ошибка - внутриблочные переменные не могут иметь те же имена что и переменные верхнего уровня
end.
// Многострочные строки
var s := '''
  Многострочная строка
  Открывающие и закрывающие кавычки должны быть каждая на своей строке
  Лидирующие пробелы слева от закрывающих кавычек удаляются во всех строках многострочной строки
  Внутри могут быть апострофы ' и '' но не три апострофа подряд
  ''';
  
// Диапазоны и типы диапазонов
1..9       // диапазон целых IntRange
1.5 .. 2.8 // диапазон вещественных RealRange
'a'..'z'   // диапазон символов CharRange
// Операции in и not in для диапазонов
5 in 1..9
x not in a..b
c in 'a'..'z'

Основные типы данных, операции и выражения

[править]

// Целые знаковые типы
shortint (1 байт)
smallint (2 байт)
integer  (4 байт)
int64    (8 байт)
Константы: 2 -555 
// Целые беззнаковые типы
byte     (1 байт)
word     (2 байт)
longword (4 байт)
uint64   (8 байт)
Константы: 2 $FFFF (шестнадцатеричная)
// Операции для целых
7 div 2  // = 3 - целочисленное деление
7 mod 2  // = 1 - остаток от целочисленного деления
-7 mod 2 // = -1
5 shl 1 // = 10 - побитовый сдвиг влево
5 shr 1 // = 2 - побитовый сдвиг вправо
// Тип длинных целых
BigInteger
// Константы 
Константы BigInteger имеют суффикс bi: 1bi  
bi ** 10 // возведение в целочисленную степень
// Вещественные типы
real    (8 байт)
single  (4 байт)
decimal (16 байт) 
Константы: 2.5 1.7E-13
2.0 ** 10 // возведение в степень
// Операции для всех числовых типов
Математические: + - * /
Сравнения: = <> > < >= <=
// Логический тип
boolean
Константы: True False
// Операции для логических
and or xor (исключающее "или") not
// Символьный тип (двухбайтовый, хранит Unicode)
char
Константы: 'a' #10 
// Стандартные функции
Ord(c) // код символа c
c.Code // код символа c
Chr(i) // символ с кодом i
// Строковый тип
string
Константы: 'abc' $'a' 
// Перечислимый тип
type Days = (Mon,Tue,Wed,Thi,Fri,Sat,Sun);
var d: Days := Mon;
// Приведения базовых типов
var 
  i: integer;
  r: real;
  c: char;
  s: string;
begin
  r := i; // целые неявно преобразуются в вещественные
  s := c; // символьные неявно преобразуются в строковые
// Явные преобразования типов с помощью методов
  s := i.ToString;  
  i := s.ToInteger; // Может быть исключение
  s := r.ToString;
  r := s.ToReal;    // Может быть исключение
end.
// Методы Print и Println для вывода базовых типов
i.Print;
r.Print;
s.Print;
c.Print;
// Операции in и not in для диапазонов
5 in 1..9
x not in a..b
c in 'a'..'z'
// Тернарная операция в стиле C
var min := x < y ? x : y;
// Тернарная операция в стиле Pascal
var min := if x < y then x else y;
// Операция явного приведения типа
// var i: integer := integer(3.56); // Результат: 3 - дробная часть отбрасывается
var r := real(3); // Результат - вещественный
var p := new Person;
var s: Student := Student(p); // DownCast: Student - наследник Person

В стандартном модуле определен ряд функций и процедур. Здесь приводятся некоторые из них

Sqrt(x)
Abs(x)
Sin(x) Cos(x) Ln(x) Log10(x) ... // математические функции
Round(x) // Округление вещественного до ближайшего целого
Round(x,n) // Округление вещественного до ближайшего вещественного с n знаками после точки
Trunc(x) // Отбрасывание дробной части, результат - integer
Floor(x) // Наибольшее целое <= x
Ceil(x)  // Наименьшее целое >= x
Odd(x)   // True если x - нечетно
Even(x)  // True если x - четно
x.Odd    // True если x - нечетно
x.Even   // True если x - четно
Random(a,b) // случайное целое от a до b
RandomReal(a,b) // случайное целое в диапазоне [a,b)
Swap(x,y) // Поменять местами значения переменных x и y
Min(x1,x2,x3,...) // Минимальное из значений
Max(x1,x2,x3,...) // Максиимальное из значений

Стандартная библиотека .NET

[править]

PascalABC.NET имеет непосредственный доступ ко всем классам стандартной библиотеки .NET.

Библиотеки mscorlib.dll и System.Core.dll неявно подключаются к любой программе PascalABC.NET

// Пример 1
## Print(System.Environment.OSVersion); 
// или 
uses System;                 // Подключение пространства имен .NET
uses System.Threading.Tasks; // Подключение пространства имен .NET
begin
  Task.Run(() -> Print(1));
  Print(Environment.OSVersion);
end.
// Пример 2
begin
  var f := new System.IO.StreamWriter('a.txt');
  f.WriteLine('Hello PascalABC.NET!');
  f.Close;
end.
// Пример 3
uses System.Net;
begin
  var w := new WebClient();
  Println(w.DownloadString('http://example.com'));
end.
// Пример 4. Подключение дополнительной стандартной dll
{$reference 'System.Windows.Forms.dll'}
uses System.Windows.Forms;
begin
  Application.EnableVisualStyles();
  Application.SetCompatibleTextRenderingDefault(false);
  var form := new Form();
  Application.Run(form);
end.

PascalABC.NET имеет очевидную проекцию своих стандартных типов на типы .NET, что позволяет пользоваться едиными типами в программах, написанных на PascalABC.NET и C#.

Проекции типов PascalABC.NET на типы .NET
Тип PascalABC.NET Соответствующий тип .NET
integer System.Int32
longint System.Int32
int64 System.Int64
shortint System.SByte
smallint System.Int16
byte System.Byte
word System.UInt16
cardinal System.UInt32
uint64 System.UInt64
single System.Single
real System.Double
double System.Double
boolean System.Boolean
char System.Char
string System.String
object System.Object
// Присваивание
a := 5;
// Присваивание с распаковкой в переменные
(a,b) := (1,2); // из кортежа
(a,b) := ReadReal2; // из кортежа, возвращаемого функцией
(a,b,c) := (b,c,a);
(a[i],a[j]) := (a[j],a[i]);
(a,b) := arr; // из массива или другой последовательности
// Расширенное присваивание
a += 1;
a -= 1;
a *= 2;
x /= 2;
// Условный оператор
if x mod 2 = 0 then
  Print(x,'- четное')
else Print(x,'- нечетное') 
// Цепочечные условные операторы
if x < 0 then
  Print('отрицательное')
else if x = 0 then  
  Print('Равное 0')
else Print('Положительное')  
// Составной оператор begin .. end
if a > b then
begin
  var t := a;
  a := b;
  b := t
end;  
// Оператор выбора варианта
case word of
'собака': Translation := 'dog';
'кошка': Translation := 'cat';
'корова': Translation := 'cow';
else Translation := 'перевод отсутствует';
end;
// Оператор выбора варианта с диапазоном и перечислением
case Month of
12,1,2: Season := 'Зима';
3..5: Season := 'Весна';
6..8: Season := 'Лето';
9..11: Season := 'Осень';
end;
// Цикл loop - повторить n раз
loop 10 do
  Print(1);
  
// Цикл for со счетчиком
for var i:=1 to 10 do
  Print(i);
  
// Цикл for с шагом -1
// 10 9 8 7 6 5 4 3 2 1
for var i:=10 downto 1 do 
  Print(i);
  
// Цикл foreach (для каждого)
foreach var x in arr do
  Print(x);
  
// Цикл foreach с индексом  
foreach var x in arr index i do
  Println(i,x);
// Цикл for с шагом
// 1 3 5 7 9
for var i:=1 to 10 step 2 do
  Print(i);
// Цикл for с отрицательным шагом
// 10 8 6 4 2
for var i:=10 to 1 step -2 do 
  Print(i);
  
// Цикл while с предусловием (цикл ПОКА)
var x := 1;
while x < 10 do
begin
  Print(x);
  x += 1;
end;  
// Цикл repeat с постусловием (цикл ДО)
var x := 1;
repeat
  Print(x);
  x += 1;
until x >= 10;
// Оператор break досрочного завершения цикла
var sum := 0;
repeat
  var x := ReadInteger;
  if x = 0 then
    break;
  sum += x;
until False;  
// Оператор continue досрочного завершения текущей итерации цикла 
var sum := 0;
loop 10 do
begin
  var x := ReadInteger;
  if x <= 0 then
    continue
  sum += x;
end;
// Кортежи
var t: (string,integer) := ('Иванов',18);
// Доступ к полю кортежа на чтение
Println(t.Item1,t.Item2);
Println(t[0],t[1]);
// Кортежи неизменны. Если надо изменить поле кортежа, то надо конструировать новый кортеж
t := (t[0],19);
// Распаковка кортежа в переменные
var (name,age) := t; // name = 'Иванов', age = 18
// Динамические массивы 
var a: array of integer; // переменная типа "массив" - ссылка на данные
a := new integer[5];     // выделение памяти (заполняется нулями)
// Описание массива и выделение памяти
var a := new integer[5];
// Описание массива, выделение памяти и заполнение значениями
var a := [1,3,5,7,9];
// Автовывод типа массива
var a1 := [1,2,3.5]; // array of real
var a2 := ['z','t','Hello']; // array of string
var a3 := ['z',1]; // ошибка
// Возврат памяти, занимаемой массивом
a := nil; // Сборка мусора
// Вывод массива
Print(a); // [1,3,5,7,9]
a.Print;  // 1 3 5 7 9
// Создающая функция Arr
var a := Arr(1..9);
// Цикл по индексам массива
for var i := 0 to a.Length - 1 do
  a[i] += 1;
// Цикл по элементам массива
foreach var x in a do
  Print(x);
// Индексация массивов
a[0] // первый элемент
a[^1] // первый с конца
a[^i] // i-тый с конца
// Срезы массивов
a := [0,1,2,3,4,5];
a[2:5]  // 2 3 4
a[3:]   // 3 4 5
a[:3]   // 0 1 2
a[:]    // копия
a[1:^1] // 1 2 3 4
a[::-1] // 5 4 3 2 1 0
a[::2]  // 0 2 4
a[1::2] // 1 3 5
// Операция in
var a := [1,2,3,5];
Print(4 in a); // False
// Операции + и *
var a := [1,2,3];
var a1 := [4,5,6];
Print(a + a1); // [1,2,3,4,5,6]
Print(a * 2);  // [1,2,3,1,2,3]
// Сравнение массивов 
var a := Arr(1,2,3);
var a1 := Arr(1,2,3);
Print(a = a1); // False - сравниваются ссылки, они - разные
Print(a.ArrEqual(a1)); // True - сравниваются значения
// Цикл по массиву
for var i:=0 to a.Length - 1 do
  a[i] += 1;
foreach var x in a do
  Print(x);
  
// Статические массивы (устарело)
var a: array[1..10] of integer;
// Описание
var s: string; // При описании строка инициализируется пустой строкой ''
// Индексация
s[i] // i-тый символ строки
s[^i] // i-тый символ с конца
Строки по умолчанию индексируются с 1 (плохая традиция)
s[1] - первый символ
s[^1] - последний символ
В PascalABC.NET имеется директива компиляции, позволяющая строки индексировать с 0:
{$zerobasedstrings}
s[0] - первый символ
s[^1] - последний символ (не меняется!)
// Слияние строк
var s := 'Hello';
var s1 := 'World';
var s2 := s + ' ' + s1;
// Умножение строки на число
var s := 'ABC';
var s1 := s * 3; // ABCABCABC
// Операция in
var s := 'abracadabra';
Print('bra' in s); // True
// Сравнение строк
var s := 'abc';
var s1 := 'abc';
Print(s = s1); // True - строки сравниваются по значению
s := 'abc';
s1 := 'abba';
Print(s < s1); // False - строки сравниваются лексикографически (как в словаре) - мЕньшей является строка, которая первой идёт в словаре
// Срезы строк 
// Настройка {$zerobasedstrings} влияет на индексацию и здесь
// Будем рассматривать срезы со включенной опцией 
{$zerobasedstrings}
var s := 'abcdef';
s[1:3] // bc
s[:3]  // abc
s[3:]  // def
s[1:^1] // bcde
s[::-1] // fedcba - инвертирование строки
s[::2]  // ace
s[1::2] // bdf
// Циклы по строке
for var i:=1 to s.Length do
  Print(s[i]);
  
{$zerobasedstrings}
for var i:=0 to s.Length - 1 do
  Print(s[i]);
  
foreach var c in s do
  Print(c);
  
// Преобразования строка - целое
var s: string := '456';
var i: integer := 123;
s := i.ToString;
i := s.ToInteger; // возможно исключение
if s.TryToInteger(i) then // безопасное преобразование
  Print(i);  
// Преобразования строка - вещественное - аналогично
// Основные методы строк
// Вставка подстроки. Индексация в методах - с нуля
var s := 'Привет, мир!';
s := s.Insert(8, 'прекрасный '); 
Println(s); // Привет, прекрасный мир!
// Удаление подстроки. Индексация в методах - с нуля
s := s.Remove(8, 11); // Удаляет 11 символов начиная с 7-й позиции
Println(s); // Привет, мир!
// Разбиение строки на слова
var s := 'Привет, мир! Как дела?';
var words := s.ToWords(AllDelimiters); // Разбиение строки на слова. AllDelimiters - все возможные разделители. По умолчанию - ' '
Println(words); // ['Привет', 'мир', 'Как', 'дела']
// Замена подстроки
var s := 'Привет, мир!';
s := s.Replace('мир', 'Паскаль');
Println(s); // Привет, Паскаль!

StringBuilder - эффективно изменяемые строки

[править]

В .NET строки неизменяемы - нельзя в строке поменять символ. Это сделано для эффективности и безопасности в многопоточных приложениях.

Чтобы изменять символы строк, следует использовать тип StringBuilder (эффективно изменяемая строка)

{$zerobasedstrings}
var s := 'Table'; 
// s[0] := 'C'; // Нельзя - строка неизменяема
var sb: StringBuilder := s; // Неявное преобразование
sb[0] := 'C';
s := sb.ToString;

Тип StringBuilder позволяет также добавлять символы к строке, вставлять в середину строки и удалять

{$zerobasedstrings}
var s := 'car'; 
var sb: StringBuilder := s; 
sb.Append('d'); // card
s := sb.ToString;
sb := 'hat';
sb.Insert(1,'e'); // heat
s := sb.ToString;
Print(sb); 
sb := 'Planet';
sb.Remove(4,1); // Plant
s := sb.ToString;
// Многомерные динамические массивы
var a: array[,] of integer := new integer[3,4];
// Создающая функция для двумерного массива (матрицы)
var a := Matr([1,2,3,4],[5,6,7,8],[9,10,11,12]);
// Вывод двумерного массива
a.Print;
// Результат
   1   2   3   4
   5   6   7   8
   9  10  11  12
Print(a);
// Результат
[[1,2,3,4],[5,6,7,8],[9,10,11,12]]
// Срезы многомерных массивов
a[1:3,2:4]
// Результат
   7   8
  11  12  
a[:,1] // одномерный массив - 1 столбец
a[2,:] // одномерный массив - 2 строка
// Операция in
var a := Matr([1,2,3,4],[5,6,7,8],[9,10,11,12]);
Print(4 in a); // True
// Сравнение матриц
var a := Matr([1,2,3,4],[5,6,7,8],[9,10,11,12]);
var a1 := Matr([1,2,3,4],[5,6,7,8],[9,10,11,12]);
Print(a = a1); // False - сравниваются ссылки
Print(a.MatrEqual(a1)); // True - сравниваются элементы
 Методы матриц (двумерных массивов)
 var a := Matr([1,2,3,4],[5,6,7,8],[9,10,11,12]);
 a.RowCount // количество строк
 a.ColCount // количество столбцов
 a.Row(i)   // i-тая строка матрицы (одномерный массив)
 a.Col(j)   // j-тый столбец матрицы (одномерный массив)
// Создание типа записи
type PersonRec = record
  Name: string;
  Age: integer;
end;
// Пременная типа "запись"
var p: PersonRec; // Память выделена на программном стеке - конструктор вызывать не надо
p.Name := 'Иванов';
p.Age := 18;
// Присваивание и сравнение записей
var p1: PersonRec;
p1.Name := 'Иванов';
p1.Age := 18;
Print(p = p1); // True - сравниваются все поля
p := p1; // При присваивании копируются все поля
// Создание типа класса
type Person = class
  Name: string;
  Age: integer;
end;
// Классы - ссылочные данные
var p: Person;
p := new Person; // Для создания объекта класса требуется вызвать конструктор
p.Name := 'Иванов';
p.Age := 18;
// Присваивание и сравнение объектов классов
var p1: Person;
p1.Name := 'Иванов';
p1.Age := 18;
Print(p = p1); // False - сравниваются ссылки
p := p1; // При присваивании копируется ссылка
// Автоклассы - автоматически создают конструктор по полям
type Person = auto class
  Name: string;
  Age: integer;
end;
var p := new Person('Иванов',18);
// Освобождение памяти, занимаемой объектом
p := nil; // память под объект будет возвращена при сборке мусора

Список можно рассматривать как массив с эффективно изменяемым размером

// Описание
var L: List<integer>;
// Вызов конструктора
L := new List<integer>;
// Основные методы
L.Add(3); // Добавление в конец
L.AddRange([1,2,3,4,5]); // Добавление в конец
L.Insert(0,666); // Вставка в середину
L.RemoveAt(0);   // Удаление элемента в заданной позиции
L.Count          // Количество элементов
L.Clear;         // Очистка списка
// Создающая функция
L := Lst(1,2,3,4,5);
L := Lst(1..9);
// Операция in
var L := Lst(2..5);
Print(6 in L); // False
// Операции + и *
var L := Lst(1,2,3);
var L1 := Lst(4,5,6);
Print(L + L1); // [1,2,3,4,5,6]
Print(L * 2);  // [1,2,3,1,2,3]

Множество - неупорядоченный набор неповторяющихся значений. В PascalABC.NET имеется несколько типов множеств

Встроенные множества set of T

[править]

var s: set of integer := [1,3,5];
var s1 := SetOf(1,3,5); // создающая функция для множества
s1 := s; // копирует содержимое одного множества в другое. Встроенные множества - размерные!
s := []; // Пустое множество
// Автовывод типа для литералов множеств и массивов
var a := [1,2,3,4,5]; // массив
var s := [1..3,5]; // множество, т.к. в литерале присутствует диапазон
// Элементы во множествах не могут повторяться
var s: set of integer := [3,1,5,3,1];
s.Print
// Результат:
3 1 5
Print(s);
// Результат:
{3,1,5}
// Основные операции над множествами:
s.Count // Количество элементов
3 in s // Принадлежность элемента множеству. Работает за время O(1)
s.Add(7); // Добавление элемента к множеству. Работает за время O(1)
s.Remove(7); // Удаление элемента из множества. Работает за время O(1)
// Другие операции над множествами
var s := SetOf(1,2,3);
var s1 := SetOf(2,3,4);
s * s1 // Пересечение: 2 3
s + s1 // Объединение: 1 2 3 4
s - s1 // Разность: 1
SetOf(1,3) < s // Собственное вложение: True
SetOf(1,2,3) <= s // Несобственное вложение: True
s = s1 // Множества равны если количества элементов в них равны и все элементы совпадают
// Например, если s = SetOf(1,2,3), а s1 = SetOf(3,1,2), то s = s1
s <> s1
s > s1
s >= s1
// В других операциях с множествами один из операндов может быть массивом:
[1,3] < s
s = [1,3,2]
// Описание переменной
var s := new HashSet<integer>; // пока множество пустое
s := [1,2,3]; // множество заполняется значениями 1, 2, 3
s := HSet(1,2,3); // Создающая функция для HashSet
// или описание с инициализацией
var s: HashSet<integer> := [1,2,3];
// или с автовыводом типа
var s := HSet(1,2,3);
// Добавление значений
s.Add(5);
// Печать
s.Print;  // 1 2 3 5
Print(s); // {1,2,3,5}
// Все методы и операции, приведенные для set of T, работают и для HashSet
// В частности сравнение на равенство сравнивает все элементы независимо от порядка
var s := HSet(1,2,3);
Print(s = HSet(2,3,1)); // True
// Важное отличие HashSet от встроенных множеств: HashSet - ссылочный тип. То есть
var s1 := HSet(4,5,6);
s := s1; // присваиваются ссылки

Словарь - неупорядоченный набор пар вида (ключ,значение) с быстрым поиском по ключу

// Описание словаря
var d: Dictionary<string,integer>;   // переменная типа "словарь"
d := new Dictionary<string,integer>; // вызов конструктора словаря
// Описание словаря и выделение памяти
var d := new Dictionary<string,integer>;
// Создающая функция для словаря - Dict
// Создающая операция для пары (ключ,значение) - ключ to значение
var d := Dict('бегемот' to 2, 'крокодил' to 3);
var d1 := Dict(d); // копия словаря
var d2 := Dict(['бегемот','какаду'],[3,5]); // создание словаря заданием массивов ключей и значений
// Операции
d.Update(d1); // обновляет данные словаря d данными словаря d1
'бегемот' in d; // есть ли пара со значением ключа 'бегемот'
d.Contains('бегемот'); // синоним 'бегемот' in d
d += d1; // синоним d.Update(d1)
d + d1 // Слияние словарей. Данные d1 приоритетны
d -= [ключи] // Исключает пары, в которых ключи - из последовательности справа
d - [ключи] // возвращает новый словарь с исключенными из первого словаря парами с указанными значениями ключа
// Возврат памяти, занимаемой словарем
d := nil; // Сборка мусора
// Вывод словаря
Print(d); // {(бегемот,2),(крокодил,3)}
d.Print;  // (бегемот,2) (крокодил,3)
// Цикл по словарю
// kv - переменная типа KeyValuePair<ключ, значение> со свойствами Key и Value
foreach var kv in d do
  Println(kv.Key, kv.Value);
// Цикл по ключам словаря
foreach var k in d.Keys do
  Println(k, d[k]);
  var st := new Stack<Integer>; // Описание, вызов конструктора
  // Операции со стеком
  st.Push(10);  // Добавление элемента на вершину стека
  var removedItem := st.Pop();  // Удаление элемента с вершины и сохранение его в removedItem
  var topItem := st.Peek();  // Получение элемента на вершине без его удаления
  var count := st.Count;  // Получение количества элементов в стеке
  var isEmpty := st.IsEmpty;  // Проверка, пуст ли стек
var q := new Queue<Integer>; // Описание, вызов конструктора
// Операции с очередью
q.Enqueue(10);  // Добавление элемента в конец очереди
var removedItem := q.Dequeue();  // Удаление элемента из начала очереди и сохранение его в removedItem
var frontItem := q.Peek();  // Получение элемента в начале очереди без его удаления
var count := q.Count;  // Получение количества элементов в очереди
var isEmpty := q.IsEmpty;  // Проверка, пуста ли очередь
// Процедура с параметрами 
procedure PrintPerson(name: string; age: integer);
begin
  Println($'Имя: {name}   Возраст: {age}');
end;
// Короткий синтаксис процедур
procedure PrintPerson(name: string; age: integer) := Println($'Имя: {name}   Возраст: {age}');
// Функция
function Add(a,b: real): real;
begin
  Result := a + b;
end;
// Короткий синтаксис функций
function Add(a,b: real): real := a + b;
// передача параметров по ссылке - выходные параметры
procedure CalcSumProd(a,b: real; var sum,prod: real);
begin
  sum := a + b;
  prod := a * b;
end;
// Функция, возвращающая кортеж
function SumProd(a,b: real): (real,real) := (a + b, a * b);
...
var (s,p) := SumProd(2,3);
// Переменное количество параметров
function SumSquares(params a: array of real): real;
begin
  Result := 0;
  foreach var x in a do
    Result += x * x;
end;
SumSquares(1, 2, 3).Print; // Выведет 14
// Параметры по умолчанию
procedure Greet(name: string := 'Guest');
begin
  Println($'Hello, {name}!');
end;
Greet();          // Выведет 'Hello, Guest!'
Greet('Alice');   // Выведет 'Hello, Alice!'
procedure ShowInfo(name: string := 'Guest'; age: integer := 18);
begin
  Println($'Name: {name}, Age: {age}');
end;
// Вызов с использованием именованных аргументов:
ShowInfo(age := 25);          // Выведет: Name: Guest, Age: 25
ShowInfo(name := 'Alice');    // Выведет: Name: Alice, Age: 18
ShowInfo(age := 30, name := 'Bob'); // Можно в любом порядке
// Обобщенные параметры
procedure RotateRight<T>(arr: array of T);
begin
  var n := arr.Length;
  var last := arr[^1]; 
  for var i := n - 1 downto 1 do
    arr[i] := arr[i - 1]; 
  arr[0] := last; 
end;
// Передача записей по ссылке на const
type Person = record
  Name: string;
  Age: integer; 
end;
procedure PrintPerson(const p: Person);
begin
  Print(p.Name,p.Age); // запись p внутри процедуры менять нельзя
end;

Переменная, которая хранит ссылку на функцию, называется функциональной переменной (или процедурной переменной если хранит ссылку на процедуру). Функциональной переменной можно присвоить функцию с соответствующими параметрами и возвращаемым значением. Затем функциональную переменную можно использовать для вызова функции.

// Пример функции и соответствующей функциональной переменной
function f(x: real): real := x * x;
// Тип функциональной переменной: real -> real
var ff: real -> real := f;
ff(5).Print; // вызов функции через функциональную переменную
// Пример 2
function Mult(x,y: real): real := x * y;
var ff2: (real,real) -> real := Mult;
ff2(5,6).Print; // вызов функции через функциональную переменную
// Типы функциональных переменных
real -> real
(integer,integer) -> boolean
(string,integer) -> () // процедура с двумя параметрами
() -> () // процедура без параметров
procedure // синоним () -> ()
// Функциональные типы с развернутым описанием
type
  Proc = procedure(x,y: real; var a,b: real);
  Fun<T> = function(lst: List<T>; a,b: T): List<T>;

Функции могут быть переданы как параметры в другие функции и затем могут быть вызваны. Такой вызов называется Callback (обратный вызов), а сама функция - функцией обратного вызова.

// Описание функции с функциональным параметром condition
function Filter(lst: List<integer>; condition: integer -> boolean): List<integer>;
begin
  Result := new List<integer>;
  foreach var x in lst do
    if condition(x) then // обратный вызов
      Result.Add(x);
end;
// Функция, используемая в качестве функционального параметра
function Even(x: integer) := x mod 2 = 0;
// Вызов
var L := Lst(1..10);
Filter(L, Even).Print; // 2 4 6 8 10

Лямбда-выражение - это анонимная (безымянная) функция, которая определяется в месте своего использования. Лямбда-выражения можно присваивать функциональным переменным и передавать в другие функции как callback.

// Общая форма лямбда-выражения
параметры -> значение  // для лямбда-функций
параметры -> действие  // для лямбда-процедур
// Инициализация функциональной переменной лямбда-выражением
// Типы параметров лямбда-функции и тип возвращаемого значения автовыводятся из типа функциональной переменной
var f: real -> real := x -> x*x;
var pred: (real,real) -> boolean := (x,y) -> x < y;
var pr: integer -> () := n -> Print(n); 
var hello: procedure := procedure -> Print('Hello');
// Вызов через функциональную переменную
f(5).Print;
pred(3,2).Print;
pr(77);
hello;
// Передача лямбда-выражения как параметра функции
var a := Arr(1..10);
a.Select(x -> x * x).Println;      // лямбда-преобразование
a.Where(x -> x mod 2 = 0).Println; // лямбда-условие

Последовательность – это набор элементов, которые можно перебирать один за другим. Для перебора обычно используют цикл foreach или методы последовательностей.

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

Описание и стандартные генераторы

[править]

// Описание последовательности
var s: sequence of integer;
// Инициализация последовательности конкретными значениями
s := Seq(1,4,8);
// Инициализация последовательности элементами от 1 до 10
s := Range(1,10);
// Инициализация последовательности элементами от 1 до 10 с шагом 2
s := Range(1,10,2); // 1 3 5 7 9
// Инициализация последовательности с отрицательным шагом
s := Range(10,1,-2); // 10 8 6 4 2
// Генератор вещественной последовательности с шагом
var sr: sequence of real;
sr := Range(1,3,0.2); // 1 1.2 1.4 1.6 1.8 2 2.2 2.4 2.6 2.8 3
// Генератор вещественной последовательности на основе отрезка, разбитого на n частей
var sr: sequence of real;
sr := PartitionPoints(1,3,10); // 1 1.2 1.4 1.6 1.8 2 2.2 2.4 2.6 2.8 3
// Цикл по последовательности
foreach var x in s do
  Print(x);
  
// Распаковка последовательности в переменные
var s := Range(1,3,0.2);
var (a,b) := s; // a = 1, b = 1.2

Методы последовательностей

[править]

  
// Основные операции с числовыми последовательностями
s.Sum       // Сумма элементов
s.Average   // Среднее значение
s.Min       // Минимум
s.Max       // Максимум
// Первый, последний, количество
s.Count
s.First
s.Last
// Числовые характеристики
s.CountOf(3)            // Количество троек
s.Count(x -> x in 2..5) // Количество элементов в диапазоне 2..5
// Печать элементов через пробел
s.Print
s.Println
// Печать элементов - каждый на новой строке
s.PrintLines
// Выбор и пропуск элементов
s.Take(n) // возвращает последовательность из n первых элементов
s.Skip(n) // пропускает n первых элементов и возвращает оставшуюся последовательность 
s.TakeWhile(x -> x > 5) // Возвращает начальные элементы последовательности, большие 5
s.SkipWhile(x -> x > 5) // Пропускает начальные элементы последовательности, большие 5
s.TakeLast(n)           // Возвращает последовательность n последних элементов
s.SkipLast(n)           // Возвращает последовательность без n последних элементов
// Условия для последовательностей
s.All(x -> x > 3)   // Все больше 3?
s.Any(x -> x > 3)   // Есть ли элемент больше 3?
s.Contains(3)       // Содержит 3?
s.SequenceEqual(s1) // Равны ли последовательности
// Преобразование в другие структуры
s.ToArray    // В массив
s.ToList     // В список
s.ToSet      // В set of T
s.ToHashSet  // В HashSet
// Преобразование последовательности в последовательность
s.Where(x -> x > 3)  // возвращает последовательность элементов больше 3
s.Select(x -> x * x) // возвращает последовательность квадратов элементов
s.Order              // возвращает последовательность, отсортированную по возрастанию
s.OrderDescending    // возвращает последовательность, отсортированную по убыванию
s.Distinct           // возвращает последовательность неповторяющихся элементов
s.Reverse            // возвращает инвертированную последовательность
// Две и более последовательностей
var s := Seq(1,2,3);
var s1 := Seq(4,5,6);
s.Zip(s1) // объединяет две последовательности в последовательность кортежей: (1,4) (2,5) (3,6)
s.Zip(s1, (x,y) -> x * y) // объединяет две последовательности по заданному правилу: 4 10 18
Zip(s,s1,s,(x,y,z) -> x + y + z) // Внешняя Zip
s.Concat(s1) // слияние последовательностей: 1 2 3 4 5 6
s.Cartesian(s1) // декартово произведение последовательностей: (1,4) (1,5) (1,6) (2,4) (2,5) (2,6) (3,4) (3,5) (3,6)
s.Interleave(s1) // Чередует элементы двух последовательностей: 1 4 2 5 3 6
// Перестановки и сочетания
var s := 'abc'; // Строка - это последовательность символов
s.Permutations.Print; // Все перестановки: abc acb bac bca cab cba
s := 'abcd';
s.Permutations(2).Print; // Все частичные перестановки длины 2: ab ba ac ca ad da bc cb bd db cd dc
s.Combinations(2).Print; // Все сочетания по 2 элемента: ab ac ad bc bd cd
// Выполнение операции для всех элементов
var s := Seq(1,2,3,4);
s.ForEach(x -> Print(x)); // Печать каждого элемента
// Превращение в составную последовательность
var s := Seq(55,23,14,76,93,48); 
s.Numerate.Println; // Нумерует элементы последовательности, возвращая кортежи (номер, элемент)
  // Вывод: (1,55) (2,23) (3,14) (4,76) (5,93) (6,48)
s.Pairwise.Println; // возвращает последовательность кортежей пар соседних элементов
  // Вывод: (55,23) (23,14) (14,76) (76,93) (93,48)
s.NWise(3).Println; // Превращает последовательность в последовательность массивов, содержащих n соседних элементов (n=3)
  // Вывод: [55,23,14] [23,14,76] [14,76,93] [76,93,48]
s.Batch(3).Println; // разбивает последовательность на серии длины 3 (массивы)
  // Вывод: [55,23,14] [76,93,48]
// Flatten - преобразование последовательности последовательностей в плоскую последовательность
var aa := [[1,2,3,4],[5,6,7,8],[9,10,11,12]];
aa.Flatten.Print; // Flatten сливает внутренние последовательности в одну плоскую: 1 2 3 4 5 6 7 8 9 10 11 12
// Метод SelectMany 
// Метод SelectMany используется для проекции каждого элемента последовательности 
// в другую последовательность и затем объединяет все эти последовательности в одну
var listOfStrings := ['Hello world', 'Pascal is great', 'LINQ in action'];
listOfStrings.SelectMany(x -> x.ToWords).Print; // Hello world Pascal is great LINQ in action
// Группировка
var s := Seq(1,2,3,4,5,6,7,8,9);
s.GroupBy(x -> x mod 3).Print; // Разбиение на последовательность групп с одинаковым остатком при делении на 3
// Вывод: [1,4,7] [2,5,8] [3,6,9]
// Количество каждого элемента
var s := Seq(1,2,1,1,2,3);
s.EachCount.Print; // возвращает словарь пар (элемент,количество): (1,3) (2,2) (3,1)
// Диапазоны как последовательности
(1..9).Println // 1 2 3 4 5 6 7 8 9
// Сортировка по разным критериям
var words := ['cat', 'bread', 'chair', 'door', 'dog', 'school', 'yellow', 'apple', 'table', 'flower'];
words.OrderBy(word -> word.Length).ThenBy(word -> word).Println; // упорядочить вначале по длине, а при равной длине по алфавиту
// cat dog door apple bread chair table flower school yellow
// Числовые характеристики для нечисловых последовательностей
words.Sum(word -> word.Length).Print;     // Суммарная длина слов
words.Average(word -> word.Length).Print; // Средняя длина слов
words.Max(word -> word.Length).Print;     // Максимальная длина слова
words.Min(word -> word.Length).Print;     // Минимальная длина слова
// Цепочки методов. Методы можно объединять в цепочки
var a := Arr(9,7,2,1,8,5,6,7,1);
// Отфильтровать нечетные, убрать дубли, возвести в квадрат, отсортировать
a.Where(x -> x mod 2 <> 0).Println.Distinct.Println.Select(x -> x * x).Println.Order.Println;
// Вывод
9 7 1 5 7 1
9 7 1 5
81 49 1 25
1 25 49 81
// Вывод последовательности в файл
s.WriteLines(filename)

Пользовательские генераторы последовательностей

[править]

Генератор последовательностей в PascalABC.NET — это функция, которая возвращает элементы последовательности по одному, используя оператор yield. Генераторы позволяют создавать ленивые последовательности, где элементы вычисляются только по мере необходимости.

Оператор yield используется внутри генератора для возврата текущего элемента последовательности и приостановки выполнения функции до следующего запроса элемента. Выполнение продолжается с того места, где было остановлено.

// Функция - генератор последовательности
function GenerateEvenNumbers(n: integer): sequence of integer;
begin
  for var i := 0 to n do
    if i mod 2 = 0 then
      yield i;
end;
// Использование в цикле foreach
foreach var x in GenerateEvenNumbers(10) do
  Print(x);
// Вывод: 0 2 4 6 8 10
// Использование методов последовательностей
GenerateEvenNumbers(10).Println;

Оператор yield sequence используется внутри генератора для возврата подпоследовательности

function GenerateEvenNumbers(n: integer): sequence of integer;
begin
  for var i := 0 to n do
    if i mod 2 = 0 then
      yield i;
end;
function GenerateOddNumbers(n: integer): sequence of integer;
begin
  for var i := 0 to n do
    if i mod 2 <> 0 then
      yield i;
end;
// Функция с yield sequence
function GenerateSeq(n: integer): sequence of integer;
begin
  yield sequence GenerateEvenNumbers(n);
  yield 777;
  yield sequence GenerateOddNumbers(n);
end;
// Использование 
GenerateSeq(10).Println; // 0 2 4 6 8 10 777 1 3 5 7 9

Модули — это основные единицы компиляции, предназначенные для структурирования и логического разделения большой программы на части.

Модуль должен храниться в файле с тем же именем, что и имя модуля: MyUnit.pas

unit MyUnit;
 
interface // Раздел интерфейса: видимые извне объявления
 
type 
  int = integer; // Сокращение для типа integer
  double = real; // Сокращение для типа real
function SumSquares(a, b: int): int; // Объявление функции 
 
implementation // Раздел реализации: детали, скрытые от внешнего кода
 
function SumSquares(a, b: int): int := a * a + b * b; // Реализация функции
end.

Упрощенная структура модуля

[править]

unit MyUnit;
// Нет деления на интерфейс и реализацию. Удобно для небольших модулей
type 
  int = integer;
  double = real;
function SumSquares(a, b: int): int := a * a + b * b;
end.
uses MyUnit;
begin
  var a: int := 1;
  var b: int := 2;
  Print(SumSquares(a,b));
end.

Подключение пространств имен .NET

[править]

// Пространства имен .NET имеют тот же синтаксис подключения, что и модули
uses System.Net;
uses System.Text;
begin
  var w := new WebClient();
  w.Encoding := Encoding.UTF8;
  var s := w.DownloadString('http://pascalabc.net');
  Println(s);
end.

Библиотеки — это другой способ организации кода большой программы. Библиотеки содержат связанные классы и функции в одном файле, который компилируется в файл с расширением .dll, называемый сборкой.

Сборки можно затем подключать к программам, написанным на любом .NET-языке (в частности, к программам на C#)

Как и модуль, библиотека должна храниться в файле с тем же именем, что и имя библиотеки: MyLib.pas

library MyLib;
 
type 
  int = integer; // Сокращение для типа integer
  double = real; // Сокращение для типа real
function SumSquares(a, b: int): int := a * a + b * b;
end.

Сборки (откомпилированные библиотеки) подключаются к Pascal-программе при помощи специальной директивы компилятора {$reference MyLib.dll}

{$reference MyLib.dll}
begin
  var a: int := 3;
  var b: int := 4;
  SumSquares(a,b).Print;
end.

Сборка может быть написана и откомпилирована на любом .NET - языке.

Объектно-ориентированное программирование (ООП) — это парадигма программирования, основанная на представлении программы в виде набора объектов, которые взаимодействуют друг с другом. Каждый объект является экземпляром класса и объединяет данные (состояние) и методы (поведение), которые могут работать с этими данными.

type
  Point = class
  private // видимость только для членов класса и внутри модуля, где определен класс
    x,y: real;
  public
    // Конструктор - метод, создающий объект класса
    constructor Create(x,y: real); // Конструктор всегда имеет имя Create. Можно писать constructor (x,y: real);
    begin
      Self.x := x; // Self - ссылка на себя
      Self.y := y;
    end;
    procedure Print; // Метод класса 
    begin
      PABCSystem.Print($'({x},{y})'); // PABCSystem - стандартный модуль
    end;
    // Статический метод - вызывается через имя типа
    static function RandomPoint: Point;
    begin
      Result := new Point(Random(800),Random(600));
    end;
  end;
begin
  var p := new Point(2,3); // Вызов конструктора
    // Ссылочная объектная модель: переменная типа "класс" - это ссылка на объект, создаваемый конструктором
  p.Print;                 // (2,3) 
  Point.RandomPoint.Print; // (603,176) 
end.
// В автоклассах автоматически по полям генерируется конструктор и представление для вывода
type
  Point = auto class
    x,y: real;
  end;
begin
  var p := new Point(2,3);
  Print(p); // (2,3) 
end.

Свойства классов - это "умные поля". При доступе к ним на чтение и запись выполняются дополнительные действия.

Обычно свойства используются

  • либо когда надо сделать доступ только на чтение
  • либо когда при доступе на запись надо проверить диапазон значений
type
  Person = class
  private  
    fname: string;
    fage: integer;
    procedure SetAge(age: integer); // Логика доступа на запись
    begin
      if age < 0 then
        raise new System.ArgumentOutOfRangeException('Параметр должен быть >= 0');
      fage := age;
    end;
  public
    property Name: string read fname; // Name - только на чтение
    property Age: integer read fage write SetAge; 
  end;
  
begin
  var p := new Person('Ivsnov',20);
  //p.Name := 'Petrov'; // Ошибка - нет метода для записи
  p.Age := -1; // Исключение: Параметр должен быть >= 0
end.

Любой тип, в том числе стандартный, можно расширить новыми методами.

Метод расширения описывается как обычная функция с модификатором extensionmethod. Первый параметр должен иметь имя Self и тип, который мы хотим расширить данным методом.

function MultBy2(Self: integer): integer; extensionmethod;
begin
  Result := Self * 2;
end;
begin
  var i: integer:= 3;
  i.MultBy2().Print
end.
type
  Pair<T1, T2> = class
  public  
    auto property First: T1; // автосвойство создает внутреннее поле и методы на чтение и запись
    auto property Second: T2;
    constructor Create(f: T1; s: T2);
    begin
      First := f;
      Second := s;
    end;
  end;
begin
  var p := new Pair<integer, string>(1, 'Hello');
  Println($'{p.First}, {p.Second}'); // 1, Hello
end.

Класс можно унаследовать от другого класса - наследуются все поля, свойства и методы

type
  Figure = class
  public
    procedure PrintType := Println('Тип фигуры: ', TypeName(Self));
  end;
  // Определение производного класса Circle
  Circle = auto class(Figure)
  public
    x, y, radius: real;
    function Area: real := Pi * radius * radius;
  end;
  // Определение производного класса Rectangle
  Rectangle = auto class(Figure)
  public
    x, y: real;
    width, height: real;
    function Area: real := width * height;
  end;
begin
  var c := new Circle(0, 0, 5);
  c.PrintType; // Вывод: Тип фигуры: Circle
  Println('Площадь окружности: ', c.Area); // Вывод: Площадь окружности: 78.54
  var r := new Rectangle(1, 1, 4, 6);
  r.PrintType; // Вывод: Тип фигуры: Rectangle
  Println('Площадь прямоугольника: ', r.Area); // Вывод: Площадь прямоугольника: 24
end.

Полиморфизм - способность объектов разных типов, связанных иерархией наследования, реализовывать одни и те же действия по разному (полиморфизм - много форм). Полиморфизм реализуется через виртуальные функции

type
  Animal = class
    procedure Speak; virtual := Println('');
  end;
  Dog = class(Animal)
    procedure Speak; override := Println('Gaw gaw!'); // в производных классах - override
  end;
  Cat = class(Animal)
    procedure Speak; override := Println('Mia-ay!');
  end;
begin
  var a: Animal := new Dog;
  a.Speak; // Gaw gaw!
  a := new Cat;
  a.Speak; // Mia-ay!
end.

Интерфейс – набор требований, которым должен удовлетворять класс. В интерфейсе содержатся заголовки методов и свойств.

// Определение интерфейса
type
  IAnimal = interface
    procedure Speak;
    function GetName: string;
  end;
// Классы, реализующие интерфейсы
  Dog = class(IAnimal)
  public // обязательно публичным образом
    function GetName: string := 'Dog';
    procedure Speak := Println('Woof');
  end;
  Cat = class(IAnimal)
  public 
    function GetName: string := 'Cat';
    procedure Speak := Println('Meow');
  end;  
// Использование интерфейсов
procedure MakeSound(animal: IAnimal);
begin
  Println('Animal: ' + animal.GetName);
  animal.Speak;
end;
begin
  var d: IAnimal := new Dog;
  var c: IAnimal := new Cat;
  MakeSound(d); 
  MakeSound(c); 
end.

Ограничения на параметры обобщений

[править]

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

// Общий пример
function DoSomething<T>: T; where T: class, constructor, IComparable<T>;
begin
  // ...  
end;
// Виды ограничений:
where T: class;         // тип должен быть ссылочным
where T: record;        // тип должен быть размерным
where T: constructor;   // тип должен иметь конструктор по умолчанию
where T: IComparable<T> // тип должен реализовывать интерфейс
where T: Person;        // тип должен наследоваться от определенного базового класса
// Пример
function Min<T>(a: array of T): T; where T: IComparable<T>;
begin
  Result := a[0];
  for var i:=1 to a.Length - 1 do
    if a[i].CompareTo(Result) < 0 then
      Result := a[i];
end;

Все исключения наследуются от общего класса System.Exception.

// Обработка исключений в блоке try/except
try
  var i := ReadInteger; // Неверный ввод вызовет исключение
  var x := 10 div i;    // Деление на ноль вызовет исключение
except
  on e: System.DivideByZeroException do
    Println('Ошибка: ', e.Message);
  on e: System.FormatException do
    Println('Ошибка: ', e.Message);
end;
// Блок try..finally
procedure p;
begin
  var f := OpenRead('a.txt');
  try
    var i := f.ReadInteger;
  finally
    f.Close; // файл закрывается независимо от того, произошло исключение или нет
  end;
end;

Идентификация типов во время выполнения -

[править]

// Операции as, is
type
  Person = auto class
    Name: string;
  end;
  
  Student = class(Person)
    Group: string;
    constructor Create(name, group: string);
    begin
      inherited Create(name);
      Self.Group := group;
    end;
  end;
begin
  var p: Person := new Student('Alice', 'CS-101');
  if p is Student then // Является ли объект p экземпляром класса Student
    Student(p).Group := 'CS-102'; // Приводим объект p к типу Student и изменяем его группу
  
  
  var s := p as Student; // Приводим объект p к типу Student
  if s <> nil then       // Если приведение успешно, изменяем группу
    s.Group := 'CS-102';
  
  // Используем расширенную форму is
  // Если p это Student, то s1 - переменная типа Student
  if p is Student (var s1) then
    s1.Group := 'CS-102';
end.
// Метод GetType
Метод GetType позволяет получить информацию о типе объекта во время выполнения программы:
var p: Person := new Student('Alice', 'CS-101');
  
if p.GetType = typeof(Student) then
  Student(p).Group := 'CS-102';
// Типы, допускающие значение nil
var i: integer?; // i может принимать все целые значения и nil
i := nil;
i := 10;
if i = nil then 
  Print('Nullable');
if i = 10 then 
  i := i.Value + 5;
  
// Операция ?.
var a: array of integer;
Print(a?.Length); // nil вместо исключения
a := new integer[3];
Print(a?.Length); // 3
// Операция ??
// x ?? y - возвращает x, а если x возвращает nil, то возвращает y
var a: array of integer;
Print(a?.Length ?? 0); // 0
a := new integer[3];
Print(a?.Length);      // 3

Read the original on pascalabc.miraheze.org ↗