Переменная типа множество может содержать несколько значений базового типа. Чтобы сконструировать значение типа множество, используется конструкция вида
[список значений]
где в списке могут перечисляться через запятую либо выражения базового типа, либо (для порядковых типов) их диапазоны в виде a..b, где a и b - выражения базового типа. Например:
var
bs: ByteSet := [1,3,5,20..25];
fios: StringSet := ['Иванов','Петров','Сидорова'];
Значения в списке могут отсутствовать, тогда множество является пустым:
bs:=[];
Пустое множество совместимо по присваиванию с множеством любого типа.
Для множеств имеет место структурная эквивалентность типов.
Множества целых и множества на базе типа и его диапазонного подтипа или на базе двух диапазонных типов одного базового типа неявно преобразуются друг к другу. Если при присваивании s := s1 во множестве s1 содержатся элементы, которые не входят в диапазон значений базового типа для множества s, то они отсекаются.
Например:
var st: set of 3..9;
...
st := [1..5,8,10,12]; // в st попадут значения [3..5,8]
Операция in проверяет принадлежность элемента множеству:
if Wed in bestdays then ...
Для множеств определены операции + (объединение), - (разность), * (пересечение), = (равенство), <> (неравенство), <= (нестрогое вложение), < (строгое вложение), >= (нестрого содержит) и > (строго содержит).
Процедура write при выводе множества выводит все его элементы. Например,
write(['Иванов','Петров','Сидорова']);
выведет ['Иванов','Петров','Сидорова'], при этом данные, если это возможно, будут отсортированы по возрастанию.
Для перебора всех элементов множества можно использовать цикл foreach, данные перебираются в некотором внутреннем порядке:
foreach s: string in fios do
write(s,' ');
Для добавления элемента x к множеству s используется конструкция s += [x] или стандартная процедура Include: Include(s,x). Для удаления элемента x из множества s используется конструкция s -= [x] или стандартная процедура Exclude: Exclude(s,x).
Процедурный тип
Тип, предназначенный для хранения ссылок на процедуры или функции, называется процедурным, а переменная такого типа - процедурной переменной. Основное назначение процедурных переменных - хранение и косвенный вызов действий (функций) в ходе выполнения программы и передача их в качестве параметров.
Описание процедурного типа совпадает с заголовком соответствующей процедуры или функции без имени. Например:
type
ProcI = procedure (i: integer);
FunI = function (x,y: integer): integer;
Процедурной переменной можно присвоить процедуру или функцию с совместимым типом, например:
function Mult(x,y: integer): integer;
begin
Result := x*y;
end;
var f: FunI := Mult;
Процедурной переменной можно также присвоить лямбда-выражение с соответствующим количеством параметров и типом возвращаемого значения:
var f2: FunI := (x,y) -> x+2*y;
После этого можно вызвать процедуру или функцию через эту процедурную переменную, пользуясь обычным синтаксисом вызова:
write(f(2)); // 8
write(f2(3)); // 10
Для наиболее распространенных процедурных типов в системном модуле определен ряд синонимов. Приведем примеры с их использованием:
var f3: IntFunc := x -> 2*x-1;
var f4: Func<integer,real> := x -> 2.5*x;
var f3: Action<real> := x -> write(x,' ');
var pr: Predicate<string> := s -> s.Length>0;
Для процедурных типов определены также сокращенные конструкции:
() -> T; // функция без параметров, возвращающая T
T1 -> T; // функция c параметром T1, возвращающая T
(T1,T2) -> T // функция c параметрами T1 и T2, возвращающая T
(T1,T2,T3) -> T // функция c параметрами T1, T2 и T3, возвращающая T
и т.д.
() -> (); // процедура без параметров
T1 -> T; // процедура c параметром T1
(T1,T2) -> T // процедура c параметрами T1 и T2
(T1,T2,T3) -> T // процедура c параметрами T1, T2 и T3
и т.д.
Сокращенные конструкции не могут описывать процедурные переменные с параметрами, передаваемыми по ссылке.
Для процедурных переменных принята структурная эквивалентность типов: можно присваивать друг другу и передавать в качестве параметров процедурные переменные, совпадающие по структуре (типы и количество параметров, тип возвращаемого значения).
Обычно процедурные переменные передаются как параметры для реализации обратного вызова - вызова подпрограммы через процедурную переменную, переданную в качестве параметра в другую подпрограмму:
procedure forall(a: array of real; f: real->real);
begin
for var i := 0 to a.Length-1 do
a[i] := f(a[i]);
end;
...
forall(a,x->x*2); // умножение элементов массива на 2
forall(a,x->x+3); // увеличение элементов массива на 3
Процедурная переменная может хранить нулевое значение, которое задается константой nil. Вызов подпрограммы через нулевую процедурную переменную приводит к ошибке.
Процедурные переменные реализуются через делегаты .NET. Это означает, что они могут хранить несколько подпрограмм. Для добавления/отсоединения подпрограмм используются операторы += и -=:
p1 += mult2;
p1 += add3;
forall(a,p1);
Подпрограммы в этом случае вызываются в порядке прикрепления: вначале умножение, потом сложение.
Отсоединение неприкрепленных подпрограмм не выполняет никаких действий:
p1 -= print;
Кроме того, к процедурной переменной можно прикреплять/откреплять классовые и экземплярные методы классов. В последнем случае процедурная переменная в полях объекта запоминает некоторое состояние, которое меняется между вызовами метода, связанного с этой процедурной переменной.
type
A = class
x0: integer := 1;
h: integer := 2;
procedure PrintNext;
begin
Print(x0);
x0 *= h;
end;
end;
begin
var p: procedure;
var a1 := new A();
p := a1.PrintNext;
for var i:=1 to 10 do
p;
// 1 2 4 8 16 32 64 128 256 512
end.
Подобное поведение гораздо проще реализовать с помощью захвата переменной лямбда-выражением:
begin