November 8, 2023

Анонс обучающего курса на платформе Stepik. Часть 3

Часто приходится слышать как люди, увидев код PascalАВС.NЕТ в точечной нотации, говорят: "А, тут LINQ используется...". И это зачастую совершенно неверно. Вот пример кода с точечной нотацией, который не имеет к LINQ никакого отношения:

##
function Sum(Self: array of integer): integer; extensionmethod;
begin
  Result := 0;
  foreach var n in Self do
    Result += n
end;

function Println(Self: array of integer): array of integer; extensionmethod;
begin
  var n := Self.Length;
  Result := new integer[n];
  for var i := 0 to n - 2 do
  begin  
    Result[i] := Self[i];
    Write(Self[i], ' ')
  end;
  Result[n - 1] := Self[n - 1];
  Writeln(Self[n - 1])
end;

var n := ReadInteger;
var a := new integer[n];
a[0] := 1;
a[1] := 3;
for var i := 2 to n - 1 do
  a[i] := 3 * a[i - 2] + 5 * a[i - 1] - 4;
Write(a.Println.Sum)
{
Результат работы программы:
10
1 3 14 75 413 2286 12665 70179 388886 2154963
2629485
}

Так что же такое LINQ на самом деле?

LINQ - сокращенное название технологии Microsoft LINQ to Objects библиотек .NET Framework. для доступа к данным, расположенным в памяти компьютера. Сам термин LINQ - аббревиатура Language INtegrated Query (интегрированный язык запросов). Используя LINQ, можно обращаться к последовательностям, а также к коллекциям данных, например, к спискам List, как к таблицам базы данных.

Традиционно данные из последовательностей извлекались путем перебора всех элементов в цикле foreach. Использование LINQ предполагает создание запросов - фрагментов кода, описывающих данные, которые требуется извлечь без указания того, как это сделать.

Использование запросов LINQ дает следующие преимущества:

  • запросы короткие, их удобно читать и легко понимать;
  • запросы мощные, с их помощью можно производить сложную обработку данных;
  • запросы могут переноситься в другие источники данных практически без изменения (они одинаковы для последовательностей, списков и т.д.).

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

Технология LINQ включает около полусотни операций, разделяющихся на две группы - отложенные ("ленивые") операции и не отложенные операции (исполняются сразу). К группе не отложенных операций относятся получение свёрток (приведение данных к единственному значению - сумме, произведению, количеству и т.п.), выборка единственного элемента, преобразование к другому типу данных (последовательности в список List, массив или словарь), а также установление эквивалентности двух наборов данных.

Чтобы получить непосредственный доступ к методам LINQ, нужно либо подключить пространство имён System.Linq, либо указывать его при каждом вызове метода. Для последовательностей в .NET нужно обращаться к классу Enumerable.

## uses System.Linq;
var s := Enumerable.Range(-3, 8);
s.Println;                                // -3 -2 -1 0 1 2 3 4
s := Enumerable.Reverse(s);
s.Print                                   // 4 3 2 1 0 -1 -2 -3
##
var s := System.Linq.Enumerable.Range(-3, 8);
s.Println;                                // -3 -2 -1 0 1 2 3 4
s := System.Linq.Enumerable.Reverse(s);
s.Print                                   // 4 3 2 1 0 -1 -2 -3

Вызовы подобной длины приходится писать, например, в языке C#. Разработчики PascalАВС.NЕТ ввели множество дополнительных функций и методов расширения, позволяющих сделать работу с LINQ существенно удобнее.

##
var s := Range(-3, 4);
s.Println;                                // -3 -2 -1 0 1 2 3 4
s := s.Reverse;
s.Print                                   // 4 3 2 1 0 -1 -2 -3

Здесь Range - уже не метод из LINQ, а функция PascalАВС.NЕТ. Если в .NET-методе второй параметр обозначал длину генерируемой последовательности, то в функции это верхняя граница диапазона данных. Сама же функция находится в системной библиотеке PABCSystem и является "обёрткой" для вызова метода .NET:

function Range(a, b: integer): sequence of integer;
begin
  if b < a then 
    Result := System.Linq.Enumerable.Empty&<integer>
  else Result := System.Linq.Enumerable.Range(a, b - a + 1);
end;

В состав .NET LINQ входят 14 типов операций: агрегация, преобразование, конкатенация, элемент, множество, генерация, группирование, соединение, упорядочивание, проекция, разбиение, ограничение, квантификатор и эквивалентность.