Читать интересную книгу Описание языка PascalABC.NET - W Cat

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 23 24 25 26 27 28 29 30 31 ... 101

begin

Result := i;

exit;

end;

end;

var x: array of string;

begin

SetLength(x,4);

x[0] := 'Ваня';

x[1] := 'Коля';

x[2] := 'Сережа';

x[3] := 'Саша';

writeln(FindFirstInArray(x,'Сережа'));

end.

При вызове обобщенной подпрограммы тип-параметр обобщения можно не указывать, поскольку компилятор выводит типы параметров шаблона по типам фактических параметров. В данном случае после выведения получено: T=string.

При выведении требуется точное соответствие типов, приведение типов не допускается. Например, при компиляции следующего кода

...

var x: array of real;

begin

SetLength(x,3);

x[0] := 1;

x[1] := 2.71;

x[2] := 3.14;

writeln(FindFirstInArray(x,1));

end.

произойдет ошибка. Причина состоит в том, что первый параметр имеет тип array of real, а второй - тип integer, что не соответствует ни одному типу T в заголовке обобщенной функции. Для решения проблемы следует либо изменить тип второго параметра на real:

FindFirstInArray(x,1.0)

либо явно после имени функции в угловых скобках указать имя типа, которым параметризован данный вызов:

FindFirstInArray&<real>(x,1)

Использование знака & здесь обязательно, поскольку в противном случае компилятор трактует знак < как <меньше<.

Обобщёнными могут быть не только обычные подпрограммы, но и методы классов, а также методы другого обобщённого класса. Например:

type

Pair<T,Q> = class

first: T;

second: Q;

function ChangeSecond<S>(newval: S): Pair<T, S>;

end;

function Pair<T,Q>.ChangeSecond<S>(newval: S): Pair<T,S>;

begin

result := new Pair<T,S>;

result.first := first;

result.second := newval;

end;

var

x: Pair<integer,real>;

y: Pair<integer,string>;

begin

x := new Pair<integer,real>;

x.first := 3;

y := x.ChangeSecond('abc');

writeln(y.first, y.second);

end.

По окончании работы данная программа выведет 3abc.

Обобщенные подпрограммы в качестве параметров

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

Например, в классе System.Array имеется несколько статических обобщенных методов с обобщенными подпрограммами в качестве параметров. Так, System.Array.Find имеет следующий прототип:

System.Array.FindAll<T>(a: array of T; pred: Predicate<T>): array of T;

и возвращает подмассив массива a элементов T, удовлетворяющих условию pred.

Приведем пример вызова этой функции:

function f(x: integer): boolean;

begin

Result := ;

end;

var a := Seq(1,3,6,5,8);

var b := System.Array.FindAll(a,x -> x mod 2 = 0);

Здесь возвращается массив b, содержащий все четные значения массива a в том же порядке.

Ограничения на параметры обобщенных подпрограмм и классов

По умолчанию с переменными, имеющими тип параметра обобщенного класса или подпрограммы, внутри методов обобщённых классов и обобщенных подпрограмм можно делать лишь ограниченный набор действий: присваивать и сравнивать на равенство (отметим, что в NET сравнение на равенство внутри обобщений запрещено!).

Например, данный код будет работать:

function Eq<T>(a,b: T): boolean;

begin

Result := a = b;

end;

Можно также использовать присваивание переменной, имеющей тип параметра обобщенного класса или подпрограммы, значение по умолчанию, используя конструкцию default(T) - значение по умолчанию для типа T (nil для ссылочных типов и нулевое значение для размерных типов):

procedure Def<T>(var a: T);

begin

a := default(T);

end;

Однако, данный код

function Sum<T>(a,b: T): T;

begin

Result := a + b;

end;

вызовет ошибку компиляции до инстанцирования (создания экземпляра с конкретным типом). Такое поведение в .NET кардинально отличается от шаблонов в C++, где в коде шаблона можно использовать любые операции с шаблонными параметрами, и ошибка может произойти только в момент инстанцирования с конкретным типом.

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

type

MyPair<T> = class

where T: System.ICloneable;

private

x,y: T;

public

constructor (x,y: T);

begin

Self.x := x;

Self.y := y;

end;

function Clone: MyPair;

begin

Result := new MyPair<T>(x.Clone,y.Clone);

end;

end;

В секции where через запятую перечисляются следующие ограничения:

На 1 месте: слово class или слово record или имя класса-предка.

На 2 месте: список реализуемых интерфейсов через запятую.

На 3 месте: слово constructor, указывающее, что данный тип должен иметь конструктор по умолчанию.

При этом каждое из мест, кроме одного, может быть пустым.

Для каждого типа-параметра может быть своя секция where, каждая секция where завершается точкой с запятой.

Пример. Обобщенная функция поиска минимального элемента в массиве. Элементы должны реализовывать интерфейс IComparable<T>.

function MinElem<T>(a: array of T): T;

where T: IComparable<T>;

begin

var min: T := a[0];

for var i := 1 to a.Length-1 do

if min.CompareTo(a[i])<0 then

min := a[i];

Result := max;

end;

К сожалению, нет возможности использовать операцию <, поскольку операции не входят в интерфейсы.

Элементы функционального программирования

Лямбда-выражения

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

Здесь излагается полный синтаксис лямбда-выражений.

Здесь рассказывается о захвате лямбда-выражением переменных из внешнего контекста.

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

Синтаксис лямбда-выражений достаточно сложен и в данном пункте иллюстрируется на примерах.

Пример 1.

var f: integer -> integer := x -> x*x;

f(2);

Запись x -> x является лямбда-выражением, представляющем собой функцию с одним параметром x типа integer, возвращающую x*x типа integer. По данной записи компилятор генерирует следующий код:

function #fun1(x: integer): integer;

begin

Result := x*x;

end;

...

var f: integer -> integer := #fun1;

f(2);

Здесь #fun1 - это имя, генерируемое компилятором. Кроме того, код функции #fun1 также генерируется компилятором.

Пример 2. Фильтрация четных

Обычно лямбда-выражение передаётся как параметр подпрограммы. Например, в следующем коде

var a := Seq(3,2,4,8,5,5);

a.Where(x -> x mod 2 = 0).Print;

лямбда-выражение x -> x mod 2 = 0 задаёт условие отбора чётных чисел из массива a.

Пример 3. Сумма квадратов

var a := Seq(1,3,5);

writeln(a.Aggregate(0,(s,x)->s+x*x));

Иногда необходимо явно задавать тип параметров в лямбда-выражении.

Пример 4. Выбор перегруженной версии процедуры с параметром-лямбдой.

procedure p(f: integer -> integer);

begin

write(f(1));

end;

procedure p(f: real -> real);

begin

write(f(2.5));

end;

begin

p((x: real)->x*x);

end.

В данном примере вызов p(x -> x) вызовет ошибку компиляции, потому что компилятор не может выбрать, какую версию процедуры p выбирать. Задание типа параметра лямбды помогает устранить эту неоднозначность.

Пример 5. Лямбда-процедура.

procedure p(a: integer -> ());

begin

a(1)

end;

begin

p(procedure(x) -> write(x));

end.

Захват переменных в лямбда-выражении

Лямбда-выражение может использовать переменные из внешнего контекста. Такие переменные называются захваченными лямбда-выражением.

Пример 1. Захват переменной в запросе Select.

begin

var a := Seq(2,3,4);

var z := 1;

var q := a.Select(x->x+z);

q.Println;

z := 2;

q.Println;

end.

Здесь лямбда-выражение x->x+z захватывает внешнюю переменную z. Важно заметить, что при изменении значения переменной z запрос a.Select(x->x+z), хранящийся в переменной q, выполняется с новым значением z.

Пример 2. Накопление суммы во внешней переменной.

begin

var sum := 0;

var AddToSum: integer -> () := procedure (x) -> begin sum += x; end;

AddToSum(1);

AddToSum(3);

AddToSum(5);

1 ... 23 24 25 26 27 28 29 30 31 ... 101
На этом сайте Вы можете читать книги онлайн бесплатно русская версия Описание языка PascalABC.NET - W Cat.
Книги, аналогичгные Описание языка PascalABC.NET - W Cat

Оставить комментарий