Text
                    Рубанцев ВалерийПрограммирование в среде PascalABC.NET
Функциональное программирование на паскале

Валерий РубанцевФункциональное
программирование напаскале2)
От автораМногие современные языки программирования поддерживают несколько пара¬
дигм, почему и называются мультипарадигменными. Например, на C++, Си-шарпе,
Яве, Питоне, паскале PascalABC.NET можно писать программы в процедурном,
объектно-ориентированном и функциональном стиле.Функциональное программирование насчитывает уже несколько десятилетий, но
только сравнительно недавно стало одной из ведущих парадигм программирова¬
ния.Программы, написанные в функциональном стиле более надёжные, короткие и
понятные. Их проще писать и отлаживать. Они превращают императивное про¬
граммирование в декларативное. Программы, написанные в императивном
стиле, показывают, как нужно что-то сделать. А программы, написанные в декла¬
ративном стиле, показывают, что мы хотим сделать. Декларативное программи¬
рование помогает избежать множества мелких подробностей. Например, методы
расширения для последовательностей OrderBy и OrderByDescebding умеют сорти¬
ровать элементы по заданному условию. Теперь нет необходимости подробно
расписывать функции сортировки. Весь код занимает единственную строку в про¬
грамме.ФункцииФункции имеются во многих языках программирования, в том числе и в паскале.
Однако функции в функциональном программировании существенно отличаются
от них и имеют свойства математических функций.Определение функции в паскале имеет вид:function имя(список формальных параметров): тип возвращаемого значения;Простейшая функция Summa, которая вычисляет и возвращает сумму двух целых
чисел может быть такой:
function Summa(a,b : integer): integer;
beginResult := a + b;
end;При вызове функции вместо формальных параметров подставляются фактические
параметры, или аргументы:і var sum:= Summa(5,7);Печатаем результат, возвращаемый функцией:
і Println('Сумма чисел = ', sum);■ Функции—□XАФункцииСумма чисел = 12VСколько бы раз вы ни вызывали функцию Summa с этими аргументами, вы всегда
будете получать число 12.Математические функции действуют точно так же. Если они получают одни и те
же аргументы, то всегда возвращают один и тот же результат.Аргументами могут быть не только литералы, но и переменные совместимого
типа:var a:= 5;
var b:= 7;var sum:= Summa(a,b);Println('Сумма чисел = ', sum);Println('Переменная a = ', a);Println('Переменная b = ', b);Println;
После вызова функции Summa значения переменных не изменились:■ Функции — □ XФунтцииСунна чисел =12Переменная а =БПеременная Ь =7Функция Summa получает значения переменных, но не сами эти переменные. В
теле функции создаются локальные переменные с именами параметров, которые
получают переданные аргументы. Внутри функции можно как угодно изменять
значения этих переменных, и при этом значения внешних переменных не изме¬
нятся:function Summa(a,b : integer): integer;begina := b;Result := a + b;end;Внутри функции переменная а получила значение переменной b, то есть 7, по¬
этому функция вернула число 14. Однако внешние переменные а и b остались
неизменными:■ Функции — □ XФункцииСумма чисел =14Переменная а =5Переменная Ь =7Но мы можем передать параметр по ссылке. Для этого перед идентификатором
параметра нужно поставить ключевое слово var. Во второй версии нашей функции
пусть будут параметры-переменные:
function Summa2(var a,b : integer): integer;
begina := b;
b:= 10;Result := a + b;end;Теперь при вызове функция получает не значения локальных переменных, а
внешние переменные а и b:var a:= 5;
var b:= 7;Println('Переменная a = ', a);Println('Переменная b = ', b);
var sum:= Summa2(a,b);Println('Сумма чисел = ', sum);Println('Переменная a = ', a);Println('Переменная b = ', b);Как вы видите на рисунке, значения внешних переменных изменились:ill Функции — □ XФунКЦИИПеременная a =БПеременная Ь =7Сумма чисел =17Переменная а =7Переменная Ь =10Внешние переменные могут называться иначе, чем параметры функции, но ре¬
зультат будет точно таким же:var m:= 5;
var n:= 7;Println('Переменная m = ', m);Println('Переменная n = ', n);
var sum:= Summa2(m,n);
IPrintln('Сумма чисел = ', sum);Println('Переменная m = ', m);Println('Переменная n = ', n);Итак, функция Summa2 изменила значения внешних переменных. Это действие
называется побочным эффектом. Функции, не имеющие побочного эффекта, назы¬
ваются чистыми. Все математические функции относятся к чистым.В большой программе может быть несколько функций, которые изменяют значе¬
ния внешних переменных. Работу таких программ очень сложно контролировать,
поскольку любая из этих функций может работать неверно.Функция может вообще не иметь параметров и использовать глобальные пере¬
менные как аргументы.Объявим 2 глобальные переменные:
і var ga, gb: integer;В главном блоке присвоим этим переменным значения и напечатаем их:ga:= 5;
gb:= 7;Println('Переменная ga = ', ga);Println('Переменная gb = ', gb);■ Функции—□XФункіцииАПеременная ga = Б
Переменная gb = 7VПишем новую функцию:I function Summa3(): integer;
begin
I Result := ga + gb;
end;Она не имеет параметров, а для вычисления значения использует глобальные пе¬
ременные программы.Мы присвоили глобальным переменным значения 5 и 7, так что сумму наша функ¬
ция вычислила верно:■ Функции—□XФункцииЛПеременная ga = Б
Переменная gb = 7
Сумма чисел = 12VТак как значения глобальных переменных можно изменить в главном блоке про¬
граммы и в любой функции, то функция Summa3 вполне вероятно будет возвра¬
щать различные значения при нескольких вызовах. Такое поведение функций в
функциональном программировании не допускается.В функции Summa4 мы можем изменить значения глобальных переменных:function Summa4(): integer;
beginga := gb;
gb:= 10;Result := ga + gb;end;Как вы помните, до вызова этой функции они имели значения 5 и 7, а после вы¬
зова - 7 и 10:Ivar sum:= Summa4();Println('Сумма чисел = ', sum);Println('Переменная ga = ', ga);
I Println('Переменная gb = ', gb);■ Функции□ XВ чистых функциях нельзя использовать изменяемые значения. Например, ввод
данных пользователя или показания системных часов. Понятно, что в таких слу¬
чаях возвращаемое значение может изменяться при каждом вызове функции.В функциональном программировании уже существующие данные не изменя¬
ются! К ним либо добавляются данные, либо создаются новые. При каждом изме¬
нении объекта программы создаётся новый экземпляр. К примеру, в паскале
нельзя изменить последовательность. Всякий раз будет создаваться новая после¬
довательность, которая может включать элементы исходной последовательно¬
сти. Поэтому в функциональном программировании все переменные могут
только единственный раз получить значение, то есть должны быть константами.В мультипарадигменных языках программирования, к которым относится и пас¬
каль, невозможно использовать только чистые функции, но вполне можно про¬
граммировать в функциональном стиле.Функции высших порядков - это функции, которые могут принимать другие функ¬
ции как аргументы.В паскале в функции можно передавать переменные процедурных типов:Функции высших порядков|type///объявление процедурного типа:
I FiboFact = function(n: integer): decimal;function GetFF(ff: FiboFact; n: integer): decimal;
beginResult:= ff(n);end;Println('Факториал числа 27 равен ', GetFF(Factorial, 27));
Println('Число Фибоначчи 139 равно ', GetFF(Fibonacci, 139));Или непосредственно ссылки на функции:
і lstInt := BubbleSort(lstNum, CompareLen&<integer>);Если код функции короткий, то её можно определить непосредственно при вы¬
зове функции высшего порядка:lstNum.Sort(function(n1, n2) ->
beginResult := n1 - n2;end);Такие функции называются анонимными.Для упрощения кода анонимные функции часто заменяют лямбда-выражениями:
і lstNum.Sort((n1, n2) -> n1 - n2);Лямбда-выражения - это реализация математических функций в программирова¬
нии.
В книге подробно, с многочисленными примерами, рассказывается:• о процедурном типе в паскале• об анонимных функциях• о лямбда-выражениях• о генерировании последовательностей• об языке интегрированных запросов LINQ• обо всех встроенных методах расширения для последовательностей• обо всех дополнительных методах расширения в языке PascalABC.NET• о разработке собственных методов расширенияКнига адресуется: школьникам, изучающим PascalABC.NET на уроках или самосто¬
ятельно, учителям информатики, любителям программирования.Валерий Рубанцев
Условные обозначения, принятые в книге:IIДополнение или замечаниеТребование или указаниеИсходный кодЗадание для самостоятельного решенияЗаголовок проекта:ПроектИсходные коды всех проектов находятся в папке _Projects'О
ОглавлениеФункциональное программирование на паскале 2От автора 3Функции 3Функции высших порядков 9Оглавление 13
Процедурный тип 17Описание процедурного типа 17Объявление и создание процедурной переменной 20Вызов функции через процедурную переменную 25Список вызовов 25Обобщённые процедурные переменные 35Обобщённые процедурные типы Action<T> и Func<T> 38Синонимы для процедурных типов 43Процедурные переменные как параметры 44Анонимные функции 55
Лямбда-выражения 65
Последовательности 72Язык интегрированных запросов LINQ 73Операторы запросов LINQ 74Генерирование последовательностей 77Функция Range 77Методы расширения Println и Print 80Функция Range (продолжение) 82Функция Range и циклы 86Методы расширения типа integer 88Ѳ
Функции Seq 93Функция Partition 101Ввод последовательностей с клавиатуры 102
Операции над последовательностями 104Оператор + и метод Concat 104Оператор * 107Встроенные методы расширения для последовательностей 109Метод Where 109Методы Count и LongCount 146Метод Sum 153Метод Average 161Методы Min и Max 162Методы First и FirstOrDefault 164Методы Last и LastOrDefault 176Методы ElementAt и ElementAtOrDefault 177Метод Reverse 196Методы OrderBy и OrderByDescending 196Методы ThenBy и ThenByDescending 200Методы Take и TakeWhile 202Методы Skip и SkipWhile 216Метод Select 229Метод SelectMany 247Метод Zip 254Метод Distinct 259Метод Union 261Метод Intersect 262Метод Except 263Метод Contains 264Методы Single и SingleOrDefault 265Метод Any 270Метод All 272")
Метод DefaultIfEmpty 273Метод SequenceEqual 274Метод OfType 275Метод Cast 276Метод ToArray 277Метод ToList 277Метод ToDictionary 278Метод ToLookup 281Метод AsEnumerable 283Метод Aggregate 284Метод Join 288Метод GroupJoin 293Метод GroupBy 296Дополнительные методы расширения для последовательностей 304Методы Print и Println 304Метод ReadLines 304Метод WriteLines 305Метод ForEach 306Метод Batch 308Метод Pairwise 309Метод Partition 310Метод SplitAt 312Метод Cartesian 312Метод Incremental 321Метод Interleave 322Метод JoinIntoString 324Методы LastMaxBy и LastMinBy 328Методы MaxBy и MinBy 329Метод Numerate 330Методы Print и Println 331Метод SkipLast 331Метод Slice 332Методы Sorted и SortedDescending 333Метод Tabulate 33415
Метод TakeLast 335Метод ToHashSet 335Метод ToSortedSet 335Метод ToLinkedList 336Метод ZipTuple 337Метод UnZipTuple 339Задания для самостоятельного решения 342Собственные методы расширения для последовательностей 343Метод RandomElement 343Метод SubSequence 345Метод RandomSubSequence 346Метод Shuffle 347Модуль OlympUnit 348OlympUnit 348")
Процедурный типПроцедурные типы - это ссылочные типы, определяемые пользователем. Пере¬
менная процедурного типа (процедурная переменная) хранит ссылку на функ¬
цию, процедуру, метод класса, экземпляра класса или структуры, то есть их
начальный адрес в памяти. Множественные процедурные переменные содержат
целый список вызовов, в котором записаны ссылки на методы.Образно процедурные переменные можно назвать представителями (в языке Си-
шарп они называются делегатами) метода или методов в программе. При этом
процедурная переменная предъявляет к методам определённые требования:
они должны иметь тот же тип возвращаемого значения и такую же сигнатуру.Чтобы пользоваться процедурной переменной в своей программе, вы должны:• Объявить процедурный тип• Объявить переменную процедурного типа и присвоить ей значение• Вызвать процедуру, функцию или метод через процедурную переменнуюОписание процедурного типаПеред использованием процедурный тип должен быть объявлен:
typeимя_типа = procedure | ^п^іоп(список параметров): typeRet;Здесь:• typeRet - тип возвращаемого значения
Объявление процедурного типа можно сравнить с заголовком процедуры/функ¬
ции: оно также содержит тип возвращаемого значение и список формальных пара¬
метров (сигнатуру).Тип возвращаемого значения и сигнатура процедурного типа задают «формат»
процедур/функций, на которые он может ссылаться.Чтобы яснее понять работу с процедурными переменными, напишем новый про¬
ект. Пусть нам нужны факториалы и числа Фибоначчи. Функции для их вычисления
могут быть такими:// ФАКТОРИАЛfunction Factorial(n: integer): decimal;beginif (n < 1) then
beginResult := 1;
exit;
end;var res: decimal := 1;
for var i := 2 to n do
begin
tryres := res * i;
exceptPrintln('Слишком большое число!');Result := 0;exit;end;end;Result := res;Процедурный типend;// ЧИСЛО ФИБОНАЧЧИ
function Fibonacci(n: integer): decimal;
begin
if (n <= 2) then
beginResult := 1;
exit;
end;var x: decimal := 1;
var y: decimal := 1;
var res: decimal := 0;
for var i := 3 to n do
begin
tryres := x + y;
x := y;
y := res;
exceptPrintln('Слишком большое число!');Result := 0;
exit;
end;
end;Result := res;
end;Обе функции возвращают значение типа decimal и имеют единственный параметр
типа integer. Совершенно очевидно, что процедурный тип для этих функций также
должен иметь:• тип возвращаемого значения - decimal• единственный параметр типа integerСамое трудное в объявлении процедурного типа - придумать для него красивый
идентификатор:I// процедурный тип:type FiboFact = function(n: integer): decimal;
Объявление и создание процедурной перемен¬
нойПосле описания процедурного типа вы можете объявить переменную этого типа
и инициализировать её.Объявление процедурной переменной ничем не отличается от объявления пере¬
менных других типов: сначала указывается идентификатор переменной, а затем
процедурный тип:var имя_переменной : ProcType;Поскольку процедурный тип - это ссылочный тип, то переменная после её объяв¬
ления имеет значение nil, то есть не ссылается ни на один объект.Вернёмся в наш проект и объявим переменную:I// объявляем переменную процедурного типа:
var fifa: FiboFact;Пока этой переменной не присвоено значение, пользоваться ею нельзя. Поэтому
мы должны присвоить ей значение подходящего типа. Например, ссылку на функ¬
цию Factorial:I// присваиваем ей значение:
fifa := Factorial;С тем же успехом мы могли бы инициализировать её функцией Fibonacci.Как обычно, объявление переменной можно сочетать с её инициализацией:I// создаём переменную процедурного типа:
var fifa2: FiboFact := Fibonacci;
После создания процедурной переменной ей можно присвоить новое значение
подходящего типа:і fifa := Fibonacci;Когда процедурная переменная получает значение, она ссылается на проце¬
дуру/функцию, то есть, по сути, процедурная переменная - это лишь псевдоним
(или «представитель») процедуры/функции, имеющей сигнатуру, которую опре¬
деляет процедурный тип.Такое образное определение процедурной переменной верно, только когда
она ссылается на единственную процедуру/функцию. Как вы увидите дальше,
процедурная переменная содержит список вызовов процедур/функций и то¬
гда представляет сразу несколько процедур/функций.Процедура/функция, на которую ссылается процедурная переменная, может
быть также методом класса и методом экземпляра класса. Метод класса указыва¬
ется после имени класса, а метод экземпляра класса - после имени объекта.Чтобы присвоить процедурной переменной ссылку на методы, опишем 2 новых
класса с методами Factorial и Fibonacci:typeFF = class
public// ФАКТОРИАЛclass function Factorial(n: integer): decimal;
beginif (n < 1) then
beginResult := 1;
exit;
end;var res: decimal := 1;
for var i := 2 to n do
begin
tryres := res * i;
exceptPrintln('Слишком большое число!');
Result := 0;
exit;
end;
end;Result := res;
end;// ЧИСЛО ФИБОНАЧЧИclass function Fibonacci(n: integer): decimal
beginif (n <= 2) then
beginResult := 1;
exit;
end;var x: decimal := 1;
var y: decimal := 1;
var res: decimal := 0;
for var i := 3 to n do
begin
tryres := x + y;
x := y;
y := res;
exceptPrintln('Слишком большое число!');
Result := 0;
exit;
end;
end;Result := res;
end;end;FF2 = class
public// ФАКТОРИАЛfunction Factorial(n: integer): decimal;
beginif (n < 1) then
begin
Result := 1;
exit;
end;var res: decimal := 1;
for var i := 2 to n do
begin
tryres := res * i;
exceptPrintln('Слишком большое число
Result := 0;
exit;
end;
end;Result := res;
end;// ЧИСЛО ФИБОНАЧЧИfunction Fibonacci(n: integer): decimal
beginif (n <= 2) then
beginResult := 1;
exit;
end;var x: decimal := 1;
var y: decimal := 1;
var res: decimal := 0;
for var i := 3 to n do
begin
tryres := x + y;
x := y;
y := res;
exceptPrintln('Слишком большое число
Result := 0;
exit;
end;
end;Result := res;
end;
end;
Методы класса FF принадлежат классу, а методы класса FF2 - объекту.Метод класса можно присвоить процедурной переменной без создания объекта:III метод класса:
fifa := FF.Factorial;Println(fifa(27));А теперь создадим экземпляр класса FF2 и присвоим переменной fifa ссылку на
метод Factorial этого объекта:II метод экземпляра класса:
var ffe := new FF2();
fifa := ffe.Factorial;Println(fifa(27));Оба метода возвращают одно и то же значение факториала:■ Процедурный тип—□XПроцедурный типл1088886945041835216076800000010888869450418352160768000000VКогда процедурная переменная представляет метод класса, то она хранит только
ссылку на точку входа этого метода. Если же процедурная переменная представ¬
ляет метод экземпляра, то она хранит также ссылку и на экземпляр его класса.Процедурной переменной можно присвоить лямбда-выражение. О лямбда-выра¬
жениях речь пойдёт дальше.
Вызов функции через процедурную перемен¬
нуюЕсли мы хотим вычислить факториал числа 27, то должны вызвать функцию
Factorial с соответствующим аргументом:I var dm1 := Factorial(27);Но если переменная fifa ссылается на функцию Factorial, то вместо имени этой
функции мы можем поставить имя процедурной переменной:Ivar fifa: FiboFact := Factorial;
var dm2 := fifa(27);Результат мы получим точно такой же, поскольку и в том, и в другом случае вызы¬
вается одна и та же функция Factorial.Переменной процедурного типа можно присвоить ссылку на другую функцию:Ififa := Fibonacci;
dm2 := fifa(27);И тогда она будет вызывать эту функцию.Так с помощью одной процедурной переменной мы можем вызывать разные
функции - и Factorial, и Fibonacci.Список вызововПока речь шла о представительстве единственной процедуры/функции, можно
было и не упоминать о том, что каждая процедурная переменная хранит список
вызовов (invocation list), то есть указателей на процедуры, функции и методы.
Начните новый проект и перепишите методы Factorial и Фибоначчи так, чтобы
они не возвращали значение, а просто печатали его в консольном окне:// ФАКТОРИАЛprocedure Factorial(n: integer);
beginif (n > 1) then
beginvar res: decimal := 1;
for var i := 2 to n do
begin
tryres := res * i;
exceptPrintln('Слишком большое число!');
exit;end;end;Println(res);endend;// ЧИСЛО ФИБОНАЧЧИprocedure Fibonacci(n: integer);beginif (n > 2) then
beginvar x: decimal := 1;
var y: decimal := 1;
var res: decimal := 0;
for var i := 3 to n do
begin
tryres := x + y;y := res;
exceptPrintln('Слишком большое число!');
exit;end;end;Println(res);endx := y;
Объявление процедурной переменной нужно изменить соответствующим обра¬
зом:I// процедурный тип:
typeFiboFact = procedure(n: integer);Объявим две процедурные переменные для этих процедур:Ivar fact : FiboFact := Factorial;
var fibo : FiboFact := Fibonacci;Обе содержат указатель на единственную процедуру, и мы можем вызвать каж¬
дую переменную по отдельности с нужным аргументом:|fact(27);
fibo(139);■ Процедурный тип 2—□XПроцедурный тип 2Л108888694Б0418352160768000000Б009Б3012480Б8391139327916261VНо процедурные переменные можно связывать (объединять, комбинировать)
друг с другом, получая новую - множественную, многоадресную процедурную пе¬
ременную. Она объединяет списки вызовов других процедурных переменных та¬
ким образом, что получается единый список вызовов.При вызове множественной процедурной переменной она последовательно вы¬
зывает процедуры/функции из общего списка.
Мы можем получить множественную (групповую) процедурную переменную с
помощью метода Combine класса System.Delegate.Он получает две процедурные переменные и возвращает новую процедурную пе¬
ременную типа Delegate. Для конкретного случая необходимо приведение типов.
Давайте объединим две процедурные переменные- fact и fibo - в одну, множе¬
ственную процедурную переменную fifa:і var fifa := FiboFact(Delegate.Combine(fact, fibo));Список вызовов переменной fifa состоит из списка вызовов первого аргумента, к
которому дописан список вызовов второго аргумента, то есть при вызове пере¬
менной fifa сначала будут вызваны процедуры/функции/методы переменной
fact, а затем - переменной fibo. В нашем случае - процедуры Factorial и Fibonacci.
Выполним такой код:Println;Console.ForegroundColor := ConsoleColor.Yellow;
var fifa := FiboFact(Delegate.Combine(fact, fibo));
fifa(27);С учётом предыдущего кода (зелёный цвет) мы получим такие результаты (жёлтый
цвет):■ Процедурный тип 2—□XПроцедурный тип 2Л108888694504183521607680000005009530124805839113932791626110888869450418352160768000000196418VВсё бы хорошо, но наши процедуры Factorial и Fibonacci при вызове требуют ар¬
гумент, который мы должны сообщить процедурной переменной. Но если в
списке вызовов несколько процедур/функций/методов, то мы не можем передать
каждому из них свой аргумент - при вызове они получат один и тот же аргумент.
Это значит, что не стоит создавать множественные процедурные переменные для
подпрограмм с параметрами.ІЕсли процедура/функция/метод имеет ссылочные параметры, то их значение
будет изменять каждый вызванный метод (процедура/функция) из списка.Но давайте закроем на это глаза и воспользуемся более приятными свойствами
множественных процедурных переменных, которые позволяют сцеплять списки
любого числа процедурных переменных. Для этого нужно обратиться к перегру¬
женному методу Combine, который принимает любое число переменных.В нашем проекте мы можем комбинировать наши переменные как угодно, при
этом они могут повторять любое число раз:Println;Console.ForegroundColor := ConsoleColor.Magenta;fifa := FiboFact(Delegate.Combine(fact, fibo, fact, fact, fibo));fifa(27);Запускаем приложение - и видим, что все процедуры вызваны верно:llJ Процедурный тип 1 — □ XVПроцедурный тип 2108888694504183521607680000005009530124805839113932791626110888869450418352160768000000196418108888694504183521607680000001964181088886945041835216076800000010888869450418352160768000000196418
I Если одна из процедур генерирует исключение, то остальные процедуры в
списке не вызываются, а исключение передаётся в метод, который вызвал
процедурную переменную.Число процедур/функций/методов в списке процедурной переменной можно
узнать с помощью метода GetlnvocationList:Println;Console.ForegroundColor := ConsoleColor.Green;
fifa := fact + fibo + fibo + fact + fact;
var delegates := fifa.GetInvocationList();Console.WriteLine('Сейчас в списке вызовов - {0} методов',delegates.Length);fifa(27);1 Процедурный тип 1 —□XСейчас в списке вызовов - Б методовА108888694504183521607680000001964181964181088886945041835216076800000010888869450418352160768000000VI Метод GetlnvocationList возвращает массив переменных, в списке вызовов ко¬
торых - единственный метод, поэтому общее число методов в точности равно
числу элементов в массиве.Вы даже можете самостоятельно вызвать процедуры/функции/методы из списка
вызовов:Println;Console.ForegroundColor := ConsoleColor.Yellow;
foreach var d in delegates do
d.DynamicInvoke(27);")
■ Процедурный тип ZСейчас в списке вызовов - Б методов108888694504183521607680000001964181964181088886945041835216076800000010888869450418352160768000000108888694504183521607680000001964181964181088886945041835216076800000010888869450418352160768000000Этот трюк позволяет произвольно вызывать процедуры/функции/методы из
списка вызовов, то есть передавать им аргументы и получать возвращаемые зна¬
чения.Объявите новый процедурный тип для прежних версий методов:FiboFact2 = function(n: integer): decimal;И добавьте к проекту, дополнив их имена двойкой, чтобы не смешивать с новыми
версиями этих методов.В главном блоке создаём множественную переменную fifa2, получаем её список
вызовов и в цикле foreach (или for, или по индексу) извлекаем функции и вызы¬
ваем их с нужным аргументом. Возвращаемое значение можно использовать, как
угодно:Println;Console.ForegroundColor := ConsoleColor.White;
var fact2: FiboFact2 := Factorial2;
var fibo2: FiboFact2 := Fibonacci2;var fifa2: FiboFact2 := fact2 + fibo2 + Fibonacci2 + Factorial2;
delegates := fifa2.GetInvocationList();
foreach var d in delegates do
beginvar ff2 := FiboFact2(d);
I Println('=', ff2(27));
end;Мы просто напечатали возвращаемые значения, но вы можете вычислить, напри¬
мер, их сумму.■ Процедурный тип 2—□X= 10888869450418352160768000000= 196418= 196418= 10888869450418352160768000000Однако вспомним, что изначально процедуры Factorial и Fibonacci возвращали
вычисленное значение, а в этом проекте мы сделали их «невозвращенцами».
Здесь мы имеем ту же проблему, что и с параметрами. Групповая переменная мо¬
жет вернуть только одно значение - это значение последней подпрограммы в
списке вызовов, все остальные значения будут проигнорированы. Если не печа¬
тать результаты на экране, то все подпрограммы, кроме последней, отработают
зря.Мы можем не только добавлять новые методы в список вызовов, но и удалять из
него любой метод с помощью метода Remove класса Delegate.Так как в списке вызовов подпрограммы могут повторяться, то из него будет уда¬
лено последнее вхождение списка заданной подпрограммы. Метод Remove вер¬
нёт новую переменную:• С укороченным списком вызовов, если совпадение обнаружилось• Ту же переменную, если совпадения не было• nil, если в списке вызовов не осталось ни одного методаУдалим из переменной fifa последние списки вызовов переменных fibo и fact:IPrintln;Console.ForegroundColor := ConsoleColor.Cyan;
fifa := FiboFact(Delegate.Remove(fifa, fibo));
fifa := FiboFact(Delegate.Remove(fifa, fact));
fifa(27);На рисунке видно, что удаление прошло успешно:1033886945041835216076800000019641310333369450413352160763000000Метод RemoveAll удаляет все вхождения списка вызовов заданной переменной
из списка вызовов множественной переменной.Например, если из списка вызовов переменной fifa удалить весь его список вызо¬
вов, то она прекратит своё существование.[Fifa := FiboFact(Delegate.RemoveAll(fif аfifa))j
V fifa nilГрупповую переменную можно получить и с помощью оператора +, который при¬
меняется к процедурным переменным одного типа.Пример с этим оператором вы уже видели выше:
fifa := fact + fibo + fibo + fact + fact;Оператор - удаляет списки вызовов указанных делегатов:Println;Console.ForegroundColor := ConsoleColor.Magenta;
fifa := fact + fibo + fibo + fact + fact;
fifa := fifa - fact - fibo;
fifa(27);”)
I Процедурный тип 2 — □ X1&88886945041835216076800000019641810888869450418352160768000000Оператор += позволяет добавлять к списку вызовов одной переменной списки вы
зовов других переменных или непосредственно однотипные функции/проце¬
дуры/методы:Println;Console.ForegroundColor := ConsoleColor.Cyan;fifa := nil;fifa += Fibonacci;fifa += Factorial;Println;fifa(27);Println;Console.ForegroundColor := ConsoleColor.Yellow;fifa += fifa;fifa(27);196418108888694504183521607680000001964181088886945041835216Ѳ768000000
1964181088886945041835216Ѳ768000000Оператор -= удаляет из множественной переменной списки вызовов других пере¬
менных или вызовы отдельных методов:Println;Console.ForegroundColor := ConsoleColor.White;
fifa2 := Factorial2;
fifa2 += Fibonacci2;
fifa2 -= Fibonacci2;
Println(fifa2(27));■ Процедурный тип 2—□XЛ10000369450418352160768000000VОбобщённые процедурные переменныеМы можем объявить процедурный тип так, чтобы он имел возвращаемое значе¬
ние и/или параметры произвольных типов:type//объявление универсального типа-делегата:GetLength<T> = function(t: T): integer;GetLength<T, TResult> = function(t: T): TResult;Такие процедурные типы называются обобщёнными.Первый процедурный тип всегда возвращает значение типа integer, но может
принимать 1 параметр любого типа. Второй процедурный тип отличается от пер¬
вого только тем, что и тип возвращаемого значения может быть любым.Благодаря обобщённым процедурным типам-делегатам мы получили в своё рас¬
поряжение огромное число типов функций, которые можно с ними использовать.Начните новый проект и напишите несколько функций, возвращающих значение
и получающих 1 аргумент. Например, такие:function GetStringLength(str: string): integer;
beginResult:= str.Length;end;")
function GetIntLength(num: integer): integer;
beginif (num = 0) then
beginResult:= 1;
exit;
end;var log := Log10(Abs(num));Result:= Trunc(log) + 1;end;function GetDoubleLength(num: double): integer;
beginif (num = 0) then
beginResult:= 1;
exit;
end;var log := Log10(Abs(Trunc(num)));
var res := Trunc(log);Result:= res + 1;
end;function GetCircleArea(d: double): integer;
beginvar area := PI * d * d / 4;
var res := Trunc(area);Result:= res;
end;function GetCircleAreaD(d: double): double;
beginvar res := PI * d * d / 4;Result:= res;
end;Теперь перейдём в главный блок, где объявим и инициализируем процедурные
переменные для наших функций. Обобщённые переменные создаются точно так
же, как и обычные:
begin//заголовок окна:Console.Title := 'Процедурный тип 3';
Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Процедурный тип 3');
Console.ForegroundColor := ConsoleColor.Green;
Println;var getLength := GetStringLength;Console.WriteLine('Длина слова "длина" равна ' +getLength('длина'));var getLength2 := GetIntLength;Console.WriteLine('Длина числа 1234567 равна ' +getLength2(1234567));var getLength3 := GetDoubleLength;Console.WriteLine('Длина числа 3.1415926 равна ' +getLength3(3.1415926));getLength3 := GetCircleArea;Console.WriteLine('Площадь круга диаметра 100 равна ' +getLength3(100));var getLength4 := GetCircleAreaD;Console.WriteLine('Площадь круга диаметра 100 равна ' +getLength4(100));Println;Console.ForegroundColor := ConsoleColor.Red;end.Результаты работы проце¬
дурных переменных вы
можете видеть на ри¬
сунке:Процедурный тип 3Длина слова "длина" равна БДлина числа 1234567 равна 7Длина числа 3.1415926 равна 1Площадь круга диаметра 100 равна 78БЗПлощадь круга диаметра 100 равна 78БЗ.98163397448
Обобщённые процедурные типы Action<T> и
Func<T>Всем хороши обобщённые процедурные типы, но всё-таки их нужно объявлять.
Однако в языке паскаль имеются готовые обобщённые процедурные типы
Action<T> и Func<T>, которые могут избавить нас от лишних усилий.Обобщённые процедурные типы Action<T> ссылаются на процедуры, а универ¬
сальные процедурные типы Func<T> - на функции, возвращающие значение про¬
извольного типа.Имеется целый набор процедурных типов Action<T> и Func<T> с различным чис¬
лом параметров - от 0 до 16:Action()
Action<T>
Action<T1, T2>
Func<TResult>(Func<T, TResult>Func<T1, T2, TResult>T1, T2, ... - тип параметраTResult - тип возвращаемого значенияСравним наши усилия в предыдущем проекте с меньшими трудозатратами в но¬
вом. Скопируйте в него все функции из проекта Процедурные типы 3 и добавьте
ещё одну процедуру, чтобы можно было задействовать обобщённый процедур¬
ный тип Action<> (обобщённый процедурный тип Func<> также можно использо¬
вать для ссылки на процедуры, но такая практика не рекомендуется):
procedure GetCircleAreaA(d: double);
beginvar area := Math.Round((PI * d * d / 4),2);Console.WriteLine('Площадь круга диаметра {0}100 равна {1}', d,
area);
end;В главном блоке мы просто описываем процедурные переменные, соответствую
щие универсальным типам Func и Action:uses System;function GetStringLength(str: string): integer;
beginResult:= str.Length;end;function GetIntLength(num: integer): integer;
beginif (num = 0) then
beginResult:= 1;
exit;
end;var log := Log10(Abs(num));Result:= Trunc(log) + 1;end;function GetDoubleLength(num: double): integer;
beginif (num = 0) then
beginResult:= 1;
exit;
end;var log := Log10(Abs(Trunc(num)));
var res := Trunc(log);Result:= res + 1;end;
function GetCircleArea(d: double): integer;
beginvar area := PI * d * d / 4;
var res := Trunc(area);Result:= res;
end;function GetCircleAreaD(d: double): double;
beginvar res := PI * d * d / 4;Result:= res;
end;procedure GetCircleAreaA(d: double);
beginvar area := Math.Round((PI * d * d / 4),2);
Console.WriteLine('Площадь круга диаметра {0}100 равна {1}',
area);
end;begin//заголовок окна:Console.Title := 'ActionFunc';Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('ActionFunc');
Console.ForegroundColor := ConsoleColor.Green;
Println;var getLength := GetStringLength;
Console.WriteLine('Длина слова "длина" равна ' +getLength('длина'));var getLength2 := GetIntLength;Console.WriteLine('Длина числа 1234567 равна ' +getLength2(1234567));var getLength3 := GetDoubleLength;
Console.WriteLine('Длина числа 3.1415926 равна ' +getLength3(3.1415926));
getLength3 := GetCircleArea;Console.WriteLine('Площадь круга диаметра 100 равна ' +getLength3(100));var getLength4 := GetCircleAreaD;Console.WriteLine('Площадь круга диаметра 100 равна ' +getLength4(100));var CircleArea := GetCircleAreaA;CircleArea(100);Println;Console.ForegroundColor := ConsoleColor.Red;end.Рисунок показывает, что замена типов прошла успешно!* ActionFuncX□1ActionFuncлДлина слова "длина" равна БДлина числа 1234Б67 равна 7Длина числа 3.141Б926 равна 1Площадь круга диаметра 100 равна70БЗПлощадь круга диаметра 100 равна70БЗ.98163397448Площадь круга диаметра 100 равна73БЗ,98VОбобщённые процедурные типы также могут быть множественными.Добавьте к проекту ещё одну процедуру того же типа Action<double>, что и
GetCircleAreaA:procedure GetQuadratAreaA(d: double);
beginvar area := Math.Round((d * d),2);Console.WriteLine('Площадь квадрата со стороной {0} равна {1}', d,
area);
end;")
Только вместо площади круга она вычисляет и печатает площадь квадрата.Допишите в главный блок ещё несколько строк:Println;Console.ForegroundColor := ConsoleColor.Green;CircleArea += GetQuadratAreaA;CircleArea(100);Здесь мы создаём множественную переменную CircleArea со списком вызовов,
состоящим из двух процедур - GetCircleAreaA и GetQuadratAreaA.Запускаем приложение - зелёные строчки на рисунке подтверждают наше пред¬
положение: переменная CircleArea последовательно вызывает обе процедуры:■ ActionFunc — □ XActionFuncДлина слова "длина" равна БДлина числа 1234567 равна 7Длина числа 3.1415926 равна 1Площадь круга диаметра 100 равна7S53Площадь круга диаметра 100 равна7S53.90163397440Площадь круга диаметра 100 равна7053,9SПлощадь круга диаметра 100 равна7053,9SПлощадь квадрата со стороной 100равна10000Итак, обобщённые процедурные типы Func<T> и Action<T> позволяют нам не объ¬
являть новые типы, что очень удобно.
Синонимы для процедурных типовВ паскале для некоторых процедурных типов введены синонимы, облегчающие
их использование:Action0 ^ Action
Action<T> ^ Action<T>Action2<T1,T2> ^ Action<T1,T2>
Action3<T1,T2,T3> ^ Action<T1,T2,T3>Func0<Res> ^ Func<Res>Func<T,Res> ^ Func<T,Res>Func2<T1,T2,Res> ^ Func<T1,T2,Res>
Func3<T1,T2,T3,Res> ^ Func<T1,T2,T3,Res>IntFunc ^ Func<integer,integer>RealFunc ^ Func<real,real>StringFunc ^ Func<string,string>
Predicate<T> ^ Predicate<T>
Predicate2<T1,T2> ^ Predicate<T1,T2>
Predicate3<T1,T2,T3> ^ Predicate<T1,T2,T3>Парочка примеров:var fres: Func0<double> := () -> PI;Console.WriteLine('Число пи равно: ' + fres);Println;var fint: IntFunc := n -> n*n;
var n:= 5;Console.WriteLine('Число {0} в квадрате равно {1}', n, fint(n));
Println;var pr2: Predicate2<String, integer>:= (s, n) -> s.Length > n;
var s:= 'Строка';Console.WriteLine('Длина строки {0} больше {1}', s, 5);
Println(pr2(s, 5));Println;
var pr3: Predicate3<integer, integer, integer>:= (n1,n2,n3) ->n1 > n2 + n3;Console.WriteLine('Число {0} больше суммы чисел {1} и {2}',12, 2, 3);Println(pr3(12,2,3));Процедурные переменные как параметрыПроцедурные переменные можно использовать как параметры процедур и функ¬
ций - точно так же, как и другие объекты ссылочных типов.I Функции, которые передаются как аргументы, называются функциями
обратного вызова {callback). Процедурные переменные служат прекрасным
средством для организации таких функций в языке паскаль.Начните новый проект, скопируйте в него функции Factorial и Fibonacci, а затем
объявите процедурный тип для них:■ Синонимы для процедурныйТНПОЕ□ Xtype//объявление процедурного типа:FiboFact = function(n: integer): decimal;
Теперь опишем новую функцию, которая принимает функцию ff типа FiboFact и
целое число n:function GetFF(ff: FiboFact; n: integer): decimal;
beginResult:= ff(n);end;Целое число мы используем для передачи в функцию, на которую ссылается пе¬
ременная ff, для вычисления факториала или числа Фибоначчи.В главном блоке мы передаём функции GetFF ссылку на нужную функцию, а также
аргумент для неё:IPrintln('Факториал числа 27 равен ', GetFF(Factorial, 27));Println('Число Фибоначчи 139 равно ', GetFF(Fibonacci, 139));В первом случае выполняется функция Factorial с аргументом 27, во втором -
функция Fibonacci с аргументом 139:Процедурный тип 4Факториал числа 27 равен 10888869450418352160768000000
Число Фибоначчи 139 равно 50095301248058391139327916261Мы можем обойтись и без объявления процедурного типа, а для типа параметра
указать процедурный тип Func:function GetFFF(ff: Func<integer, decimal>; n: integer): decimal;
beginResult:= ff(n);end;
Не забывайте, что тип последнего параметра в угловых скобках процедурного
типа Func - это тип возвращаемого значения. Все остальные типы определяют
сигнатуру делегируемого метода (процедуры или функции).В главном блоке новая функция GetFFF вызывается точно так же:Println;Console.ForegroundColor := ConsoleColor.Yellow;Println('Факториал числа 27 равен ', GetFFF(Factorial, 27));
Println('Число Фибоначчи 139 равно ', GetFFF(Fibonacci, 139));Метод пузырьковой сортировки медленный, но очень простой, поэтому приме¬
няется исключительно в учебных целях. У нас цели именно такие, поэтому в но¬
вом приложении мы будем сортировать данные любых типов. Нас вполне удо¬
влетворят строковые и числовые данные, что, впрочем, ничуть не мешает вам
отсортировать и что-нибудь другое.Итак, функция пузырьковой сортировки должна быть универсальной - это оче¬
видно, поскольку она должна уметь сортировать списки объектов разных типов.
Менее очевиден вопрос: как именно следует сортировать списки? - Обычно
числа сортируют в порядке увеличения их значений, а слова - в лексикографи¬
ческом порядке. Но, возможно, вам потребуется список, в котором порядок сле¬
дования элементов противоположный - от больших значений к меньшим.■ Процедурный тип 4□ XПроект Пузырьковая сортировка
Например, в каких-нибудь статистических исследованиях или при составлении
обратного словаря русского языка. В некоторых случаях очень удобен словарь, в
котором слова располагаются по длине. При разгадывании кроссвордов он был
бы очень кстати! Короче говоря, было бы совсем неплохо, если бы одна и та же
функция умела сортировать списки по-разному. Научить её этому можно с помо¬
щью процедурной переменной, которую мы передадим функции сортировки
при её вызове:// ФУНКЦИЯ СОРТИРОВКИfunction BubbleSort<T>(lst:List<T>; comp: Func<T, T, boolean>):List<T>;beginvar res := new List<T>(lst);
var n := lst.Count - 1;
var sorted := true;
while (sorted) do
beginsorted := false;for var i := 0 to n-1 dobeginif (comp(res[i], res[i+1])) then
begin(res[i], res[i+1]) := (res[i+1], res[i]);
sorted := true;
end;
end;
end;Result:= res;
end;Процедурная переменная comp сравнивает два соседних элемента списка 1st
типа T(точнее, его копии res) и возвращает true, если они нарушают правильный
порядок следования элементов в списке. Если это так, то функция сортировки
переставляет их местами.В качестве аргумента можно передавать функции сортировки любые функции
сравнения, которые совпадают с типом Func<T, T, boolean>. Это значит, что мы
можем изменять поведение функции сортировки с помощью соответствующих
функций сравнения.
В простейшем случае можно сортировать элементы так, как это принято для их
типа T:function Compare<T>(t1, t2: T): boolean;
beginvar res := (IComparable(t1)).CompareTo(t2) > 0;Result:= res;
end;Но для начала нам нужно создать списки элементов числового и строкового типа.
Здесь важно знать меру, чтобы не потерять веру в метод пузырьковой сорти¬
ровки. Я думаю, что сотни элементов в каждом списке вполне достаточно, чтобы
ожидание ещё не стало утомительным.Сотню случайных чисел мы легко получим от функции SeqRandom, а вот со сло¬
вами труднее. Мы загрузим словарь Ожегова и случайно выберем из него сотню
слов:begin//заголовок окна:Console.Title := 'Пузырьковая сортировка';Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Пузырьковая сортировка');Console.ForegroundColor := ConsoleColor.Green;Println;var lstNum := SeqRandom(100, 1, 1000).ToList;Println(lstNum);Println;var lstStrAll := ReadAllLines('OSH-W97.txt').ToList();
var lstStr := new List<string>();
for var i := 0 to 100-1 do
beginvar ind := PABCSystem.Random(0, lstStrAll.Count-1);
lstStr.Add(lstStrAll[ind]);end;Println(lstStr);
Println;Console.ForegroundColor := ConsoleColor.Red;end.Полученные списки мы печатаем в консольном окне.Глядя на рисунок, трудно не заметить, что и числа, и слова располагаются в списке
именно в случайном порядке, чего мы и добивались:■ Пузырьковая сортировка — □ XПришло время отсортировать оба списка в общепринятом порядке. Для этого мы
вызываем функцию сортировки BubbleSort, передавая ей в качестве аргументов
списки и ссылку на функцию сравнения Compare<T>:Console.ForegroundColor := ConsoleColor.Green;Println;Console.ForegroundColor := ConsoleColor.Green;
var lstInt := BubbleSort(lstNum, Compare&<integer>);Println(lstInt);
Println;var lstStr2 := BubbleSort(lstStr, Compare&<string>);Println(lstStr2);На рисунке жёлтым цветом показано содержимое списков до сортировки, а зелё¬
ным - после. Убеждаемся, что числа располагаются согласно величине, а слова -
согласно алфавиту:■ Пузырьковая сортировка — □ XПузырьковая сортировка[374,576,252,625,435,639,714,387,292,569,912,629,829,236,176,484,559,208,549,675,7
38,224,571,9,588,286,863,362,246,207,420,233,523,35,976,364,637,331,33,961,719,693
,530,884,846,769,451,414,873,842,264,624,340,53,287,150,6,243,37,150,827,353,142,8
5,149,679,107,794,261,813,848,228,175,588,957,46,695,687,164,819,836,449,489,404,944,797,894,471,749,826,358,394,294,884,135,366,552,446,735,167,...][ПРОРЕЗКА,ПРОЗРАЧНОСТЬ,ГРАНД,БОБИК,ЮННАТ,БАРХАТНОСТЬ,НЕ ГОЦИАНТКА,СВИЩ,ВИЛЫ,ЗЛОНРАВ
ИЕ,НЕОГЛЯДНОСТЬ,КОНТРАБАНДИСТ,ВАЗЕЛИН,ЗАГОТОВИТЕЛЬ,КОМПРОМЕТИРОВАНИЕ,ЛЮМБАГО,ТРАПЕ
ЦИЯ,САЛО,ГЛУБЬ,ЛАВР,СМЕРТНИК,ПРИБОЙ,БЕЗУМСТВО,НАМЁТ,Н ЕНАВИСТНОСТЬ,Н ЕЛЮДИМОСТЬ,ХОХМ
АЧ,КЕНАРКА,НЕИСТОЩИМОСТЬ,ПОДПОРА,ОДНОЛЕТНИК,ИЖДИВЕНИЕ,ЧЕСАЛКА,ЯЗЫЧОК,ГРОМОГЛАСНОСТ
Ь,ДИКОРОС,КИРПИЧ,НАВ ЕС,МНОГОЧЛ ЕН,ФАРИСЕЙСТВО,КУПАЛЬЩИК,ГЛУХОТА,ЛУ Г,ОРБИТА,ЛОГ,БЕ ГС
ТВО,КОМПЕНСАЦИЯ,БУР ЕЛОМ,ПИСКЛЯ,С ЕКТАНТСТВО,ОНДАТРА,СТАРАНИЕ,КОГОРТА,МАЛОЗЕМЕЛЬЕ,МА
КАРОНИНА,АСТРОНАВТИКА,ПОПОЛНЕНИЕ,РЕПЕЙНИК,ПОЛУКАФТАН,ПАРТЕР,ВОДОНОСНОСТЬ,ГРОМАДИНА
,КЛЕПТОМАНИЯ,ОПРЕСНЕНИЕ,СВАТАНЬЕ,ЖЕЛАННОСТЬ,ЛИРА,ВАХЛАК,БОГОМАТЕРЬ,ГЕРОНТОЛОГИЯ,ВА
Р ЕВО,НАН ЕС ЕНИЕ,КОРЮШКА,ЗАТРАТА,АВТОРУЧКА,ПРИНУЖДЁННОСТЬ,ИНТ Е ГРАЦИЯ,ПАПИРОСА,БОМБОУ
БЕЖИЩЕ,МОНОПОЛИСТ,ГЛИНОЗ ЁМ,ТОПКОСТЬ,РИФЛ ЕНИ Е,РАЗРАБОТЧИК,В ЕРТОЛ ЁТ,ПЬЯНЧУГА,ЧАЙНИЦА
,ПИЧКАНЬЕ,ПОС ЕТИТ ЕЛЫЧИЦА,ГАМАК,ПИЛКА,ПЛЮЩ,СТРАХОВАНИЕ,ТРОЙСТВ ЕННОСТЬ,СП ЕКТАКЛЬ,ДОТ
ОШНОСТЬ,ТАСОВКА,ТРОИЦА,СЕНОВАЛ,ДВОРНИЧИХА,...][6,9,33,35,37,46,53,85,107,135,142,149,150,150,164,167,175,176,207,208,224,228,233
,236,243,246,252,261,264,286,287,292,294,331,340,353,358,362,364,366,374,387,394,4
04,414,420,435,446,449,451,471,484,489,523,530,549,552,559,569,571,576,588,588,624
,625,629,637,675,679,687,689,693,695,714,719,735,738,749,769,794,797,813,819,826,8
27,829,836,842,846,848,863,873,884,884,894,912,944,957,961,976, ...][АВТОРУЧКА,АСТРОНАВТИКА,БАРХАТНОСТЬ,БЕГСТВО,БЕЗУМСТВО,БОБИК,БОГОМАТЕРЬ,БОМБОУБЕЖИЩ
Е,БУРЕЛОМ,ВАЗЕЛИН,ВАРЕВО,ВАХЛАК,ВЕРТОЛЁТ,ВИЛЫ,ВОДОНОСНОСТЬ,ГАМАК,ГЕРОНТОЛОГИЯ,ГЛИН
ЬзЁМ,ГЛУБЬ,ГЛУХОТА,ГРАНД,ГРОМАДИНА,ГРОМОГЛАСНОСТЬ,ДВОРНИЧИХА,ДИКОРОС,ДОТОШНОСТЬ,ЖЕДанность,заготовитель,затрата,злонравие,иждивение,интеграция,кенарка, кирпич,клептоМАНИЯ,КОГОРТА,КОМПЕНСАЦИЯ,КОМПРОМЕТИРОВАНИЕ,КОНТРАБАНДИСТ,КОРЮШКА,КУПАЛЬЩИК,ЛАВР,Л
ИРА,ЛОГ,ЛУ Г,ЛЮМБАГО,МАКАРОНИНА,МАЛОЗЕМЕЛЬЕ,МНОГОЧЛЕН,МОНОПОЛИСТ,НАВЕС,НАМЁТ,НАНЕСЕ
НИЕ,НЕ ГОЦИАНТКА,НЕИСТОЩИМОСТЬ,НЕЛЮДИМОСТЬ,НЕНАВИСТНОСТЬ,НЕОГЛЯДНОСТЬ,ОДНОЛЕТНИК,ОН
ДАТРА,ОПРЕСНЕНИЕ,ОРБИТА,ПАПИРОСА,ПАРТЕР,ПИЛКА,ПИСКЛЯ,ПИЧКАНЬЕ,ПЛЮЩ,ПОДПОРА,ПОЛУКАФ
ТАН,ПОПОЛНЕНИЕ,ПОСЕТИТЕЛЬНИЦА,ПРИБОЙ,ПРИНУЖДЁННОСТЬ,ПРОЗРАЧНОСТЬ,ПРОРЕЗКА,ПЬЯНЧУГА
,РАЗРАБОТЧИК,РЕПЕЙНИК,РИФЛЕНИЕ,САЛО,СВАТАНЬЕ,СВИЩ,СЕКТАНТСТВО,СЕНОВАЛ,СМЕРТНИК,СПЕ
КТАКЛЬ,СТАРАНИЕ,СТРАХОВАНИЕ,ТАСОВКА,ТОПКОСТЬ,ТРАПЕЦИЯ,ТРОИЦА,ТРОЙСТВЕННОСТЬ,ФАРИСЕ
ЙСТВО,ХОХМАЧ,ЧАЙНИЦА,ЧЕСАЛКА,ЮННАТ,ЯЗЫЧОК,...]
Функция для обратной сортировки отличается от функции прямой сортировки
только тем, что мы изменили оператор сравнения:// ОБРАТНАЯ СОРТИРОВКАfunction CompareReversed<T>(t1, t2: T): boolean;
beginvar res := (IComparable(t1)).CompareTo(t2) < 0;Result:= res;
end;В главном блоке на этот раз мы сортируем списки шиворот-навыворот:// обратная сортировка:Println;Console.ForegroundColor := ConsoleColor.Cyan;lstInt := BubbleSort(lstNum, CompareReversed&<integer>);Println(lstInt);Println;lstStr2 := BubbleSort(lstStr, CompareReversed&<string>);
Println(lstStr2);Списки отсортированы:■ Пузырьковая сортировка — □ X[990,986,961,942,941,940,914,909,897,896,893,850,825,822,819,816,815,810,797,783,7
76,774,764,758,755,730,716,716,714,693,662,660,654,646,626,625,623, 592, 587, 579, 575
,570,570,569,563,545,537,536,529,527,496,487,470,470,444,441,437,426,426,411,405,4
01,378,374,372,363,361,359,359,343,323,314,305,299,297,290,289,280,272,262,241,229
,216,200,164,156,155,152,149,136,111,96,93,86,80,72,45,44,36,8,...][ЯМА,ЮКОЛА,ЭЛЕКТРИЧКА,ШТРАФНИК,ЧЕСАНИЕ,ХОЛОД,ХЛЕБОПАШЕСТВО,ХИНА,ФРАНТ,ФИКСАЖ,ѴЧАСТ
ЛИВОСТЬ,УЛЬТРА,ТРАГИКОМЕДИЯ,ТОРШЕР,ТЕОРЕТИЧНОСТЬ,ТАЙМЕНЬ,СТРУНА,СТРОГОСТЬ,СТЕСНЕНИ
Е,СТАВЛЕННИК,СОСРЕДОТОЧЕННОСТЬ,СКОЛ,СЕРОСТЬ,РИФ,РЕФРЕН,ПУЗЫРЧАТОСТЬ,ПРОТИРАНИЕ,ПРО
ГУЛЫЦИК,ПРОВОЗГЛАШЕНИЕ,ПРИЛЕЖНОСТЬ,ПРИЛЕЖАНИЕ,ПОПУГАЙНИЧЕСТВО,ПОЗВОЛЕНИЕ,ПОДВЕРЖЕН
НОСТЬ,ПИРОЖКОВАЯ,ПИВОВАРЕНИЕ,ПЕРУАНКА,ПЕРЛАМУТРОВОСТЬ,ПЕРЁД,ОТСЛОЕНИЕ,ОТРЕЗВЛЕНИЕ,
ОТРАСЛЬ,ОТОРИНОЛАРИН ГОЛОГИЯ,ОТОБРАНИЕ,ОТВАР,ОСЁЛ,ОПЕРЕТКА,ОДИЧАНИЕ,ОБРИСОВКА,НЕСТЕ
РПИМОСТЬ,НЕПРИТЯЗАТЕЛЬНОСТЬ,НЕДОУМЕННОСТЬ,НАПАРНИЦА,НАДРУГАТЕЛЬСТВО,МЯСИСТОСТЬ,МЭР
[МУСКУЛИСТОСТЬ,МОЗ ГИ,МНОГОЛЮДСТВО,МИЛЯ ГА,МАТЕРИАЛИСТ,ЛЮДОЕД,ЛУНОХОД,ЛАНДЫШ,КУСТАРН
ИК,КОШМАРНОСТЬ,КОШМАР,КОСТИСТОСТЬ,КАПИТАЛИСТ,КАЗНЬ,ИСКУССТВЕ ННИК,ИЗЖИТИЕ,ИЗВОД,ЗЛО
ВРЕДНОСТЬ,ЗЕМЛЕДЕЛИЕ,ЗАНАЧКА,ЗАЖИМАНИЕ,ДРАЧЛИВОСТЬ,ДОХЛЯТИНА,ДОМРАБОТНИЦА,ДИЧЬ,ДЕМ
ЬкРАТИЯ,ДЕЛИКАТЕС,ДЕБЮТАНТ,ГУБКА,ГОГОЛЬ,ГИДРОЛОГИЯ,ВЫЧУРНОСТЬ,ВЫКОРМОК,ВЫВЕСКА,ВНУ
ШИТЕЛЬНОСТЬ,ВАЛЬЦОВЩИК,БРУСНИЧНИК,БРАТВА,БЕРЕТКА,БЕЗУДЕРЖНОСТЬ,БЕГУН,БАСОК,АТЛЕТИК
А,АРАБКА,...]
Теперь отсортируем элементы списков согласно сумме их символов, то есть числа
- по сумме их цифр, а слова - по сумме кодов их букв:// СОРТИРОВКА ПО СУММЕfunction CompareSum<T>(t1, t2: T): boolean;
beginvar arr := t1.ToString().ToCharArray();
var sum1 := 0;
foreach var i in arr do
sum1 += ord(i);arr := t2.ToString().ToCharArray();
var sum2 := 0;
foreach var i in arr do
sum2 += ord(i);var res := sum1 > sum2;Result:= res;end;В главный блок дописываем новые строки:// сортировка по сумме:Console.ForegroundColor := ConsoleColor.Gray;
Println;lstInt := BubbleSort(lstNum, CompareSum&<integer>);
Println(lstInt);Println;lstStr2 := BubbleSort(lstStr, CompareSum&<string>);
Println(lstStr2);И получаем на выходе причудливо отсортированные списки:
■ Пузырьковая сортировка[6,80,36ИЗ,96,97,200,122,420,133,322,322,521,251,800,323,10В, 522, 72Э,В06, 311
,136,217,217,280,506,524,362,524,533,155,326,416,426,156,255,471,309,165,561,364,445,382,247,265,922,391,590,914,509,419,581,473,815,860,464,293,392,906,6
27,843,807,681,465,763,736,475,673,268,980,971,827,854,881,548,656,846,774,90
9,486,972,927,990,747,738,982,685,497,488,938,885,498,795,993,498,769,886,896
,988,979,...][ШИК,ДЖАЗ,ПОПА,АРЫК,ТРЮК,ШТОФ,ЛАВКА,НАБОР,ГОВНО,ГОВОР,КОПЬЁ,ЖЁЛЧЬ,УЙГУР,БРОНЯ
,КРОЛЬ,ЭТНОС,МАРКЁР,ГИББОН,УКАЗКА,БАМПЕР,МЕСИВО,БАРЬЕР,ДОПУСК,ВТОРАЯ,КОПЫТО,К
У П ЧАЯ,НАКАТ КА,ЛУЖАЙ КА,НОВ ЕЛЛА,3АСЛУ ГА,Л ЕЖБ ИЩЕ,П ЕДИАТ Р,Г РУДН И К,САМО ГОН,СОБАЧ ЬИ
,ГЛАЖЕНЬЕ,ПОЖАРНИК,Т ЕПЛОВОЗ,ПРОКОЛКА,КИНОШНИК,ИЗМОРОЗЬ,ТРАДИЦИЯ,ОТ ГОВОРЫ,ГРАЖ
ШАНКА, БЕЛЬГИЙКА,ДИВЕРСАНТ,ОТОБРАНИЕ,РОМАНТИКА,ПАДЧЕРИЦА,КАТОЛИЧКА,ОХЛАМОНКА,К
ЬЛ0ШЕНИЕ,ФИЛАТЕЛИЯ,ЭНТОМОЛОГ,БРАНДМАЙОР,КЛАДОВЩИЦА,СКИРДОПРАВ,МЕТАЛЛОЛОМ,ИСТО
РЖЕНИЕ,ПРОЯВЛЕНИЕ,ПОНЧИКОВАЯ,ПОСВЯЩЕНИЕ,ОФИЦЕРСТВО,ПОЛУОБОРОТ,ТЕЛЕСНОСТЬ,ТЕЛЕ
СНОСТЬ,КОЛЕСОВАНИЕ,ГЕРМАФРОДИТ,ГЕРМАФРОДИТ,ЗАКЛЮЧЁННАЯ,ПРОГИМНАЗИЯ,ВАЛЬЦОВЩИЦ
к,ОЧЕРЁДНОСТЬ,НИЧТОЖЕСТВО,ПРИВЕШИВАНИЕ,УНИВЕРСАЛИЗМ,АНТИСЕМИТИЗМ,ПОДПИСЫВАНИЕ
[брезгливость,СУБЪЕКТИВИЗМ,СУБЪЕКТИВИСТ,ФИЗИОТЕРАПИЯ,РАЗНОЛИКОСТЬ,ХРОНОМЕТРИС
Т,ПРОВИНЦИАЛИЗМ,СВИДЕТ ЕЛЬСТВО,КРОВОПРОЛИТИЕ,ТАЛАНТЛИВОСТЬ,Б ЕЗЗАЩИТНОСТЬ,НЕУЖИ
ВЧИВОСТЬ,ЖИВОТВОРНОСТЬ,КАНИТЕЛЬНОСТЬ,ПОСРЕДНИЧЕСТВО,БЕССТРАСТНОСТЬ,ПРОТОКОЛЬН
ОСТЬ, ВОСПИТАТЕЛЬНИЦА,БЕЗБОЛЕЗНЕННОСТЬ,НАСЛЕДСТВЕННОСТЬ,БЕЗОСТАНОВОЧНОСТЬ,ТРУД
НОВОСПИТУЕМОСТЬ,...]Такая сортировка на первый взгляд кажется несколько искусственной, но,
например, в нумерологии и в теории чисел часто оперируют суммой цифр чи¬
сел, а сумма букв, составляющих слова, иногда используется в словесных за¬
дачах.И наконец, мы отсортируем списки по длине элементов. Для чисел эта сортировка
бесполезна, так как они и без того сортируются именно так, а для слов она вполне
уместна и разумна.Здесь важно учесть, что слова равной длины должны располагаться в алфавитном
порядке:// СОРТИРОВКА ПО ДЛИНЕfunction CompareLen<T>(t1, t2: T): boolean;
beginvar len1 := t1.ToString().Length;
var len2 := t2.ToString().Length;var res:= true;")
if (len1 = len2) thenres := (IComparable(t1)).CompareTo(t2) > 0elseres := len1 > len2;Result:= res;end;Возвращаемся в главный блок и пишем последнюю порцию кода:
// сортировка по длине:Console.ForegroundColor := ConsoleColor.White;Println;lstInt := BubbleSort(lstNum, CompareLen&<integer>);
Println(lstInt);Println;lstStr2 := BubbleSort(lstStr, CompareLen&<string>);
Println(lstStr2);И опять оба списка отсортировались успешно:[2,11,1В ,21,27,27,31,52,71,76,105,1Э7,121,132,155,156,16В,170,196,219,220,224,
249,252,257,284,285,286,291,292,297,293,338,345,350,351,360,361,365,375,377,38
0,391,402,402,402,421,440,471,484,508,513,522,533,536,540,550,551,560,501,593,
594,612,624,630,637,651,661,662,667,601,695,698,707,714,764,769,770,770,779,70
0,781,790,828,859,866,868,881,883,893,896,096,919,926,936,949,964,966,905,995,
■■■][ОР,ЛАЙ,БУЕР,БЬЕФ,МИСС,ОЧЁС,ХРЕН,ДРАМА,ДРОВА,КОСЕЦ,ПѴРГА,СКАРБ,СПРОС,ЖНЕЙКА,ЗА
ІЛ Е Е К, 3 Л Ы ДН Я, К Р АС К А, П АСЛ Ё Н, СОРОКА, ФИ ГУ РА, БАП Т И ЗМ, Г НОЙ Н И К, ДОСМОТ Р, ЗДЁМЩИ К, К АВ Е Р Н
А,ОДНОДУМ,ПАДАНЕЦ,ПРИЯТИЕ,ПРОЯВКА,ПЬЯНИЦА,РАЗВРАТ,САНКЦИЯ,ДРОВОСЕК,КАРТИНКА,НА
Н ИФ Е С Т, Н АУIUH И ЦА, ОЗОН ATOP, П С ИХИАТ Р,РАЗ В И Т И Е,РАЗЛОМКА,РАС СУДОК,С БИ ВАН И Е, В ЫСЕ ВАН И
Е,ГРИМАСНИК,ДРОБНОСТЬ,КАРТОФЕЛЬ,НАСТУРЦИЯ,ОБЖИМАНИЕ,ОБРУБАНИЕ,ОГОРЧЕНИЕ,ОРГВЫВ
ОДЫ,ПЕНОПЛАСТ,ПРИСЛОВЬЕ,СОЗВЕЗДИЕ,СОТРУДНИК,СЦЕНАРИСТ,УСЫПЛЕНИЕ,ФОРМАЛИСТ,БУКС
ИРОВКА,ВЫПОЛНЕНИЕ,ЗУБОСКАЛКА,ЗУБОТЫЧИНА,КВАРТПЛАТА,КОЖЕВЕННИК,МАВРИТАНЕЦ,ОДНОТ
ОМН И К,ПЕРЕСТАРОК,ПОДПОЛ ЫЦИ К,СЛАВ Я НОФИЛ,СТИПЕНДИАТ,ЕХИДН И ЧАН Ь Е,НАСЛ И Ч НОС Т Ь, Н Е И С
“DBCTBO,ОБЕСПЕЧЕНИЕ,ОБКАПЫВАНИЕ,ОСЯЗАЕМОСТЬ,ПОСТОРОННИЙ,ПРОМЕРЗАНИЕ,РАСПУТНОСТ
Ь,СОАВ ТОР С Т ВО,СУMAC ІЛ ЕДШ И Й,ТЕРНИСТОСТЬ,Э КОНОМНОС Т Ь,БОМБОУ Б ЕЖИЩЕ,НЕОГЛЯДНОСТЬ,РА
ЗРЬІХЛИТЕЛЬ,РОДИТЕЛЬНИЦА,ХРОМОНОГОСТЬ,ОБЕЗВОЖИВАНИЕ,ОДНОЦВЕТНОСТЬ,ПЛОДОНОС НОСТЬ
, Р Е ГЛАМЕ Н ТАЦИ Я,ПАРЛ АМЕ Н ТАР И ЗМ,У С Т РОИ Т ЕЛ ЬН И ЦА,Б Е СП РЕДМЕТ НОС Т Ь,АР ГУМЕ НТИ РОВАН И Е,
ФИЛОСОФСТВОВАНИЕ,ПРИОБРЕТАТЕЛЬСТВО,КОРРУМПИРОВАННОСТЬ,НЕПОСЛЕДОВАТЕЛЬНОСТЬ,...
]
И так, отправляя методу сортировки ссылку на метод сравнения, вы можете рас¬
полагать элементы списка и вдоль, и поперёк. И при этом метод сортировки оста¬
ётся неизменным. Этот приём широко используется во многих коллекциях для
сортировки элементов.Анонимные функцииВы, должно быть, обратили внимание, что все функции сравнения очень короткие
и не используются самостоятельно - они нужны только для передачи в функцию
сортировки. Например, функция для прямой сортировки, состоит, по сути, из
единственной строки:// ЕСТЕСТВЕННАЯ СОРТИРОВКАfunction Compare<T>(t1, t2: T): boolean;beginvar res := (IComparable(t1)).CompareTo(t2) > 0;Result:= res;
end;В паскале можно создавать анонимные функции. У них нет имени. Это просто блок
кода, который, как обычно, заключается в операторные скобки, а ему предше¬
ствует ключевое слово function, после которого в скобках перечисляются фор¬
мальные параметры.Продолжим пузырьковую сортировку в новом проекте. В него нужно скопировать
весь код из предыдущего проекта, за исключением кода главного блока после со¬
здания списков чисел и слов.Начнём с естественной сортировки списков. Для этого мы передавали функции
BubbleSort<T> ссылку на функцию Compare<T>:Ivar lstInt := BubbleSort(lstNum, Compare&<integer>);
var lstStr2 := BubbleSort(lstStr, Compare&<string>);
Поскольку эта функция одноразовая, то мы можем не писать новую функцию, а
создать процедурную переменную нужного типа непосредственно в главном
блоке, присвоив ей ссылку на встроенный блок кода - анонимную функцию.Так мы создаём две процедурные переменные - compi и comps - из анонимных
функций, а затем передаём их функции сортировки как аргументы://естественная сортировка:Println;Console.ForegroundColor := ConsoleColor.Yellow;var compi: Funccinteger, integer, boolean> := function(n1, n2) ->beginResult:= n1 > n2;
end;var lstInt := BubbleSort&cinteger>(lstNum, compi);Println(lstlnt);Println;var comps: Funccstring, string, boolean> := function(s1, s2) ->beginResult:=(lComparable(s1)).CompareTo(s2) > 0;
end;var lstStr2 := BubbleSort&cstring>(lstStr, comps);Println(lstStr2);Тип возвращаемого значения указывать не нужно, поскольку он однозначно
определяется компилятором по типу последнего параметра в процедурном типе
Func или по типу возвращаемого значения в объявлении процедурного типа.На рисунке отчётливо видно, что процедурные переменные исполняют встроен¬
ный код, который действует точно так же, как и именованные функции.
■ Анонимные функцииАнонимные функции[657,676,443,652,805,877,789,127,439,153,872,318,731,929,729,381,989,355,555,915,112,818,113,878,816,318,867,173,570,821,448,362,271,194,432,89,189,202,72
5,886,733,262,355,87,683,354,729,706,231,78,15,299,126,310,492,839,563,565,83
7,488,10,616,557,619,705,511,47,537,498,641,192,787,631,670,182,851,464,26,19
6,463,589,161,942,493,806,150,236,962,703,593,526,625,366,238,877,117,706,737
, 382,173 ][КОЛДУНЬЯ,ДОСЯ ГАЕМОСТЬ,ЖЕРТВЕННОСТЬ,ПОДПИСЬ,УЗЛОВАТОСТЬ,ЗАПЕВАЛА,ПРОТРАВИТЕЛЬ
,НЕПЕРЕДАВАЕНОСТЬ,ВЬЮК,ПРИЗНАНИЕ,ЛУЧЕНИЕ,ПАНСИОНАТ,ЛИЦЕПРИЯТИЕ,ЛОЩИНА,УСТУПЧА
ТОСТЬ,ТЕПЛОКРОВНОЕ,ПТИЧКА,БОЛЬШИНСТВО,СТАНКОСТРОЕНИЕ,ПЕРЕМЕЖКА,КАЗУС,ВЕЛИК,СФ
ЕРИЧНОСТЬ,ФАЗА,ВСЕЛЕННАЯ,ПИНЦЕТ,МРАЗЬ,МЕДАЛЬОН,СОВОК,БОРОДАЧ,ГОЛУБИНЫЕ,РОСКОШ
НОСТЬ,ПРИНУДИТЕЛЬНОСТЬ,АРТЕЛЬЩИК,ГРИМАСА,МЕРЗОСТЬ,ЧЕРТЫХАНЬЕ,КУШ,ПОЛУПУСТЫНЯ,
ПЛЮГАВОСТЬ,ОБНОВКА,СЛАБОСИЛЬНОСТЬ,СФОРМИРОВАНИЕ,БОДЛИВОСТЬ,ЗАГАЗОВАННОСТЬ,ЭПИ
ЛЕПТИЧКА,ИКОНОГРАФИЯ,ЭСТЕТКА,КРИСТАЛЛИЗАЦИЯ,АФЕРИСТКА,НАУЧНОСТЬ,ПРИЗНАТЕЛЬНОС
ТЬ,ФУНТИК,СТЫДОБА,УСТРЕМЛЕНИЕ,ЗАКАЗЧИК,ЛГУНЬЯ,КОДИФИКАЦИЯ,ОБЕЗЬЯНСТВО,КОНТЕЙН
ЕР,СПИДВЕЙ,КАЛОРИЯ,ПРОСЛОЙКА,БОДРЯК,ОДЫШКА,ПРИСЯДКА,УПОИТЕЛЬНОСТЬ,КУЛИНАРИЯ,3
АГРЕБНОЙ,ОМНИБУС,УСЛОВИЕ,КОПКА,НЕПОЛАДКА,ЗВЕРОВОДСТВО,БУКСИР,ОДУТЛОВАТОСТЬ,ИЗ
ВОЛЕНИЕ,СКЕТЧ,РАЗВОДЫ,ВЫТРАВЛИВАНИЕ,КОММУНАЛКА,НАГРУЗКА,БИБЛИОФИЛКА,СИ ГАРА,ПО
ЧЕРКОВЕДЕНИЕ,ОПОРОК,СПАД,СТЕНА,ВЫСКОЧКА,ХРЕСТОМАТИЙНОСТЬ,БЕЗБРЕЖНОСТЬ,ПРИТВОР
А,ТЫКВЕННЫЕ,КАТОРЖНИЦА,СКАКАНЬЕ,ЖАРГОН,ВСЕСИЛЬНОСТЬ,СКЕРЦО,ПИКАП,ПИТАНИЕ,...][10,15,26,47,78,87,89,112,113,117,126,127,150,153,161,173,173,182,189,192,194
,196,202,231,236,238,262,271,299,310,318,318,354,355,355,362,366,381,382,432,439,443,448,463,464,488,492,493,498,511,526,537,555,557,563,565,570,589,593,616,619,625,631,641,652,657,670,676,683,703,705,706,706,725,729,729,731,733,73
7,787,789,805,806,816,818,821,837,839,851,867,872,877,877,878,886,915,929,942
,962,989, . . .][APT ЕЛ ЫЦИК, АФЕРИСТКА,БЕЗБРЕЖНОСТЬ,БИБЛИОФИЛКА,БОДЛИВОСТЬ,БОДРЯК,БОЛЬШИНСТВО,Б
ОРОДАЧ,БУКСИР,В ЕЛИК,ВС ЕЛ ЕННАЯ,ВС ЕСИЛЬНОСТЬ,ВЫСКОЧКА,ВЫТРАВЛИВАНИЕ,ВЬЮК,ГОЛУБИ
НЫЕ,ГРИМАСА,ДОСЯГАЕМОСТЬ,ЖАРГОН,ЖЕРТВ ЕННОСТЬ,ЗАГАЗОВАННОСТЬ,ЗАГР ЕБНОЙ,ЗАКАЗЧИ
К,ЗАП ЕВАЛА,ЗВ ЕРОВОДСТВО,ИЗВОЛ ЕНИЕ,ИКОНОГРАФИЯ,КАЗУС,КАЛОРИЯ,КАТОРЖНИЦА,КОДИФИ
КАЦИЯ,КОЛДУНЬЯ,КОММУНАЛКА,КОНТ ЕЙН ЕР,КОПКА,КРИСТАЛЛИЗАЦИЯ,КУЛИНАРИЯ,КУШ,Л ГУНЬЯ
,ЛИЦЕПРИЯТИЕ,ЛОЩИНА,ЛУЧЕНИЕ,МЕДАЛЬОН,МЕРЗОСТЬ,МРАЗЬ,НАГРУЗКА,НАУЧНОСТЬ,НЕПЕРЕ
ДАВАЕМОСТЬ,Н ЕПОЛАДКА,ОБЕЗЬЯНСТВО,ОБНОВКА,ОДУТЛОВАТОСТЬ,ОДЫШКА,ОМНИБУС,ОПОРОК,
ПАНСИОНАТ,П ЕР ЕМЕЖКА,ПИКАП,ПИНЦЕТ,ПИТАНИЕ,ПЛЮГАВОСТЬ,ПОДПИСЬ,ПОЛУПУСТЫНЯ,ПОЧ ЕР
КОВЕДЕНИЕ,ПРИЗНАНИЕ,ПРИЗНАТЕЛЬНОСТЬ,ПРИНУДИТЕЛЬНОСТЬ,ПРИСЯДКА,ПРИТВОРА,ПРОСЛО
ЙКА,ПРОТРАВИТ ЕЛЬ,ПТИЧКА,РАЗВОДЫ,РОСКОШНОСТЬ,СИ ГАРА,СКАКАНЬЕ,СКЕРЦО,СКЕТЧ,СЛАБ
ОСИЛЬНОСТЬ,СОВОК,СПАД,СПИДВ ЕЙ,СТАНКОСТРОЕНИ Е,СТ ЕНА,СТЬЩОБА,СФЕРИЧНОСТЬ,СФОРМИ
РОВАНИ Е,Т ЕПЛОКРОВНОЕ,ТЫКВЕННЫЕ,УЗЛОВАТОСТЬ,УПОИТ ЕЛЬНОСТЬ,УСЛОВИЕ,УСТР ЕМЛ ЕНИ Е,
УСТУПЧАТОСТЬ,ФАЗА,ФУНТИК,ХРЕСТОМАТИЙНОСТЬ,ЧЕРТЫХАНЬЕ,ЭПИЛЕПТИЧКА,ЭСТЕТКА, ...]
Таким образом, благодаря обобщённым процедурным типам Func нам не при¬
шлось объявлять новые процедурные типы, а анонимные функции избавили нас
от написания полноценных именованных функций.Но не возникло ли у вас ощущения, что и процедурные переменные здесь из¬
лишни, ведь они нужны только для того, чтобы передать анонимную функцию в
функцию сортировки? - Действительно, мы можем отправить в функцию сорти¬
ровки непосредственно анонимную функцию:Console.ForegroundColor := ConsoleColor.Magenta;Println;lstInt := BubbleSort&cinteger>(lstNum, function(n1, n2) ->beginResult := n1 > n2;
end );Println(lstlnt);Println;lstStr2 := BubbleSort&cstring>(lstStr, function(s1, s2) ->beginResult :=(IComparable(s1)).CompareTo(s2) > 0;
end );Println(lstStr2);Заменив
прежний код
исправлен¬
ным, мы по¬
лучим пра¬
вильные ре¬
зультаты, из¬
бежав созда¬
ния не
только име¬
нованных
функций, но
и процедур¬
ных переменных для хранения их адресов.Е’. Анонимные функции — □ X[10,17,25,56,60,63,65,68,83,96,106,107,111,125,137,145,153,160,161,178,195,19
9,201,204,207,208,235,246,246,247,261,263,269,297,306,310,314,315,315,329,336
,337,352,398,400,402,403,404,407,408,410,414,442,458,462,464,491,510,511,515,
528,549,561,608,615,643,647,661,707,715,749,753,755,768,770,772,778,789,791,7
91,822,831,872,893,904,915,924,935,936,937,939,943,944,948,957,961,962,974,97
3,997 ][АДЫГЕЙКА,АРХАИКА,АРХАЛУК,АУДИЕНЦИЯ,БЕЛУХА,БЛАГОВЕЩЕНИЕ,БРАМИН,БРИФИНГ,ВЕДОМС
ТВО,ВМЯТИНА,ВРАЖДА,ГАМБИТ,ГОВЕНЬЕ,ГОЛУБИКА,ГОТ,ГРИМ,ДЕБАТЫ,ДУХОБОР,ДУХОБОРКА,
ЕРЕТИЧКА,ЖЕРТВЕННОСТЬ,ЗАДЫ,ЗАЗНАЙКА,ЗАПУСК,ЗЕНИТЧИЦА,ИГУМЕН,КИКИМОРА,КОСТРА,К
РЕЙСЕРСТВО,КУЛЕШ,ЛЕВИАФАН,ЛИНЗА,ЛЮПИН,МАЛОЛЮДСТВО,МНОГОНАЧАЛИЕ,МОНОТОННОСТЬ,М
ЮРЕПЛАВАТЕЛЬНИЦА,МЫСИК,МЭР,МЯКИНА,НАДУВКА,НАМЁТ,НАМИНКА,НАРОСТ,НАРЯД,НАТУЖНОС
ТЬ,НЕИЗРЕЧЕННОСТЬ,НЕОБХВАТНОСТЬ,НЕПОРЯДОК,НЕСКЛЁПИСТОСТЬ,ОБИНЯК,ОДНОКУРСНИК,О
КОЛИЧНОСТЬ,ОТСАЖИВАНИЕ,ПАРКЕТЧИК,ПАТРИОТИЧНОСТЬ,ПАЯЛЬНИК,ПЕРЕГРУЖЕННОСТЬ,ПЕРЕ
СТРАХОВЩИК,ПЕСЕЦ,ПОДПИЛКА,ПОДРЯД,ПОЛИВ,ПОЧТАЛЬОНША,ПРЕДОТВРАЩЕНИЕ,ПРИЧЁСЫВАНИ
Е,РАЗВЛЕКАТЕЛЬНОСТЬ,РАСПОЛОЖЕНИЕ,РАСПУТЬЕ,РЕЗЕДА,СИВОДУШКА,СКОНЧАНИЕ,СКУЛА,СМ
ЮТКА,СТАРОЗАВЕТНОСТЬ,СТАРТЕР,СТОЛБНЯК,ТЕБЕНЁВКА,ТИК,ТИРЕ,ТРАТА,ТРОМБОЗ,УВАЖЕН
ИЕ,УЛЬТРАМАРИН,УПРАЖНЕНИЕ,ФАРВАТЕР,ФОРМАЛИСТКА,ФРОНДЁРСТВО,ФУКСИЯ,ФУТБОЛИСТ,X
ЛНТЫЙКА,ХАРАКТЕРНОСТЬ,ХЛЫСТОВСТВО,ХРИСТИАНКА,ЧАШЕЧКА,ШИФР,ЩЕЛКОПЁР,ЩЕНЕНИЕ,ЯБ
ЬіОЧКО,ЯНКИ, ...]
Как вы видите, анонимные функции облегчают написание исходного кода про¬
граммы и делают разработку программ более удобной. Однако для компилятора
это дополнительная работа, поскольку он создаёт из наших анонимных функций
именованные функции, которые используются в программе так, как и раньше. Та¬
кие послабления программистам называют синтаксическим сахаром, с которым
мы встретимся ещё при изучении лямбда-выражений.И тут самое время вспомнить, что обобщённый класс List<T> имеет метод сорти¬
ровки Sort:procedure Sort ( comparison: Comparison<T>);В отличие от нашей пузырьковой функции он сортирует исходный список, а не
возвращает новый, отсортированный, но зато работает так быстро, что мы мо¬
жем сортировать практически любые списки!В качестве арнумента ему следует передать процедурную переменную compari¬
son типа Comparison<T>:function Comparison<T>( t1, t2 : T): integer;Обратите внимание, что она возврашает значение типа integer, а не boolean, как
наши методы сравнения!Если t1 < t2, то возвращаемое значение меньше нуля
Если t1 = t2, то возвращаемое значение равно нулю
Если t1 > t2, то возвращаемое значение больше нуля.Мы опять можем создать пару процедурных переменных типа Comparison<T> из
анонимных функций:Ivar compi: Comparison<integer> := function(n1, n2) ->begin
Result := n1 - n2;end;var comps: Comparison<string> := function(s1, s2) ->beginResult :=
(IComparable(s1)).CompareTo(s2);
end;И передать их методу сортировки:Println;lstNum.Sort(compi);Println(lstNum);Println;lstStr.Sort(comps);Println(lstStr);Либо отказаться от написания лишнего кода и передать в метод сортировки непо¬
средственно анонимные функции:Console.ForegroundColor := ConsoleColor.Yellow;Println;lstNum.Sort(function(n1, n2) ->
beginResult := n1 - n2;end);Println(lstNum);Println;lstStr.Sort(function(s1, s2) ->
beginResult := (IComparable(s1)).CompareTo(s2);end);Println(lstStr);
Оба способа хороши, но второй значительно короче. А результат сортировки мы
получим одинаковый:■ Анонимные функции□X[1,22,31,42,47,49,63,64,77,104,104,107,11В,16В,167,170,186,197,210,217,221,2В
8,248,249,254,257,276,286,287,290,292,312,313,319,333,344,348,360,379,386,389
,390,396,398,417,434,442,452,461,486,510,524,549,556,556,562,568,568,632,647,649,653,678,688,704,716,727,730,732,736,741,742,752,757,779,782,820,822,829,8
50,851,857,866,876,876,878,897,920,922,929,935,937,937,942,942,945,959,959,971,982,...][АЛЛОПАТ,АН Г ЕЛ,БЛАГОЕОННОСТЬ,БОМБАРДИРОВЩИК,БРОШКА,ВАНИЛЬ,ВЗАИМ00БУСЛОВЛ ЕННОС
ТЬ,ВИСЕЛИЦА,ВОДИТЕЛЬНИЦА,ВОПРОСНИК,ВЫКИДКА,ВЫПУСКНИЦА,ВЫСОТНИЦА,ГОЛУБЕЦ,ГРАФО
МАИ,ДЕЛЕШЕ,ДЕРНИСТОСТЬ,ДЕСПОТИЗМ,ДИПЛОМАТИЧНОСТЬ,ДОБАВКА,ДОВ ЕРЧИВОСТЬ,ДОСТОП
РИМЕЧАТЕЛЬНОСТЬ,ДРУЖИНА,ЖИВЧИК,ЖИЛИЦА,ЗАДОЛЖЕННОСТЬ,ЗВУКОПРОНИЦАЕМОСТЬ,ЗМЕЙ,К
АНЬОН,КАПИТУЛЯНТ,КАСКЕТКА,КИСЕЯ,КЛЕВЕТНИЦА,КЛЕЙКОСТЬ,КРОЙКА,ЛАРЁЧНИК,ЛЕНИНЕЦ,
ЛИЦЕНЗИЯ,ЛОПОТУН,ЛЮТ ЕРАНКА,МАМКА,МНОЖЕСТВО,МУСУЛЬМАНИН,МУЧИТ ЕЛЬ,НАРОДНИЦА,НАС
ИЛОВАНИЕ,НАСТАВКА,НИЗ,ОБИДЧИК,ОБЩНОСТЬ,ОВОД,ОЛЬХОВНИК,ОТКОС,ОЧАГ,ОЧИНКА,ОЧКОВ
ТИРАТ ЕЛЬНИЦА,ПЛЮРАЛИСТ,ПОЛО,ПОМЕСТИТ ЕЛЬНОСТЬ,ПОМПОН,ПОТРЕСКИВАНИЕ,ПРЕЕМНИК,ПР
ОЕКТАНТ,ПРОТАЛИНА,ПУТЧ,ПЯТИУ ГОЛЬНИК,РАЗЖИМ,РАШПИЛЬ,Р ЕТРОГРАД,САНТ ЕХНИКА,СВ ЁРТ
ЫВАНИЕ,СЕМИОТИКА,СИБАРИТ,СКУДНОСТЬ,СЛАДОСТРАСТНОСТЬ,СМЕНЩИЦА,СНОВАНИЕ,СОРОКОУ
СТ,СОУЧАСТИЕ,СПАЛЬНЯ,СРИСОВЫВАНИЕ,СТ ЕРХ,СХОЛАСТ,ТВОРЧ ЕСТВО,Т ЕКИНКА,ТРУБОУКЛАД
ЧИК, ТУ Н ЕЯДКА, У СП ЕВАНИЕ, У Т ЁС, УТИЛЬСЫРЬЁ, УТРО, УХАЖЁР, ФАТАЛИЗМ., ФРАМУ ГА, ХЛОРКА, ХО
ЛУЙСТВО,ЧЕРНОСОТЕНЕЦ,ЧИРИКАНЬЕ,ЧУМИЧКА,ЮЖАНКА,...]Столь же просто мы отсортируем элементы списков в обратном порядке. Доста¬
точно поставить знак минус://обратная сортировка:Println;Console.ForegroundColor := ConsoleColor.White;
lstNum.Sort(function(n1, n2) ->
beginResult := -(n1 - n2);end);Println(lstNum);Println;lstStr.Sort(function(s1, s2) ->
beginResult := -(IComparable(s1)).CompareTo(s2);end
Println(lstStr);■ Анонимные функции□X[934,982,956,943,947,938,930,923,920,918,907,903,384,360,860,843,841,839,837,8
33,832,827,819,812,806,791,791,778,77Ѳ,766,762,741,678,675,665,636,627,62&,617
,594,583,579,578,577,575,570,553,551,551,545,520,515,478,468,447,442,438,438,4
33,416,387,368,363,362,344,329,324,303,294,291,290,286,276,268,255,255,243,237
,228,194,194,189,184,165,160,149,135,115,115,105,97,96,89,84,74,58,41,30,27,14[ЭЛЛИПСИС,ШТИФТ,ШЕЙКА,ЧИКАНЫЕ,ЧЕЛОВЕКОНЕНАВИСТНИЦА,ХРОНИКА,ХОЗДОГОВОР,ФИНИШИРО
ВАНИЕ,УМНОСТЬ,УЛИЦА,УЗОРНОСТЬ,УБЛАЖЕНИЕ,ТРИО,ТРАКТОВКА,ТОТАЛЬНОСТЬ,ТОЛЬ,ТИРАНИ
Я,ТАТУИРОВКА,СЧИТЧИЦА,СУТЕНЁР,СУДОГОВОРЕНИЕ,СУДАРЫНЯ,СТАВЛЕННИЦА,СМЕРТНИЦА, СКО
РО ГОВОР КА,С КАЛ КА,С ГОН,С В И Н ЬЯ,САМОГОНЩИ ЦА,Р ЫЧА Г,РОС ТОВЩИ ЦА,РАС ШИ ВАН И Е,РАСФРАНЧЁ
ННОСТЬ,ПЯТНИЦА,ПРОСТРАНЩИЦА,ПРОГРЕВАНИЕ,ПРОВОРНОСТЬ,ПРИУМЕНЬШЕНИЕ,ПРИСПОСОБЛЕН
НОСТЬ,ПРИПАДОЧНЫЙ,ПОЛУТОРКА,ПОЛЁГЛОСТЬ,ПОКРАЖА,ПЛАНЕТОХОД,ПИЩЕВОД,ПИСК,ПИРОТЕХ
НИК,ПИЖОН,ПЕЧУРКА,ПЕРИСТАЛЬТИКА,ПЕРЕВОДЧИЦА,ПАНОРАМА,ОТЪЕДИНЕНИЕ,ОТСРОЧКА,ОТНЕ
СЕНИЕ,ОТКЛЁПКА,ОСТОВ,ОСАДКИ,ОКОСТЕНЕЛОСТЬ,ОБЫКНОВЕНИЕ,ОБРАБОТКА,ОБЛАДАНИЕ,ОБЕЗ
ВОЖИВАНИЕ,НОСИК,НИТРАТ,НЕУДЕРЖИМОСТЬ,НЕИСПРАВНОСТЬ,НЕВООБРАЗИМОСТЬ,МУСЛИН,МОДА
ЛЬНОСТЬ,ЛОПУХ,ЛОБ,ЛИБРЕТТИСТКА,КУШЕТКА,КОПИРОВАНИЕ,КОНСЬЕРЖ,КАНУН,ИЗНАШИВАЕМОС
Т Ь,ИДОЛ,ЗУБЧАТКА,3 В Е 3ДА,3АКОН Ч Е Н НОС Т Ь,ДУ ЛЕВАЯ,ДРЯНЬ,ГОНЧАЯ,ГОБОИ С Т КА,ГАДЮКА,ВО
Л ОКУ III А,ВОБЛА,ВНЕС ЕНИЕ,ВИРТУОЗ,ВИЗИГА,ВЗЛОМ,ВЕДОМСТВЕННОСТЬ,ВАФЛЯ,БУР,БРОНИРОВА
НИЕ,БЕРЕГ,БАЛАЛАЕЧНИК,БАБКА,...]И отсортируем элементы списков по длине://сортировка по длине:Println;Console.ForegroundColor := ConsoleColor.Cyan;
lstNum.Sort(function(n1, n2) ->
beginvar len1 := n1.ToString().Length;
var len2 := n2.ToString().Length;
var res: integer;
if (len1 = len2) then
res := n1 - n2
elseres := len1 - len2;Result := res;end);Println(lstNum);Println;
lstStr.Sort(function(s1, s2) ->
beginvar len1 := s1.Length;
var len2 := s2.Length;
var res: integer;
if (len1 = len2) then
res := (IComparable(s1)).CompareTo(s2)elseres := len1 - len2;Result := res;
end
);Println(lstStr);На этот раз анонимные функции получились многострочными, но никаких слож¬
ностей в их написании мы не встретили:■ Анонимные функции — □ X[11,17,27,42,69,35,125,126,155,157,166,160,174,175,177,194,204,205,209,230,246
,266,289,294,334,338,339,345,351,353,358,365,371,378,384,380,405,422,423,420,4
31,439,439,454,483,486,504,511,521,526,527,528,534,545,571,572,573,576,577,581
,581,584,505,592,594,606,615,654,660,677,601,690,700,761,762,765,765, 766,769, 7
03,792,011,016,016,017,023,023,027,046,053,054,070,095,909,913,930,939,960,974
,996,...][БОН,ВОЯЖ,ЛУНЬ,НЕГА,БАГЕТ,БАСМА,БЮВАР,ГОДОК,КУМИР,ПАДЁЖ,СКИРД,СМРАД,СПИРТ, ІЦИПО
К,БАЛАНС,БРЮШКО,ВЗГЛЯД,ДАЯНИЕ,КУКОЛЬ,ОСТРОГ,ПЛАТЬЕ,ПЛЮСНА,ПРИЦЕЛ,РОДНИК,ФИЗИКА
,АМФИБИЯ,БЕГЕМОТ,ВЕНТИЛЬ,ВЁРСТКА,ГАДАНИЕ,ГЕОМЕТР,ГОЛУБЕЦ,КУРЁНОК,ЛИМУЗИН,МЕДБР
АТ,ОПУХОЛЬ,ОТМОЧКА,РЕБЁНОК,УГЛЕЖОГ,БОЛТОВНЯ,ДОЛГУНЕЦ,ЗАКЛЯТИЕ,КАМУФЛЕТ,МАНТИЛЬ
Я,ПИРАМИДА,ПЛАШКОУТ,ПОВЕСТКА,ПОРТЯНКА,ПЫШНОСТЬ,РАКЕТЧИК,РИФЛЕНИЕ,ТАРАТОРА,ТРУЖ
ЕНИК,ЦИСТЕРНА,АДВЕНТИСТ,МАГИСТРАТ,ПОХВАЛЬБА,ПРАПОРЩИК,СЪЕЗЖАНИЕ,ТЕЛЬНЯШКА,ТРОГ
ШДИТ,ЧЕРНИЧИНА,БЕЛЛАДОННА,ИЗБАВИТЕЛЬ,КАПРИЗНИЦА,МНОГОПОЛЬЕ,НАДХВОСТЬЕ,ОРУЖЕНО
СЕЦ,РО3ГОВЕНЬЕ,СОМНАМБУЛА,ЦИРКУЛЯЦИЯ,ВОЛЬНОЛЮБИЕ,3АПАХИВАНИЕ,ЛЕЙБОРИСТКА,НИЗМЕ
ННОСТЬ,ОПУСТЕЛОСТЬ,ПРОКУРАТУРА,РОДСТВЕННИК,РЬІБОВОДСТВО,УПРАВЛЯЮЩИЙ,КОЛОРИТНОСТ
Ь,КОНЦЕРТАНТКА,ЦЕРЕМОННОСТЬ,ВМЕШАТЕЛЬСТВО,НЕДОУМЕННОСТЬ,ШЕРОХОВАТОСТЬ,БЕЗОШИБО
ЧНОСТЬ,ГУМАНИТАРНОСТЬ,НЕДОРАЗВИТОСТЬ,ОСТЕРВЕНЕЛОСТЬ,ПЕРЕВОПЛОЩЕНИЕ,СОБИРАТЕЛЬН
ИЦА,ЮЖНОАМЕРИКАНКА,БЕЗРАЗДЕЛЬНОСТЬ,КУЛЬТИВИРОВАНИЕ,РАЗНООБРАЗНОСТЬ,ЧЕТВЕРОКЛАС
СНИК,МАЛОГАБАРИТНОСТЬ,СОДЕРЖАТЕЛЬНОСТЬ,ОГНЕПОКЛОННИЧЕСТВО,...]Считается, что анонимные функции должны состоять не более чем из 3-5
строк. Иначе лучше определить именованную функцию.
I При сортировке по длине обобщённая именованная функция CompareLen<T>
была бы более уместна, чем аналогичная анонимная функция.Как вы видите, анонимные функции очень похожи на именованные, но описыва¬
ются внутри функции, не имеют идентификатора и для них не нужно явно указы¬
вать тип возвращаемого значения.Как обычно, параметры анонимной функции и её локальные переменные видны
только внутри тела функции. Однако анонимная функция может использовать ло¬
кальные переменные (а также значимые параметры) функции, в которой она
находится (но не локальные переменные, объявленные в блоке кода внутри функ¬
ции - например, в теле операторов if или for - если сама анонимная функция не
находится в теле этих операторов). Такие переменные называются внешними
(outer variables). Говорят, что анонимная функция захватывает внешнюю перемен¬
ную, и тогда та теряет статус локальной переменной и продолжает существовать
до тех пор, пока анонимная функция не будет уничтожена сборщиком мусора. Та¬
кой способ использования внешних переменных в анонимной функции называют
замыканием (closure). В этом случае компилятор создаёт новый класс для аноним¬
ной функции, который имеет открытое поле с захваченной внешней переменной.
В ряде случаев это может привести к изменению поведения таких переменных,
так что пользуйтесь замыканиями с осторожностью.Множественные процедурные переменные могут ссылать на несколько имено¬
ванных и анонимных функций. Функции добавляются в список вызовов с помощью
операторов + и +=. Функции можно удалять из списка вызовов так, как это было
описано раньше.“)
Лямбда-выраженияЛямбда-выражения - это следующий шаг по упрощению синтаксиса и очередной
синтаксический сахар, так как они всё равно преобразуются компилятором в ано¬
нимные функции. В современном программировании лямбда-выражения практи¬
чески полностью вытеснили анонимные функции.Поскольку лямбда-выражения это всего лишь другая форма записи анонимных
функций, то их используют совершенно аналогично. Вполне разумно перевести
наши анонимные функции в лямбда-выражения, прежде чем заняться их деталь¬
ным изучением.Начните новое приложение и скопируйте в него код из предыдущего проекта, ис¬
ключая сортировку списков.Сравним процесс создания процедурных переменных из анонимных функций:var compi: Func<integer, integer, boolean> := function(n1, n2) ->
beginResult := n1 > n2;end;var comps: Func<string, string, boolean> := function(s1, s2) ->
beginResult := (IComparable(s1)).CompareTo(s2) > 0;end;И из лямбда-выражений:I var compi2: Comparison<integer> := (n1, n2) -> n1 - n2;
var comps2: Comparison<string> := (s1, s2) ->(IComparable(s1)).CompareTo(s2);Сразу бросается в глаза, что лямбда-выражения короче. Для создания процедур¬
ных переменных не нужно указывать ключевое слово function и типы параметров.
Переменная Result также отсутствует, поскольку она предполагается после
лямбда-оператора ->. При желании эту переменную можно указать, но тогда
лямбда-выражение следует заключить в операторные скобки:
I var comps3: Comparison<string> := (s1, s2) -> beginResult:= IComparable(s1).CompareTo(s2) end;Запись(список_параметров) -> выражениеназывается лямбда-выражением.Запись(список_параметров) -> begin операторы; end;называется лямбда-оператором. Лямбда-оператор отличается от лямбда-выраже¬
ния тем, что имеет операторные скобки, в которые заключены операторы (или
один оператор).ІТо есть лямбда-выражение - это запись, которая состоит из единственного
выражения, не заключена в операторные скобки и не заканчивается точкой с
запятой.Если список параметров состоит из одного элемента, то круглые скобки допуска¬
ется не ставить. Если параметры отсутствуют или их больше одного, то круглые
скобки необходимы. Если параметров несколько, то они перечисляются через за¬
пятую.Если компилятор может самостоятельно вывести типы параметров, то их не
нужно явно указывать в списке параметров. В противном случае тип параметров
указывается, как обычно.Оператор -> делит лямбда-выражение (или лямбда-оператор) на две части. Слева
находится список параметров анонимной функции, а справа - выражение или
операторы, которые могут возвращать какое-либо значение (или ничего не воз¬
вращать).“)
Создав процедурные переменные compi2 и comps2 с помощью лямбда-выраже¬
ний, мы можем использовать их как параметры в методах сортировки:// естественная сортировка:Println;Console.ForegroundColor := ConsoleColor.Yellow;Println;lstNum.Sort(compi2);Println(lstNum);Println;lstStr.Sort(comps2);Println(lstStr);Этот код в точности повторяет код из предыдущего проекта, поскольку процедур¬
ным переменным всё равно, каким именно способом они были созданы. И ме¬
тоды сортировки действуют так же, как и раньше:[6,16,24,25,28,63,78,93,128,152,159,160,166,176,180,109,191,208,215,216,231,2
32,247,259,259,262,265,275,294,297,310,311,317,318,320,328,331,355,425,451,46
0,465,478,483,488,514,520,527,545,554,574,583,586,594,595,606,607,625,628,640
,646,651,662,663,674,677,682,696,699,715,720,732,746,747,760,769,795,798,801,317,846,853,362,872,878,892,894,917,918,920,924,924,942,956,971,971,974,976,996,997,...][АИСТИХА,АСПИРАНТ,БАРРАЖИРОВАНИЕ,БИТУМ,Б/1 АГОРОДИЕ,ВАЛЬС,ВЕНЕЦ,В ЕТХОЗАВ ЕТНОСТЬ
,ВОЕНЩИНА,ВОЗНИЦА,ВРЕМЯПРЕПРОВОЖДЕНИЕ,ГАМАША,ГРУЗОПОЛУ1ЧАТ ЕЛЬ,ДВАДЦАТКА,ДВУХ/1 Е
ТОК,ДЕЛИТЕЛЬ,ДО ГАДЛ И ВОС Т Ь,ДОХОДНОС ТЬ,ЖЁСТКОСТЬ,ЗАЖИМ,ЗАУМЬ,ЗЛОБНОСТЬ,ЗОЛОТОПР
ОМЫІЛЛЕННОСТЬ,ИЗМОРОСЬ,ИМИТАЦИЯ,ИНДИГО,КАСТЕТ,КИНОАППАРАТ,КИСЛОТА,КОЛЛОИД,КОЛО
ДЕЦ,КРОССВОРД,ЛАЙНЕР,ЛЕГКОАТЛЕТ,ЛЕСОЗАГОТОВКИ,ЛИРИКА,ЛИТР,МАГНОЛИЯ,МЕДАЛИСТКА
,МИЛОСЕРДИЕ,МИМИКРИЯ,МИРТОВЫЕ,МЯТЕЖНИЦА,НАЯДА,НЕМОЧЬ,НОСКА,ОГЛЯДКА,ОДЕЖДА,03 В
&ЧИВАНИЕ,ОСОКА,ОТПИЛКА,ОЦИНКОВЫВАНИЕ,04 ЕВИДИЦА,04 ЕРЁДНОСТЬ,ПАСПОРТ,ПЕЛ ЕНАНИЕ,
ПЕЛЕРИНА,ПОВЕРКА,ПОДАТЬ,ПОДНАЧАЛЬНОСТЬ,ПОЛИТИКАНСТВО,ПОЛУСТИШИЕ,ПОСТОЯННОСТЬ,
ПР ЕДВЗЯТОСТЬ,ПРИБЫВАНИЕ,ПРИЧАЩЕНИ Е,ПРОМОКАШКА,ПУРИТАНИН,РАЦИОНАЛИСТ,Р ЕВМОКАРД
ИТ,Р ЕФОРМИСТКА,РЖАВОСТЬ,РИСОВКА,РЮМОЧНАЯ,СЕ ЕРСТНИК,СЕ ЕТОЛЮБИБОСТЬ,СКЛ ЁПЫВАНИЕ
,СКОЛАЧИВАНИЕ,СКРЕПЕРИСТ,СОРБИТ,СОХРАННОСТЬ,СОЧИНИТЕЛЬНИЦА,СТАРОЗАВЕТНОСТЬ,ТА
БАЧНИК,ТИНИСТОСТЬ,ТОЛСТОРОГ,УДОД,УМИЛЕНИЕ,ФЛ ЕШЬ,ФУТУРОЛОГ,ЧЕКИСТКА,Ч ЕЛОВ ЕКОН Е
НАВИСТНИЧЕСТВО,ЧУВСТВЕННОСТЬ,ШТОРА,ШТУКАТУР,ШУ ГА,ШУШУКАНЬЕ,ЩИКОЛОТКА,ЭКЗ ЕКУЦИ
Я,ЭЛЕКТРОТЕРАПИЯ,...]
И опять так же, как и раньше, мы можем передать в метод сортировки непосред¬
ственно лямбда-оператор:Console.ForegroundColor := ConsoleColor.Magenta;Println;lstNum.Sort((n1, n2) ->
beginResult:= n1 - n2;
end);Println(lstNum);Println;lstStr.Sort((s1, s2) ->
beginResult:= (IComparable(s1)).CompareTo(s2);
end);Println(lstStr);Результат сортировки ничем не отличается от того, что показан на рисунке выше.
Для обратной сортировки необходимо передать в метод сортировки одностроч¬
ные лямбда-выражения:// обратная сортировка:Println;Console.ForegroundColor := ConsoleColor.White;
lstNum.Sort( (n1, n2) -> -(n1 - n2) );Println(lstNum);Println;lstStr.Sort( (s1, s2) -> -(IComparable(s1)).CompareTo(s2) );
Println(lstStr);Сортировка по длине опять выглядит тяжеловато из-за множества операторов в
теле лямбда-оператора:I// сортировка по длине:Println;Console.ForegroundColor := ConsoleColor.Cyan;
lstNum.Sort((n1, n2) ->
beginvar len1 := n1.ToString().Length;
var len2 := n2.ToString().Length;
var res: integer;
if (len1 = len2) then
res := n1 - n2
elseres := len1 - len2;Result := res;end);Println(lstNum);Println;lstStr.Sort((s1, s2) ->
beginvar len1
var len2= s1.Length;= s2.Length;
var res: integer;
if (len1 = len2) then
res := (IComparable(s1)).CompareTo(s2)elseres := len1 - len2;Result := res;
end);Println(lstStr);Но зато хорошо видно, что тело лямбда-оператора ничем не отличается от тела
анонимной функции, так что лямбда-операторы можно легко получить из ано¬
нимных функций.В программировании лямбда-выражения используются значительно чаще, чем
лямбда-операторы. Например, мы хотим найти в списках числа или слова-палин¬
дромы.Пишем обобщённую функцию для определения палиндромности заданного объ¬
екта n:I function IsPalindrom<T>(n : T): boolean;
beginvar str := n.ToString();
var len := str.Length;// слово-палиндром?for var i := 1 to len div 2 dobeginvar ch1 := str[len - i + 1];
var ch2 := str[i];
if (ch1 <> ch2) then
begin// не палиндром:Result:= false;
exit;
end;
end;// палиндром:Result:= true;
end;Теперь с помощью оператора запроса Where, который имеется у списков, мы
легко составим новые коллекции - из чисел и слов:// ищем палиндромы:Console.ForegroundColor := ConsoleColor.Green;Println;var pali := lstNum.Where(n -> IsPalindrom&<integer>(n));Println(pali);Println;var pals := lstStrAll.Where(s -> IsPalindrom&<string>(s));
Println(pals);Методу расширения Where мы передаём простое лямбда-выражение, включаю¬
щее вызов функции IsPalindrom.На рисунке зелёным цветом напечатаны искомые палиндромы:
■ Лямбда-выражения□ X[3,10,18,19,19,20,29,31,38,38,64,67,67,84,110,122,124,123,132,139,142,156,172
,179,182,183,21Ѳ,214,223,231,242,248,256,266,267,277,278,308,31Ѳ,329,336,341,
349,357,365,374,386,399,409,427,433,436,459,467,477,499,503,508,511,555,564,5
76,622,665,693,701,743,748,754,755,772,782,815,817,821,335,343,353,361,366,87
8,380,881,912,913,914,930,932,935,945,949,954,957,967,967,970,972,976,976,991[ВЬЮК,КЕТА,МЕХА,ПЕРС,САБО,СИТО,ТРОС,ЦИАН,ШЕЙК,ШЕЙХ,ШКАФ,ВЫКУП,КАНВА,НЕМЕЦ,ОРЕ
ОЛ,ПЛИЦА,ПУГАЧ,ПЯТАК,ТАБАК,ТЫЧОК,ЦИТРА,ШЕЙКА,ШКУРА,АБАЖУР,БРЕВНО,ГЛОТКА,ЗАРЕВ
р,ИХТИОЛ,КОЖНИК,МАССИВ,МНЕ НИЕ,ПАГУБА,СПЕРМА,ТРУБАЧ,АВТОМАТ,БОЛОНЬЯ,ВОДОНОС,КЕ
РОГАЗ,КЛЕВЕТА,ПОДХВАТ,РЕЧЕНИЕ,СУППОРТ,ШАМПУНЬ,АКУСТИКА,БОДРОСТЬ,ВЕЛИЧИНА,ВЫБО
РІЦИК,ДЕДУКЦИЯ,КАРБОЛКА,РАССУДОК,УНИЖЕ НИЕ,УТОЛЕНИЕ,ШАТКОСТЬ,АППЕ НДИКС,ВЕРТОПРА
рс,ГОСТИНИЦА,ДИАГРАММА,ИСТЕЧЕНИЕ,КОНТОРЩИК,КРЕТИНИЗМ,МАТЕМАТИК,МЕЧТАТЕЛЬ,ОДНОП
WTKA,ПОДДЕ РЖКА,ПРИТОЛОКА,ПРОПОВЕДЬ,СМАЧНОСТЬ,СТАЦИОНАР,УКРЫВАНИЕ,ХОЗРАСЧЁТ,ЧЕ
РНИЧИНА,ШПАРГАЛКА,ЭКУМЕНИЗМ,АВАНТЮРИЗМ,ВОДОБОЯЗНЬ,ДОКУЧНОСТЬ,ИДЕОГРАФИЯ,ИНКВИ
ЗИТОР,КОРОБЕ ЙНИК,ЛАРИНГОЛОГ,ПРИЖИ ГАНИЕ,РУКОПАШНАЯ,СЕНСУАЛИЗМ,СЛЕДОВАНИЕ,СПИЛИ
ВАНИЕ,ТРАВМАТИЗМ,УГОЛОВНИЦА,ФИЛИППИНЕЦ,ДЕГЕНЕРАТКА,НЕУРОЧНОСТЬ,ОЧАРОВАТЕЛЬ,РА
ЦИОНАЛИЗМ,ТЕМПЕРАТУРА,СЕПАРАТНОСТЬ,ДЕРЖАТЕЛЬНИЦА,НЕУДЕРЖИМОСТЬ,ПЯТИКЛАССНИЦА,
СВИДЕТЕЛЬСТВО,ОБЕЗОРУЖИВАНИЕ,ЭКСПЕДИРОВАНИЕ,...][3,242,555,843,873,949][БОБ,ДЕД,ДОВОД,ДОХОД,ЗАКАЗ,КАБАК,КАЗАК,КОК,КОЛОК,КОМОК,МАДАМ,МИМ,НАГАН,ОКО,ОН
р,ПОП,ПОТОП,ПУП,РАДАР,РОТАТОР,РОТОР,ТАТ,ТОПОТ,ТУТ,ШАБАШ,ШАЛАШ,ШИШ]Аналогично мы можем «отфильтровать» из списка строк такие, которые не ко¬
роче, например, пяти букв, и пятая буква - А:Console.WriteLine();
int npos = 5-1;IEnumerable<string> pals = lstStrAll.Where(s => s.Length > npos &&s[npos] == 'А');Print<string>(pals.ToList());Рисунок показывает, что метод расширения Where правильно нашёл слова:
□ XЗдесь мы воспользовались методом расширения Where интерфейса Enumerable.
Другие методы расширения мы рассмотрим дальше.Последовательность - это упорядоченная коллекция однотипных элементов.
і По-английски последовательность называется sequence.Хорошим примером последовательностей могут служить арифметические и гео¬
метрические прогрессии, которые изучают в школе.Элементы последовательностей можно только просматривать. Изменить их зна¬
чения нельзя. Каждый раз нужно создавать новую последовательность.В паскале к последовательностям относятся также специализированные коллек¬
ции:• одномерные динамические массивы array of T• списки List<T>• двусвязные списки LinkedList<T>• множества HashSet<T> и SortedSet<T>Последовательности
Язык интегрированных запросов LINQLINQ (Language Integrated Query) - язык интегрированных запросов - предназна¬
чен для обработки коллекций, реализующих интерфейс IEnumerable<T>. Это могут
быть последовательности, массивы, списки, словари, базы данных и другие ис¬
точники.Чтобы получить информацию от источника данных, нужно составить запрос
(query), под которым понимают выражение из операторов запроса (их называют
также стандартными операторами запроса LINQ, или LINQ Standard Query Opera¬
tors, LSQO).Операторы запроса - это методы расширения класса Enumerable, который нахо¬
дится в пространстве имён System.Linq.Запросы чаще всего получают и возвращают последовательности (sequences) типа
IEnumerable<T>, состоящие из отдельных элементов типа Т. Роль операторов со¬
стоит в генерировании выходной последовательности (результата запроса) путём
преобразования входной последовательности, которая в запросе никогда не из¬
меняется. Как правило, не изменяется и порядок следования элементов в выход¬
ной последовательности по сравнению с их исходным порядком: если какой-либо
элемент входной последовательности стоит раньше другого, то и в выходной по¬
следовательности будет точно такой же порядок их следования.Некоторые операторы возвращают не последовательности, а отдельные эле¬
менты (поэлементные операторы), числовые (операторы агрегирования) или ло¬
гические значения (квантификаторы). Такие операторы занимают в запросе по¬
следнее место.
Операторы запросов LINQДля удобства пользования все многочисленные операторы запросов разбиты на
группы:МетодГ руппаОтложенныйAggregateМетоды агрегированияAllМетоды-квантификаторыAnyМетоды-квантификаторыAsEnumerableМетоды преобразованияVAverageМетоды агрегированияCastМетоды преобразованияVConcatМетоды для множествVContainsМетоды-квантификаторыCountМетоды агрегированияDefaultlfEmptyМетоды элементовVDistinctМетоды для множествVElementAtМетоды элементовElementAtOrDefaultМетоды элементовEmptyМетоды генерированияVExceptМетоды для множествVFirstМетоды элементов7‘)
FirstOrDefaulМетоды элементовGroupByМетоды группированияVGroupJoinМетоды объединенияVIntersectМетоды для множествVJoinМетоды объединенияVLastМетоды элементовLastOrDefaultМетоды элементовLongCountМетоды агрегированияMaxМетоды агрегированияMinМетоды агрегированияOfTypeМетоды преобразованияVOrderByМетоды сортировкиVO rderByDescendi ngМетоды сортировкиVRangeМетоды генерированияVRepeatМетоды генерированияVReverseМетоды сортировкиVSelectМетоды проецированияVSelectManyМетоды проецированияVSequenceEqualМетоды-квантификаторыSingleМетоды элементов
SingleOrDefaultМетоды элементовSkipМетоды фильтрацииVSkipWhileМетоды фильтрацииVSumМетоды агрегированияTakeМетоды фильтрацииVTakeWhileМетоды фильтрацииVThenByМетоды сортировкиVThenByDescendingМетоды сортировкиVToArrayМетоды преобразованияToDictionaryМетоды преобразованияToListМетоды преобразованияToLookupМетоды преобразованияVWhereМетоды фильтрацииVZipМетоды объединенияVI Существует множество других способов группировки этих методов, поэтому
больше ориентируйтесь этот алфавитный список, чем на конкретные группы.Отложенные методы не выполняются до тех пор, пока не вызываются методы, ис¬
пользующие элементы выходной последовательности.
Генерирование последовательностейЧтобы использовать последовательности при решении задач, их нужно сначала
создать или генерировать. Для этого в паскале имеется несколько способов.Функция RangeОсновной способ генерирования последовательности натуральных чисел - при¬
менение функции Range:function Range(a,b: integer): sequence of integer;Она возвращает последовательность целых чисел типа integer.а - первый элемент последовательности
b - последний элементЕсли a = b, то в последовательности окажется 1 элемент.Если a < b, то последовательность будет пустой.Для изучения этой важной функции пишем небольшую программу:
uses System;begin//заголовок окна:Console.Title := 'Функция Range';Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Функция Range');Console.ForegroundColor := ConsoleColor.Green;Println;// генерируем последовательность натуральных чисел:
var sq := Range(-1, 9);// печатаем её:
sq.Println;Println;Console.ForegroundColor := ConsoleColor.Red;
end.Функция Range(-1, 9) возвращает последовательность целых чисел в заданном
диапазоне от минус единицы до девяти. Чтобы узнать тип результата, подведите
к имени переменной курсор мышки:// генерируем последоват
var sq[ := Range(-lj 9)j
// печ| yar sq: sequence of irteger;|На самом деле функция Range ничего не возвращает, пока последовательность не
будет использована в программе. Такие функции называют «ленивыми».Но нам, конечно, хочется посмотреть, что же сгенерировала наша функция.У последовательностей имеются методы расширения Print и Println. Второй метод
от первого отличается только тем, что переводит печать информации на другую
строку.Так как переменная sq имеет тип sequence of integer, то мы можем применить к
ней один из методов печати (обычно это Println). Для этого после имени перемен¬
ной нужно поставить точку, а затем напечатать имя метода.В Редакторе кода имеется интеллектуальная под¬
сказка, которая по мере ввода имени метода пока¬
зывает возможные продолжения. Переведите кла¬
вишами со стрелками выделение на нужную
строку и нажмите клавишу ВВОД:Всё слово целиком будет напечатано после точки.Вместо клавиш со стрелками вы можете просто
щёлкнуть мышкой по нужной строке.Теперь у нас получилась такая «программа»:sq.рг^ МіпБу* [РгNumerateСо■^ OfTypeO
^ OrderByOOrderByDescendingOPairwisePartitionPrirt,!ндеRjJ Println^ Reverseo
// генерируем последовательность натуральных чисел:
var sq := Range(-1, 9);// печатаем её:
sq.Println;Она распечатывает последовательность на экране:■ Функция Range—□XФункция Rangeл-10123456789VТак как функция Range возвращает последовательность, то прямо к ней можно
применить метод расширения. Тогда код получится короче:I// генерируем последовательность натуральных чисел// и печатаем её:var sq := Range(-1, 9).Println;Этот способ печати используется очень часто.К последовательностям можно применять цепочкой и несколько методов. Если
строка получается длинная, то её можно переносить по точке. Так:// генерируем последовательность натуральных чисел// и печатаем её:var sq := Range(-1, 9).Println;Или так:var sq := Range(-1, 9)
.Println;
Но обязательно подписывайте идентификаторы один под другим, чтобы код был
ясным и понятным.Методы расширения Println и PrintБез методов печати не обходится ни одна программа, поэтому разберём их более
подробно.По умолчанию все числа или элементы последовательности других типов разде¬
ляются пробелом, что вы и видели на экране. В этом случае вызывается такой ме¬
тод расширения:function Println<T>(Self: sequence of T): sequence of T; extensionmethod;Его объявление выглядит довольно страшно, но использовать его очень просто.Если вы хотите разделить элементы другим символом или строкой, то исполь¬
зуйте другой метод:function Println<T>(Self: sequence of T; delim: string): sequence of T;
extensionmethod;Для разделения элементов последовательности используется строка delim.Этот метод возвращает исходную последовательность.Перейдём к примерам.Если в качестве разделителя задать пустую строку:Ivar sq := Range(-1, 9).Println('');
То все элементы будут напечатаны вплотную друг за другом:■ Функция Range—□XЛФункция Range-101234Б6789VЧасто разделителем служит запятая:var sq := Range(-1, 9)
.Println(',');■ Функция Range—□XФункция RangeА-1,0,1,2,3,4,5,б,7,3,9VИли точка с запятой:■ Функция Range—□ XФункция RangeА-1;Ѳ;1;2;3;4;5;б;7;8;9VДополнительно можно поставить пробел, чтобы числа лучше читались на экраvar sq := Range(-1, 9).Println('; ');■ Функция Range—□XФункция Rangeл-1; 0; 1; 2; 3; 4; 5; б; 7; 8; 9V
Разделителем может быть любая строка:Ivar sq := Range(-1, 9).Println(' >>> ');Функция Range-1 >>> 0 >>> 1 >>> 2 >>> В >>> 4 >>> 5 >>> 6 >>> 7 »> S »> 9Метод расширения Print мы разбирать не будем. Он действует аналогично, только
не переводит вывод на следующую строку. По этой причине используется значи¬
тельно реже.Последовательности можно выводить и обычными процедурами печати Print и
Println, если передать им последовательность в качестве аргумента:Println(sq)Но такой способ печати менее удобен. К тому же элементы последовательности
всегда разделяются запятыми:llJ Функция Range — □XЛ[-1,0,1,2,3,4,5,6,7,8,9]VТочно также печатают последовательности процедуры Write и Writeln.Функция Range (продолжение)Функция Range с двумя аргументами возвращает арифметическую последова¬
тельность с разностью 1. Но ведь могут потребоваться последовательности и с
другой разностью! Тогда нужно воспользоваться функцией Range с тремя пара¬
метрами:
j function Range(a,b,step: integer): sequence of integer;Она возвращает арифметическую последовательность, в которой:
a - первый членb - максимальное значение элементов последовательности (возможно, недости¬
жимое)step - разностьРассмотрим случай, когда b кратно step:Ivar sq3 := Range(0, 15, 3).Println;■ Функция Range—□XЛФункция RangeВ 3 6 9 12 IEVПервый член арифметической последовательности равен 0, последний - 15.
Добавляем к максимальному значению 1:var sq3 := Range(0, 16, 3)
.Println;Напечатаны те же самые числа:■ Функция Range—□XФункция Rangeл0 В 6 9 12 15V
Действительно, после числа 15 в последовательности должно следовать число 18,
а оно больше заданного максимального. Будьте внимательны, используя эту
функцию!Для вещественных чисел имеется подобная функция с тремя аргументами:function Range(a,b: real; n: integer): sequence of realНо она действует иначе!Функция Range с тремя параметрами для вещественных чисел возвращает после¬
довательность действительных чисел в диапазоне a, b, разделённом на n частей:Ivar sqr := Range(0.0, 12.0, 5).Println;■ Функция Range—□XФункция RangeЛ0 2.4 4.S 7.2 9.6 12VНе используйте эту функцию в своих проектах! Чтобы избежать путаницы, она за¬
менена функцией Partition (см. дальше).Функция Range может принимать и аргументы типа char. Тогда возвращаемая по¬
следовательность состоит из символов Юникода в диапазоне с1..с2:function Range(c1,c2: char): sequence of char;Создаём последовательность букв русского алфавита:I var sqrus := Range('a','я')
I.Println;Обратите внимание, что при печати между символами разделитель не вставля¬
ется:■ Функция Range—□XФункция Rangeла бв гдежз и й клмноп р с туфхцчшцьыь зюяVНо вы можете указать нужный разделитель в методе Println:var sqrus2 := Range^a'/я').Println(' ');I Функция Range□X:нкция RangeабвгдежзийклмнопрстуфхцчшіцъыьэюяабвгдежзийклмнопрстуфхцчшщъыьэюяВ этой последовательности отсутствует буква ё. Если вместо буквы я поставить
букву ё, то она появится, но с ненужной буквой е, которая на рисунке заменена
вопросительным знаком:Функция Rangeабвгдежзийклннопрстуфхцчшіцъыьэюя?ёС буквой ё проблемы начались давно, и так до сих пор и не решены.Можно напечатать последовательность всех символов ASCII, задавая их коды:
var sqascii:= Range(#32,#127)
.Println;llJ Функция Range — □ XФункция Range!"#$%&■()*+,-./Ѳ123456789:;<=>?@ABCDEFGHi:KLHNOPQRSTUV
MXYZ[\]-"_' abcdefghijklmnopqrstuvwxyz{ | }~SФункция Range и циклыФункцию Range можно использовать совместно с оператором цикла foreach как
альтернативу циклу for:// генерируем последовательность натуральных чисел// и печатаем её:var sq := Range(-1, 9).Println;Println;foreach var i in sq doPrint(i);Println;■ Функция Range и циклы—□XФункция Range и циклыА-10123456789-10123456789V")
В этом примере мы просто распечатали значения элементов последовательности,
но их можно использовать иначе. Например, функция Range может генерировать
индексы в массиве:var words : array of string := ('РАЗ', 'ДВА', 'ТРИ', 'ЧЕТЫРЕ', 'ПЯТЬ');foreach var i in Range(0, words.Length-1) do
beginPrint(words[i]);end;■ Функция Range и циклы—□XФункция Range и циклылРАЗ ДВА ТРИ ЧЕТЫРЕ ПЯТЬVВ цикле foreach переменная цикла имеет тот же тип, что и элементы последова¬
тельности. Важно помнить, что в этом цикле нельзя изменить значения элементов
последовательности. Можно только перебрать их.Если использовать функцию Range с тремя параметрами, то можно просмотреть
массив в обратном порядке:foreach var i in Range(words.Length-1, 0, -1) do
beginPrint(words[i]);end;■ Функция Range и циклы—□XФункция Range и циклыАРАЗ ДВА ТРИ ЧЕТЫРЕ ПЯТЬ
ПЯТЬ ЧЕТЫРЕ ТРИ ДВА РАЗV
Задавая параметру step разные значения, вы можете получить более гибкие
циклы, чем цикл for!Напечатаем чётные числа в заданном диапазоне:Print^^^h^ числа:');foreach var i in Range(2, 20, 2) dobeginPrint(i);end;■ Функция Range и циклы—□XФункция Range и циклылЧётные числа:2 4 6 S 10 12 14 16 10 20VС обычным циклом for так просто не получится!Методы расширения типа integerТип integer имеет несколько методов расширения для генерирования последова¬
тельностей. Иногда ими пользоваться удобнее, чем функцией Range.Первый метод расширения также называется Range, но он не имеет параметров:
function Range(Self: integer): sequence of integer; extensionmethod;Он действует так же, как функция Range(1,n).Последовательность всегда начинается с 1 и заканчивается числом n. Этот метод
можно применять к переменным, литералам и выражениям, имеющим тип inte¬
ger:")
// переменная типа integer:
var n := 10;
n.Range().Println;// литерал типа integer:
10.Range().Println;// выражение типа integer:
(n + 2).Range().Println;Методы расширения для типа integer — □ XМетоды расширениядля типа integer1234Б6709101234Б6789101234Б678910 11 12Вот так просто можно выполнить цикл 20 раз:I// использование в цикле:
foreach var i in 20.Range do
Print(i);■ Методы расширения для типа integer — □ XМетоды расширения для типа integer1 2 3 4 Е 6 7 S 9 10 И 12 13 14 15 16 17 18 19 20Так элементы последовательностей нумеруются с нуля, то нужна последователь¬
ность, которая выдаёт нужное число элементов, также начиная с нуля. Метод рас¬
ширения Times умеет это делать:I// использование в цикле:
foreach var i in 20.Times do
Print(i);■ Методы расширения для типа integer□ XМетоды расширения для типа integer0 1 2 3 4 5 б 7 8 9 10 11 12 13 14 15 16 17 IS 19Метод расширения To аналогичен функции Range(a, b):function &To(Self: integer; n: integer): sequence of integer;extensionmethod;Он генерирует последовательность от заданного значения до значения в скобках.В этом примере метод To выдаёт последовательность от 2 до 20, что очень удобно
использовать в цикле:I// использование в цикле:
foreach var i in 2.To(20) do
Print(i);■ Методы расширения для типа integer□ XМетоды расширения для типа integer2 В 4 Б 6 7 S 9 10 11 12 13 14 1Б 16 17 18 19 20Метод расширения Downto аналогичен функции Range(a, b, -1):function &Downto(Self: integer; n: integer): sequence of integer;extensionmethod;
То есть он выдаёт убывающую последовательность.Посмотрим, как его можно использовать в цикле:// использование в цикле:
foreach var i in 20.Downto(2) do
Print(i);■ Методы расширения для типа integer- □ XлМетоды расширения для типа integer*20 19 10 17 16 1Б 14 13 12 11 10 9 07 6 Б 4 3 2VОтличный получился цикл!Метод расширения Step генерирует бесконечную последовательность целых чи¬
сел, начиная с заданного значения:і function Step(Self: integer): sequence of integer; extensionmethod;С бесконечными последовательностями и циклами нужно быть очень осторож¬
ными и всегда предусматривать условия их завершения. Мы применим к после¬
довательности метод Take, который рассмотрим дальше. Он пропускает только
заданное число элементов и завершает дальнейшее их «производство»:// метод расширения Step:2.Step().Take(10).Println;На рисунке видно, что по¬
следовательность начина¬
ется с двойки, а каждый следующий элемент больше предыдущего на 1.ЁЗ Методы расширения для типа integer —□XМетоды расширения для типа integerл2 3 4 Б 6 7 0 9 10 11V
Мы ограничились первым десятком элементов, иначе последовательность про¬
должалась бы и дальше.Метод Step может заменить бесконечный цикл, когда число итераций заранее не
известно.Метод Step с параметром генерирует бесконечную последовательность целых чи¬
сел, начиная с заданного, с шагом step:I1.Step(3).Take(10).Println;■ Методы расширения для типа integer—□XМетоды расширения для типа integer1Л1 4 7 10 13 16 19 22 25 28VТакой же метод расширения имеется и у типа real/double:function Step(Self: real; step: real): sequence of real;extensionmethod;Он возвращает бесконечную последовательность вещественных чисел:1.1.Step(3.9).Take(10).Println;■ Методы расширения для типа integer — □XАМетоды расширения для типа integer1.1 Б S.9 12.S 16.7 20.6 24.Б 2S.4 32.3 36.2V
Функции Seq..Функции SeqRandom и SeqRandomlnteger абсолютно одинаковы. Они возвращают
последовательность из n случайных целых чисел в диапазоне a..b:function SeqRandom(n: integera: integer
b: integer= 10;= 0;= 100): sequence of integer;function SeqRandomInteger(n: integera: integer
b: integer= 10;= 0;= 100): sequence of integer;По умолчанию n равно 10, а равно 0, а b равно 100.При вызове функции без параметров она выдаст 10 случайных целых чисел в диа¬
пазоне 0..99:Ivar sq := SeqRandom
.Println;■ Функции Seq...—□XФункции Seq:ЛБ 99 94 75 82 39 22 40 67 70VА теперь вопрос: что напечатает метод Println во второй раз?var sq := SeqRandom
.Println;sq.Println;“)
Точно такую же последовательность, что и первый метод Println? - А вот и нет!
Второй метод Println напечатает новую случайную последовательность:ЁІ Функции Seq.,, — □ XФункции Seq:83 7 23 91 31 3290S3953013 77 42 34 39 61957ISS4Это значит, что функции SeqRandom и SeqRandomlnteger ленивые и при каждом
обращении к ним генерируют новую последовательность. Это нужно иметь в
виду!Если вы хотите получить не 10, а, например, 20 чисел в диапазоне по умолчанию,
то укажите новое число в скобках:Ivar sq := SeqRandom(20).Println;■ Функции Seq,,,- □ XЛФункции Seq:51 33 96 21 37 25 81 13 83 92 21 89 175 84 8 42 65 92 50VПри вызове функции SeqRandom с двумя аргументами второй аргумент означает
нижнюю границу диапазона. А верхней останется по умолчанию:var sq := SeqRandom(20, 50)
.Println;' Функции Seq,□ XФункции Seq:51 62 93 72 95 87 86 68 65 83 98 74 72 61 65 100 71 63 90 91V
И наконец, вызов функции с тремя параметрами позволяет задавать число эле¬
ментов, верхнюю и нижнюю границы диапазона. Так мы можем моделировать
подбрасывание монетки:Ivar sq := SeqRandom(20, 0, 1).Println;■ Функции Seq... — □XAФункции Seq:0 0110111110011001011VАналогичная функция SeqRandomReal имеется и для вещественных чисел:function SeqRandomReal(n: integer;a: real; b: real): sequence of real;У неё нет параметров по умолчанию, поэтому всегда нужно задавать 3 аргумента:var sq := SeqRandomReal(10, 0, 10)
.Println;■ Функции Seq,,,X□1Функции Seq:Л9.235793738270081.465803189885716.510004548593431.884996440208058.838569451513977.949943918711483.883157681619364.825367375661323.61416037834402
Б.54348096043965VФункция SeqGen возвращает последовательность элементов типа Т:
і function SeqGen<T>(count: integer; f: integer -> T): sequence of T;
count - число элементовf - функция для вычисления элементов последовательностиОна объединяет функцию Range и метод расширения Select, о котором речь пой¬
дёт дальше.С помощью этой функции легко создать любую арифметическую последователь¬
ность:I var sq := SeqGen(10, i -> i + 3).Println;■ Функции Seq...—□XАФункции Seq:3 4 Б 6 7 8 9 10 11 12VЗдесь:10 - число элементов в последовательностиi -> i + 3 - функция, вычисляющая значения элементов. Начальное значение пере¬
менной i равно 0, поэтому первый элемент i + 3 равен 3. Затем i получает значение
1, а элемент i + 3 = 1 + 3 = 4. И так далее.Если начальное значение переменной i (название может быть любым) должно
отличаться от нуля, то используйте функцию SeqGen с тремя параметрами:function SeqGen<T>(count: integer;f: integer -> T; from: integer): sequence of T;count - число элементовf - функция для вычисления элементов последовательности
from - это начальное значение переменнойПусть начальное значение переменной равно 10:
var sq := SeqGen(10, i -> i + 5, 10)
.Println;Тогда первый элемент последовательности будет равен 10 + 5 = 15:■ Функции Seq,,,—□XФункции Seq:Л15 16 17 IS 19 20 21 22 23 24VДругая функция SeqGen с тремя параметрами возвращает последовательность
элементов типа Т:I function SeqGen<T>(count: integer;first: T; next: T -> T): sequence of T;count - число элементовfirst - первый элемент последовательностиnext - функция для вычисления элементов последовательности. По значению
предыдущего элемента последовательности вычисляется следующий.С её помощью также легко получить арифметическую прогрессию:Ivar sq := SeqGen(20, 1, a -> a + 3).Println;■ Функции Seq...□ XAФункции Seq:1 4 7 10 IB 16 19 22 25 23 31 34 37 40 43 46 49 52 55 53V
Здесь:20 - число элементов в последовательности
1 - первый элементa -> a + 3 - функция для вычисления следующих элементов
Эта функция действует более понятно, чем предыдущая.И последняя функция SeqGen - с четырьмя параметрами:I function SeqGen<T>(count: integer;first,second: T; next: (T,T) -> T): sequence of T;count - число элементов в последовательности
first - первый элемент последовательности
second - второй элемент последовательностиnext - функция для вычисления элементов последовательности. По значениям
двух предыдущих элементов последовательности вычисляется следующий.С помощью этой функции легко получить последовательность чисел Фибоначчи:Ivar sq := SeqGen(20, 0, 1, (a,b) -> a + b).Println;■ Функции Seq...X□1АФункции Seq:0 1 1 2 В Е Е 13 21 34 55 39 144 233377 610 987 1597 2584 4181VЗдесь:20 - число элементов в последовательности0 - первый элемент1 - второй элемент(a,b) -> a + b - функция для вычисления следующих элементов
a + b = 1 при начальных значениях. Последовательность: 0, 1, 1. Теперь а = b = 1.
Дальше:(1.1) -> 1 + 1 = 2. Последовательность: 0, 1, 1, 2. Теперь а = 1, b = 2.(1.2) -> 1 + 2 = 3. Последовательность: 0, 1, 1, 2, 3. Теперь а = 2, b = 3.И так далее.Функция SeqWhile возвращает последовательность элементов типа Т:function SeqWhile<T>(first: T;
next: T -> T; pred: T -> boolean): sequence of T;first - первый элемент последовательностиnext - функция для вычисления элементов последовательностиpred - условие прекращения вычисленийЭта функция удобна для создания последовательностей, число элементов кото¬
рых заранее неизвестно, а определяется заданным условием.Например, создадим арифметическую последовательность с первым членом 1 и
разностью 5. Наибольший член последовательности должен быть меньше 100:Ivar sq := SeqWhile(1, n -> n + 5, n -> n < 100).Println;ЁІ Функции Seq,,, — □ XФункции Seq:1 6 11 16 21 26 31 36 41 46 51 56 61 66 71 76 SI S6 91 96Функция SeqWhile с 4 параметрами полезна при генерировании последовательно¬
стей типа чисел Фибоначчи, когда общее количество членов заранее неизвестно:
function SeqWhile<T>(first,second: T;next: (T,T) -> T;pred: T -> boolean): sequence of T;first - первый элемент последовательности
second - второй элемент последовательностиnext - функция для вычисления следующего элемента последовательности по
двум предыдущимpred - условие прекращения вычисленийСледующий код напечатает числа Фибоначчи, не превышающие 1000:Ivar sq := SeqWhile(1, 1, (n1, n2) -> n1 + n2, n -> n < 1000).Println;Функции Seq:1 1 2 3 E 8 13 21 34 55 89 144 233 377 610 987Функция SeqFill возвращает последовательность из count одинаковых элементов
типа Т:function SeqFill<T>(count: integer; x: T): sequence of T;х - значение элемента
count - число элементовSeqFill(20, 3).Println
г Функции Seq,Функции Seq:3 3333333333333333333Здесь:20 - число элементов
3 - значение каждого элементаМы рассмотрели методы, которые самостоятельно генерируют последовательно¬
сти, подчиняющиеся какому-то правилу. Иногда необходимо создать последова¬
тельность произвольных элементов. Тогда их нужно перечислить в скобках при
вызове функции Seq:і function Seq<T>(params a: array of T): sequence of T;Seq(1,5,3,2,4, 0, 11, 23, 87, 900, 132, 888, 1001).Println;llJ Функции Seq,,, —□XЛФункции Seq:1 Б 3 2 4 0 11 23 87 900 132 888 1001VФункция PartitionФункция Partition возвращает последовательность вещественных чисел в диапа¬
зоне a,..b, разделённом на n частей:і function Partition(a,b: real; n: integer): sequence of real;@
var sqr := Partition(0.0, 12.0, 10)
.Println;llJ Функция Partition —□XФункция Partition0 1.2 2.4 3.6 4.8 6 7.2 S.4 9.6 10.8 12VПо сути, это арифметическая последовательность, в которой:а - первый член
n + 1 - число членов
(a - b) / n - разностьВвод последовательностей с
клавиатурыДля ввода последовательностей из n целочисленных элементов с клавиатуры слу¬
жат функции ReadSeqInteger:і function ReadSeqInteger(n: integer): sequence of integer;I function ReadSeqInteger(const prompt: string; n: integer):sequence of integer;Вторая функция дополнительно печатает на экране поясняющую строку prompt.
Аналогичные функции для вещественных чисел:і function ReadSeqReal(n: integer): sequence of real;
function ReadSeqReal(const prompt: string; n: integer):sequence of real;var sq := ReadSeqInteger('Введите 7 целых чисел черезпробел > ', 7);sq.Println;Println;var sqr := ReadSeqReal('Введите 7 вещественных чисел черезпробел > ', 7);sqr.Println;Println;Hi Ввод последовательностей с клавиатуры — □ XВвод последовательностей с клавиатурыВведите 7 целых чисел через пробел > 12345671 2 3 4 5 5 7Введите 7 вещественных чисел через пробел > 1.2 2.3 3.4 4.5 6.7 7.8 8.9 9.0 0.1 2.1
1.2 2.3 3.4 4.5 6.7 7.8 8.9Если чисел введено больше, чем указано в вызове функции, то лишние данные
отбрасываются.Функции для строк:і function ReadSeqString(n: integer): sequence of string;function ReadSeqString(const prompt: string; n: integer):sequence of string;
Если число элементов в последовательности заранее неизвестно, то используйте
следующие функции, которые завершают ввод данных, когда не выполнится усло¬
вие cond:function ReadSeqIntegerWhile(cond: integer -> boolean):sequence of integer;function ReadSeqRealWhile(cond: real -> boolean):sequence of real;function ReadSeqStringWhile(cond: string -> boolean):sequence of string;function ReadSeqIntegerWhile(const prompt: string;cond: integer -> boolean):
sequence of integer;function ReadSeqRealWhile(const prompt: string;cond: real -> boolean):
sequence of real;function ReadSeqStringWhile(const prompt: string;cond: string -> boolean):
sequence of string;Не советую пользоваться этими функциями в своих программах.Операции над последовательностямиК последовательностям можно применять различные операции.Оператор + и метод ConcatОператор + возвращает последовательность, образованную объединением двух
(или больше) последовательностей:
function IEnumerable<T>.operator+(a,b: sequence of T): sequence of T;При этом элементы второй последовательности следуют за элементами первой
последовательности:var sq1 := Range(1, 10);
var sq2 := Range(8, 20);var res := sq1 + sq2;
res.Println;■ Операции над последовательностямиX□1ЛОперации над последовательностями1 2 3 4 5 б 7 S 9 10 В 9 10 11 12 1314 15 16 17 18 19 2ѲVЧисло элементов в результирующей последовательности равно общему числу
элементов в последовательностях-слагаемых.Эта операция называется конкатенацией. Объединить две последовательности
можно и с помощью метода расширения Concat, который действует точно так же:function Concat(second: sequence of T): sequence of T;var sq1 := Range(1, 10);
var sq2 := Range(8, 20);var res := sq1 + sq2;
res.Println;Println;sq1.Concat(sq2).Println;
■ Операции над последовательностямиОперации над последовательностями1234567S9 10S9 10 1112IB14151617IS19201 2 В 4 E 6 7 S 9 10 S 9 10 11 121314151617IS1920Примеры конкатенации трёх последовательностей:Println;var sq3 := Seq(1,11,111,1111);(sql + sq2 + sq3).Println;
sq1.Concat(sq2).Concat(sq3).Println;■ . Операции над последовательностями — □ XОперации над последовательностями1 2 3 4 5 6 7 S 9 10 S 9 10 11 121314151617IS19201 2 3 4 5 6 7 S 9 10 S 9 10 11 121314151617IS19201 2 3 4 5 6 7 S 9 10 S 9 10 11 121314151617IS192011111111111 2 3 4 5 6 7 S 9 10 S 9 10 11 121314151617IS19201111111111К последовательности можно добавить элемент совместимого типа спередиIvar sq1 := Range(1, 10).Println;
var res:= 0 + sq1;
res.Println;■ Операции над последовательностями—□XОперации над последовательностямиЛ123456739 10
0123456789 10V
Или сзади:var sq1 := Range(1, 10).Println;
var res:= sq1 + 0;
res.Println;■ Операции над последовательностями—□XОперации над последовательностямиА123456789 10
1234567S9 10 0VОператор *Оператор * возвращает последовательность, образованную повторением после¬
довательности заданное число раз:I function IEnumerable<T>.operator*(n: integer; a: sequence of T):sequence of T;Числовой множитель может занимать место и первое, и второе место в выраже¬
нии:var sq1 := Range(1
10);var res:= 3 * sq1;
res.Println;res:= sq1 * 3;
res.Println;ёЗ Операции над последовательностями — □ XОперации над последовательностями
0123456789 10
■ Операции над последовательностямиОперации над последовательностями123456789 10 123456789 10 123456789 10
123456789 10 123456789 10 123456789 10ЛVМетод расширения Cycle повторяет заданную последовательность бесконечно:function Cycle<T>(Self: sequence of T): sequence of T;extensionmethod;Так как бесконечную последовательность напечатать невозможно, то мы ограни¬
чимся первыми 30-ю элементами:Ivar sq1 := Range(1, 10);
sq1.Cycle.Take(30).Println;ёЗ Операции над последовательностями — □ XОперации над последовательностями123456789 10 123456789 10 123456789 10V
Встроенные методы расширения для
последовательностейПоследовательности нужны для того, чтобы извлекать из них полезную информа¬
цию. И тут нам не обойтись без методов расширения.Метод WhereМетод Where отбирает элементы в соответствии с условием, заданным функцией
predicate:function Where(predicate: T->boolean): sequence of T;Иначе говоря, он пропускает дальше только те элементы исходной последова¬
тельности, которые удовлетворяют условию.Создадим последовательность из 20 натуральных чисел и отфильтруем чётные:Ivar sq := Range(1,20).Println;
sq.Where(n -> n mod 2 = 0).Println;■ Метод Where- □ XМетод WhereЛ1 2 3 4 Б 6 72 4 6 0 10 12S 9 10 11 12
14 16 10 2013 14 1Б 16 17 18 19 20VВторой вариант метода Where также фильтрует элементы в соответствии с усло¬
вием, заданным функцией predicate, но в ней можно использовать индекс эле¬
мента в исходной последовательности:
function Where(predicate: (T,integer)->boolean): sequence of T;Если индекс элементов не используется, то второй метод Where вернёт ту же са¬
мую последовательность чётных чисел:Ivar sq := Range(1,20).Println;
sq.Where((n, i) -> n mod 2 = 0).Println;■ Метод Where — □ XМетодWhere12 34 Б 6 7S 9 10 11 12 13 14 15 16 17 IS 19 202 4 6S 10 1214 16 IS 20Можно отфильтровать элементы только по их индексам:var sq := Range(1,20).Println;
sq.Where((n, i) -> i > 3).Println;ill Метод Where — □ XМетодWhere12 34 5 6 7 S9 10 11 12 13 14 15 16 17 IS 19 205 6 7S 9 10 1112 13 14 15 16 17 IS 19 20И наконец, в условие могут входить как индексы, так и значения элементов:var sq := Range(1,20).Println;sq.Where((n, i) -> (i > 3) and (n mod 2 = 0)).Println;
■ Метод WhereВ этом примере мы отфильтровали чётные числа, индексы которых больше трёх.Метод Where очень часто используется при решении задач, в том числе и зани¬
мательных.В книге 600 задач на сообразительность, на странице 112 напечатана задача 41:Задача очень простая, и легко решается в уме. Но и для тренировки сгодится!Из условия задачи следует, что искомое число - трёхзначное. Мы легко получим
последовательность всех трёхзначных чисел с помощью функции Range:і Range(100,999)В этом легко убедиться, если распечатать последовательность на экране:
і Range(100,999).Println;Трёхзначное число (Range.Where)Если от трёхзначного числа отнять 7, то оно разде
лится на 7; если отнять от него 8, то оно разде
лится на 8; если отнять от него 9, то оно разде¬
лится на 9. Какое это число?Трёхзначное число^111 ^
■ 600за да ч на с g о бр азител ьн ость, стр, 112, 3 ада ча 41Итак, мы получили все трёхзначные числа и теперь должны выбрать такое число
(или, возможно, числа, если решение не единственное), которое удовлетворяет
условию задачи.Условие довольно длинное, но простое, поэтому мы можем применить к после¬
довательности метод расширения Where, которому следует передать лямбда-вы¬
ражение, то есть функцию-предикат, которая «отфильтрует» из последовательно¬
сти только те элементы, которые удовлетворяют заданному условию. Если мы
обозначим буквой n текущий элемент последовательности, то условие можно за¬
писать так:((n -- 7)mod7= 0) and((n -- 8)mod8= 0) and((n -- 9)mod9= 0))
То есть одновременно должны быть верными 3 логических выражения, что и тре¬
буется в задаче.Всё решение укладывается в одну строку. Но строка получается длинная, поэтому
для удобства её разбивают на более короткие логические части. Так одна длинная
физическая строка превращается в 4 физические:usesSystem;// 600 задач на сообразительность, стр.112. Задача 41begin// заголовок окна:Console.Title := '600 задач на сообразительность,стр.112. Задача 41';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Трёхзначное число');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:Range(100,999).Where(n -> ((n - 7) mod 7 = 0) and((n - 8) mod 8 = 0) and
((n - 9) mod 9 = 0)).Println;Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Ответ мы, есте¬
ственно, получим тот
же самый, что при ре¬
шении «классиче¬
ским» способом:■ 600задач на сообразительность, стр.112. Задача 41—□XТрёхзначное числоАИскомое число = 504V
Пятизначное число (Range.Where)Задача 440 из книги Математическая шкатулка [Нагибин88]:Мне было задано пятизначное число. К этому числу нужно было прибавить 200
000 и сумму умножить на 3. Вместо этого я приписал к заданному мне числу в
конце его справа цифру 2 и получил правильный результат.Какое число было мне задано?Пятизначных чисел совсем немного, поэтому мы легко выберем их из последова¬
тельности, генерируемой методом Range.По условию задачи, к заданному пятизначному числу i следовало прибавить 200
000, а получившуюся сумму - умножить на 3. Вместо этого нерадивый ученик при¬
писал в конце числа i двойку. А мы должны умножить заданное число на 10 и до¬
бавить 2.Записываем условие задачи в методе Where:і Where(i -> (i + 200000) * 3 = (i * 10 + 2))Когда эти два числа совпадут, задача будет решена:System;
//Нагибин 440begin//заголовок окна:Console.Title := 'Нагибин 440';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Пятизначное число');uses
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:Range(10000, 99999).Where(i -> (i + 200000) * 3 = (i * 10 + 2))
.Println;Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.На всякий случай мы на этом не останавливаемся и проверяем пятизначные числа
до полного их исчерпания. Впрочем, как показывает рисунок, задача имеет един¬
ственное решение, а искомое число - 85714:■ Нагибин 440—□XПятизначное числоЛ85714VШестизначный перенос (Range.Where)Задача 557 из книги Математическая шкатулка
[Нагибин88], страница 94:Первая слева цифра шестизначного числа - 1.
Если её перенести с первого места в конец
числа, сохранив порядок остальных цифр, то
вновь полученное число будет втрое больше
первоначального.Восстановите первоначальное число.”0
В этой задача нужно перенести первую цифру шестизначного числа. Дело хлопот¬
ное, поэтому мы пишем 3 функции:// первая цифра числа:var start: integer -> integer := x -> x div 100000;// 5-значное число из последних 5 цифр числа х:
var end5: integer -> integer := x -> x mod 100000;// новое 6-значное число:var newnum: integer -> integer := x -> end5(x) * 10 + start(x);Зато теперь условие в методе Where стало простым и понятным:usesSystem;//Нагибин, с.94, Задача 557//мин. и макс. 6-значные числа:
constMIN6 = 100000;MAX6 = 999999;begin// заголовок окна:Console.Title := 'Нагибин, с.94, Задача 557';Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Шестизначный перенос');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу -->// первая цифра числа:var start: integer -> integer := x -> x div 100000;// 5-значное число из последних 5 цифр числа x:
var end5: integer -> integer := x -> x mod 100000;// новое 6-значное число:var newnum: integer -> integer := x -> end5(x) * 10 + start(x);
var res:= Range(MIN6, MAX6).Where(n -> newnum(n) = 3 * n).Println;// печатаем ответ:Console.WriteLine();Console.WriteLine('Число равно ' + res.ElementAt(0));
Console.WriteLine('Число равно ' + res.ElementAt(1));Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Среди 6-значных чисел, начинающихся с единицы, первоначальное число - един¬
ственное. Есть ещё одно подобное число, но оно начинается с двойки:■ Нагибин, с,94, Задача ЬЫ—□XлШестизначный перенос1423Б7 23Б714Число равно 1420Б7Число равно 20Б714V
Всезнающая статистика (Range. Where)Задача 14 (16) из книги Удивительный мир чисел
[КА86], страницы 86-87:Попал как-то мне в руки обрывок прошлогодней
газеты. Моё внимание привлекло чернильное
пятно, закрывшее последние три цифры ше¬
стизначного числа (Рис. 2.8). По сохранившемуся
кусочку текста я вспомнил: это была заметка, в ко¬
торой сообщалось, что к концу минувшего года
население нашего города возросло до этого
числа. В заметке говорилось также о том, что это
шестизначное число уникально среди шестизнач¬
ных: оно делится на 2, 3, 4, 6, 7, 8 и 9. Ого! Не
правда ли?Чтобы восстановить все цифры этого числа, нет нужды обращаться в реставра¬
ционную лабораторию. Собственная сообразительность подскажет вам мате¬
матический метод быстрого решения этой задачи.но он
сетсл, сь
тп с учета,
вились. На
так бить, ч*
учета
’Гель но недавно всезнающая
с тика зарегистрировала ппжлрнве
0000-го кителя нааего, а к #•^нцу минувшего годан^^^И^оро-
да возросло доОбращаем вниуМ^Пггателей на
дивителыюе свойство этого числа:
тся без остатка на 2,3,4,б ,6 * 9^118 ^
Эта задача очень простая, поскольку нам предстоит проверить всего одну тысячу
чисел. Действительно, на обрывке газеты видно, что первые три цифры 6-знач¬
ного числа - 566. Последние три - от 000 до 999:const//мин. и макс. 6-значные числа:
MIN6 = 566000;MAX6 = 566999;Метод Range генерирует нужные нам числа, а метод Where проверяет их - они
должны нацело делиться на числа 2,3,4,6,7,8 и 9:usesSystem;//Кордемский, с.86-87, Задача 14
const//мин. и макс. 6-значные числа:MIN6 = 566000;MAX6 = 566999;begin//заголовок окна:Console.Title := 'Кордемский, с.86-87, Задача 14';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Всезнающая статистика');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:
var res:= Range(MIN6, MAX6)
.Where(num ->.Println;(nummod2= 0)and(nummod3= 0)and(nummod4= 0)and(nummod6= 0)and(nummod7= 0)and(nummod8= 0)and(nummod9= 0))
// печатаем ответ:Println;Console.WriteLine('Искомое число равно ' + res.First);
Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Ответ вы можете видеть на рисунке:Восстановите потерянную цифру (Range. Where)Задача 7 из книги Удивительный мир чисел [КА86], страница
85:Числа вида Мр = 2р -1, где р - простое число, называются
числами Мерсенна. При некоторых значениях р Мр - про¬
стое число. Так, первые одиннадцать простых чисел Мер¬
сенна получаются при значениях р = 2, 3, 5, 7, 13, 17, 19, 31,Двенадцатое простое число Мерсенна равно М127 = 2і27 - 1.В его десятичной записи одна цифра стёрлась. Мы пока заменили её буквой х.
Получилась такая запись:170 141 183 460 469 231 хЗ1 687 303 715 884 105 727.Восстановите цифру, замещенную буквой х, если известно, что М127 + 3 делится
на 13.61, 89, 107.■ Кордемский, C.S6-S7, Задача 14—□XВсезнающая статистика566496Искомое число равно 566496
Задача решается очень просто, если вспомнить признак делимости чисел на 13.
Цифра, обозначенная буквой х, может принимать значения от 0 до 9. Мы их полу¬
чим в методе Range:і Range(0, 9)Для каждого значения х мы находим арифметическую сумму трёхзначных чисел -
как и предписывает нам признак делимости на 13:var summa: integer -> integer := x -> 170 - 141 + 183- 460 + 469 - 231+ x * 100 + 31 - 687
+ 303 - 715 + 884- 105 + 727 + 3;К получившейся сумме нужно добавить тройку. Если при этом функция summa
возвращает число, которое делится на 13, то задача решена:і Where(n -> summa(n) mod 13 = 0)Вся программа целиком:
usesSystem;// Кордемский, с.85, Задача 7begin// заголовок окна:Console.Title := 'Кордемский, с.85, Задача 7';Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Восстановите потерянную цифру');
Console.ForegroundColor := ConsoleColor.Green;Console.WriteLine();
var summa: integer -> integer := x -> 170 - 141 + 183- 460 + 469 - 231+ x * 100 + 31 - 687
+ 303 - 715 + 884- 105 + 727 + 3;// решаем задачу:
var x:= Range(0, 9).Where(n -> summa(n) mod 13 = 0).Println;Println;// печатаем искомое число:Console.WriteLine('Искомое число равно 170 141 183 460 469231 {0}31 687 303 715 884 105 727', x.First)Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.На рисунке вы видите, что x = 7:■ Кордемский, C.S5, Задача 7X□1АВосстановите потерянную цифруИскомое число равно 170 141 183 460 469231 731 687 303 715 884 105 727V
Снимите маску с одной цифры (Range. Where)Задача 6 из книги Удивительный мир чисел
[КА86], страница 84:В записи знаменитого «шахматного» числаМб4= 1y446 744 073 709 551 615на его вторую цифру накинута маска у.Сняв маску, расшифруйте значение у, зная,
что достаточно увеличить заданное число
кратным числу 19.Примечание. По легенде именно такое число пшеничных зёрен следовало вы¬
дать в награду изобретателю шахмат, попросившему положить всего одно
зерно на первую клетку шахматной доски, а на каждую следующую клетку
вдвое большее число зёрен, чем на предыдущую.Эта задача сродни предыдущей, но признак делимости чисел на 19 не очень удо¬
бен. Поэтому в методе Where мы просто находим остаток от деления числа n + 3
на 19:і Where(n -> num(n) mod 19 = 0)Однако мы должны учесть, что искомое число очень велико, поэтому параметр
функции num должен иметь тип decimal:Ivar num: decimal-> decimal := y -> (10 + y) * 1000000000000000000 +446744073709551615 + 3;Других сложностей в задаче нет:на 3 единицы, как оно становитсяusesSystem;
// Кордемский, с.84, Задача 6begin// заголовок окна:Console.Title := 'Кордемский, с.84, Задача 6';
Console.WniteLine('');Console.FonegnoundColon := ConsoleColon.Red;
Console.WniteLine('Снимите маску с одной цифры');
Console.FonegnoundColon := ConsoleColor.Green;
Console.WriteLine();var num: decimal-> decimal := y -> (10 + y) *1000000000000000000 +
446744073709551615 + 3;// решаем задачу:
var y := Range(0, 9).Where(n -> num(n) mod 19 = 0).Pnintln;Pnintln;// печатаем искомое число:Console.WniteLine('Искомое число равно 1{0}446 744 073 709 551 615', y.Finst);Console.WniteLine();Console.FonegnoundColon := ConsoleColon.Red;end.мы быстро находим, что у = 8:■ Кордемский, с,В4, Задача 6X□1Снимите мае нус одной цифрыЛ8Искомое числоравно 18 446 744073 709 551 615V
И «хвост», и «грива» (Range. Where)Задача 8 из книги Удивительный мир чисел
[КА86], страница 72:Если есть четырёхзначные числа, первые
две и последние две цифры каждого из
которых совпадают соответственно с пер¬
выми двумя и последними двумя циф¬
рами квадрата и куба искомого числа, токакое из них наименьшее и какое -наибольшее? Числа, оканчивающиеся единицей и двумя нулями, исключаем.В зашифрованном виде:
xyzt2= xy...zt и
xyzt3= xy...zt.В правой и левой частях этих равенств буквой х зашифрована одна и та же
цифра, буквой у - также, буквой z - также и буквой t - также, но эти цифры могут
быть и одинаковыми.Чтобы получить из квадратов и кубов две первые и две последние цифры (точнее,
двузначные числа, составленные из этих цифр), мы напишем вспомогательные
функции.Две последние цифры легко получить как остаток от деления исходного числа на
сотню:і var Last2: integer -> integer := num -> num mod 100;Так как кубы четырёхзначных чисел слишком велики для типа integer, то для квад¬
ратов и кубов необходимо написать отдельные функции:“О
Ivar Last2Quadrat: integer -> integer := num -> num * num mod 100;
var Last2Cube: int64 -> int64 := num -> num * num * num mod 100;С первыми двумя цифрами сложнее: мы не знаем «длину» числа, поэтому и не
сможем сразу разделить его на нужную степень числа 10. В этом случае нужно
последовательно делить заданное число на 10, пока оно не станет двузначным:function First2(num: int64) : int64;
beginwhile (num >= 100) do
num := num div 10;Result:= num;end;Для квадратов и кубов опять необходимы функции:Ivar First2Quadrat: int64 -> int64 := num -> First2(num*num);
var First2Cube: int64 -> int64 := num -> First2(num*num*num);От метода Range мы получаем все четырёхзначные числа:
і Range(1000,9999)Метод Where пропускает только такие числа, которые удовлетворяют условиям
задачи:Where(n -> (First2(n) = First2Quadrat(n)) and
(First2(n) = First2Cube(n)) and
(Last2(n) = Last2Quadrat(n)) and
(Last2(n) = Last2Cube(n)))А метод Println печатает все профильтрованные числа:
usesSystem;// Кордемский, с.72, Задача 8function First2(num: int64) : int64;
beginwhile (num >= 100) do
num := num div 10;Result:= num;end;var First2Quadrat: int64 -> int64 := num -> First2(num*num);
var First2Cube: int64 -> int64 := num -> First2(num*num*num);var Last2: integer -> integer := num -> num mod 100;var Last2Quadrat: integer -> integer := num -> num * num mod 100var Last2Cube: int64 -> int64 := num -> num * num * num mod 100;begin// заголовок окна:Console.Title := 'Кордемский, с.72, Задача 8';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('И «хвост», и «грива»');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();Range(1000,9999).Where(n -> (First2(n) = First2Quadrat(n)) and(First2(n) = First2Cube(n)) and
(Last2(n) = Last2Quadrat(n)) and
(Last2(n) = Last2Cube(n))).Println;Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;end.Запустив программу, мы тут же получаем числа:
■ Кордемский, С.7Д. Задача SПервые два числа нарушают условия задачи, поэтому остаются 2 последних числа.
Первое из них - 1025 - наименьшее из возможных, а второе - 9976 - наибольшее.Число в левой части равенства не меньше 1, поэтому значение цифры Ж изменя¬
ется от 2 до 9. Метод Range выдаёт нам однозначные числа:і Range(2,9)Метод Where проверяет их на соответствие условию задачи:IWhere(n -> (n-1)*(n-1)*(n-1)*(n-1)*(n-1) =n * 1000 + n * 100 + n * 10 + n - 1)Метод Println печатает все подходящие значения. От него мы узнаём, что решение
единственное. Печатаем его на экране:Ж-Ж-Ж! (Range. Where)Задача 3 из книги Удивительный мир чисел [КА86],
страница 71:и/чі/ W/ТѴ/Тѵ /Тч/тл" /Т\(Ж—1)5=Ж Ж Ж (Ж—1)
usesSystem;// Кордемский, с.71, Задача 3begin// заголовок окна:Console.Title := 'Кордемский, с.71, Задача 3';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Ж-Ж-Ж!');Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:
var res:= Range(2,9).Where(n -> (n-1)*(n-1)*(n-1)*(n-1)*(n-1) =n * 1000 + n * 100 + n * 10 + n - 1).Println;// печатаем ответ:Console.WriteLine(^ = ' + res.First);
Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.На рисунке вы видите, что Ж = 7, то есть полное решение такое:
(7-1)5 = 77761 Кордемский, с,71, Задача 3—□XЖ-Ж-Ж!Ж = 7
Девять в квадрате (Range. Where)Задача 1 из книги Удивительный мир чисел [КА86],
страница 70:Найдите шестизначное число, зашифрованное в ре¬
бусе:ДЕВЯТЬ2=§§§§§§ДЕВЯТЬМы легко найдём, что наименьшее 6-значное число, состоящее из разных цифр,
равно 102345, а наибольшее - 987654. Таким образом, нужно перебрать все
числа в этом диапазоне, возвести их в квадрат и сравнить последние 6 цифр квад¬
рата с исходным числом. По условию задачи, они должны совпадать.Метод Range легко выдаст нам нужную последовательность чисел:
і Range(102345, 987654)А вот с проверкой в методе Where возникают проблемы из-за больших чисел.
Приходится вводить дополнительную функцию для проверки условия задачи:і var f: int64 -> boolean := n -> n * n mod 1000000 = n;Зато в методе Where проверка упрощается:I Where(n -> f(n) = true)Метод Println печатает все варианты решения.Задача имеет 2 решения:
■ Кордемский, с.70, Задача 1Девять в квадрате
109376 890625Напечатаем их красиво:// печатаем ответ:foreach var num: int64 in res dobeginvar s := 'num = ' + num.ToString;
s += ' num2 = ' + (num * num).ToString();
Console.WriteLine(s);
end;Теперь хорошо видно, что задача решена верно:■ Кордемский, с.70. Задача 1—□XДевять в квадратеA109376 890625num = 109376 num2 = 11963109376
num = 890625 num2 = 793212890625VВся программа целиком:usesSystem;/ /Кордемский, с.70, Задача 1begin// заголовок окна:Console.Title := 'Кордемский, с.70, Задача 1';
Console.WriteLine('');
Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Девять в квадрате');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:var f: int64 -> boolean := n -> n * n mod 1000000 = n;
var res := Range(102345, 987654).Where(n -> f(n) = true).Println();Println();// печатаем ответ:foreach var num: int64 in res dobeginvar s := 'num = ' + num.ToString;
s += ' num2 = ' + (num * num).ToString();
Console.WriteLine(s);
end;Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Число 117 649 (Range.Where)Задача 3.6 из книги Удивительный мир чисел [КА86], страница 44:Число 117 649 существует одновременно в трёх качествах. Оно:• квадратное• кубическое• кратное семи:117 6 49 = 3432 = 493 = 7k, k £ N.Более того, на отрезке от единицы до миллиона оно единственное с таким
свойством.Докажите!
Чтобы доказать это утверждение, нужно проверить все числа в заданном диапа¬
зоне (но мы расширим диапазон, чтобы найти и другие числа):I const MIN = 7;MAX = 1000000000;Чтобы не проверять делимость чисел на 7, можно генерировать только 1/7 часть
чисел из диапазона MIN..MAX:I Range(MIN, MAX, 7)Проверку числа на квадратность и кубичность мы доверим двум методам рас¬
ширения из модуля OlympUnit:usesSystem, OlympUnit;Where(num -> num.IsQuadnat and num.IsCube)Если очередное число num выдержало все проверки, то оно является решением
задачи:■ Кордемский, c.43. Задача 3-6 —□XЧисло 117 649A117649 7529536 S5766121 4S1S90304_VКрасиво печатаем ответ:// печатаем ответ:Pnintln;foreach var num: int64 in nes do
beginvar s := 'num = ' + num.ToStning();
Console.WriteLine(s);
s := 'Квадратный корень = ' +Round(Power(num, 1.0 / 2.0));Console.WriteLine(s);
s := 'Кубический корень = ' +Round(Power(num, 1.0 / 3.0));Console.WriteLine(s);Console.WriteLine();end;Рисунок ниже показывает, что первое число с указанными в задаче свойствами, -
117 649, а второе - 7 529 536 - значительно больше 1 миллиона. А всего среди
первого миллиарда чисел только 4 кратны 7 и являются полными квадратами и
кубами:і Кордемский, с.43, Задача 3-6 — □ XЧисло 117 649117649 7529536 85766121 481890304hum = 117649Квадратный корень= 343Кубический корень= 49num = 7529536Квадратный корень= 2744Кубический корень= 196num = 85766121Квадратный корень= 9261Кубический корень= 441num = 481890304Квадратный корень= 21952Кубический корень= 784Если не требовать, чтобы числа были кратны семи, то можно найти ещё несколько
любопытных чисел:Ivar res := Range(MIN, MAX, 1).Where(num -> num.IsQuadrat and num.IsCube).Println;
■ Кордемский, с,43, Задача 3-6num = 11390625Квадратный корень = 3375Кубический корень = 225num = 16777216Квадратный корень = 4096Кубический корень = 256num = 24137569Квадратный корень = 4913Кубический корень = 289num = 34012224Квадратный корень = 5832Кубический корень = 324num = 47045881Квадратный корень = 6859Кубический корень = 361Пит = 64000000Квадратный корень = 8000Кубический корень = 400num = 85766121Квадратный корень = 9261Кубический корень = 441Или в более наглядной форме:• 33752 = 2253 = 11390625• 49132 = 2893 = 24137560• И так далее.Программа полностью:usesSystem, OlympUnit;// Кордемский, с.43, Задача 3-6
const MIN = 7;S
MAX = 100000000;begin// заголовок окна:Console.Title := 'Кордемский, с.43, Задача 3-6';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Число 117 649');Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:{var f: int64 -> boolean:= num -> num.IsQuadrat and num.IsCube
var res:= Range(MIN, MAX).Where(num -> f(num) = true).Println;}{var res:= Range(MIN div 7, MAX div 7).Where(num -> (num*7).IsQuadrat and (num*7).IsCube)
.Select(num -> num*7).Println;}var res:= Range(MIN, MAX, 1).Where(num -> num.IsQuadrat and num.IsCube).Println;// печатаем ответ:Println;foreach var num: int64 in res do
beginvar s := 'num = ' + num.ToString();Console.WriteLine(s);s := 'Квадратный корень = ' +Round(Power(num, 1.0 / 2.0));Console.WriteLine(s);s := 'Кубический корень = ' +Round(Power(num, 1.0 / 3.0));Console.WriteLine(s);Console.WriteLine();end;Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.
Пара чисел: 3149 и 3151 (Range.Where)Задача 3.2 из книги Удивительный мир чисел [КА86],
страница 43:1 УА(-3, 2)Десятичная запись куба каждого из чисел 3149 и
3151 начинается двумя его первыми цифрами, а
оканчивается двумя его последними цифрами:-0-3В(г;-э)31493 = 31 226 116 949
31513 = 31 285 651 951Есть ещё два последовательных четырёхзначных числа, обладающих таким же
свойством.Какие это числа?Задача решается полным перебором всех четырёхзначных чисел:
і Range(1000, 9999)Для каждого четырёхзначного числа мы находим его куб.
і Числа большие, поэтому они должны иметь тип int64\Затем сравниваем в этих числах 2 первые и 2 последние цифры. Для этого мы при¬
влекаем функции First2, First2Cube, Last2 и Last2Cube, разработанные нами в про¬
екте И «хвост», и «грива»:function First2(num: int64): int64;
beginwhile (num >= 100) do
num := num div 10;Result := num;"0
end;var First2Cube: int64-> int64 := num -> First2(num * num * num);var Last2: integer-> integer := num -> num mod 100;var Last2Cube: int64-> int64 := num -> num * num * num mod 100;Благодаря этим функциям метод Where получился очень простым:Where(n -> ((First2(n) = First2Cube(n)) and
(Last2(n) = Last2Cube(n))))Метод Println показывает, что задача имеет несколько решений. Печатаем их кра¬
сиво:usesSystem;// Кордемский, с.43, Задача 3-2function First2(num: int64): int64;
beginwhile (num >= 100) do
num := num div 10;Result := num;end;var First2Cube: int64-> int64 := num -> First2(num * num * num);var Last2: integer-> integer := num -> num mod 100;var Last2Cube: int64-> int64 := num -> num * num * num mod 100;begin// заголовок окна:Console.Title := 'Кордемский, с.43, Задача 3-2';
Console.WriteLine();
Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Пара чисел: 3149 и 3151');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:var nums := Range(1000, 9999).Where(n -> ((First2(n) = First2Cube(n)) and
(Last2(n) = Last2Cube(n)))).Println;// печатаем ответ:Println;foreach var num: int64 in nums do
beginvar s := 'num = ' + num.ToString;s += ' num3 = ' + (num * num * num).ToString();Println(s);end;Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;end.На рисунке приведён полный список чисел, выполняющих условие задачи:■ Кордемский, с.43, Задача U-2 — □ XПарачисел: 3149и 315110001001 :1024 1025 3149 3151 3200 3201 9975 9976 9999num =1000num3=1000000000num =1001num3=1003003001num =1024num3=1073741S24num =1025num3=1076S90625num =3149num3=31226116949num =3151num3=312S5651951num =3200num3=32760000000num =3201num3=32790729601num =9975num3=99251S734375num =9976num3=992S17266176num =9999num3=999700029999В книге указана пара чисел 3200 и 3201, но вы видите, что есть и другие пары чисел:
1000 и 1001
1024 и 1025
9975 и 9976Трёхзначное число (Range.Where)Задача 3 из книги Удивительный
мир чисел [КА86], страница 63:Найдите трёхзначное число, обла¬
дающее следующими свой¬
ствами:• число десятков на 4 меньше числа единиц, но на 4 больше числа сотен;• если цифры этого числа разместить в обратном порядке, то новое получен¬
ное число будет на 792 больше искомого.Чтобы найти трёхзначное число с требуемыми свойствами, необходимо пере¬
брать все трёхзначные числа и выбрать те из них, которые удовлетворяют усло¬
виям задачи.Метод Range генерирует все трёхзначные числа:
і Range(100, 999)Нам нужно найти, сколько единиц, десятков и сотен содержит текущее число. Это
нетрудно сделать с помощью двух операций - целочисленного деления и деле¬
ния с остатком:// число единиц:var e: integen-> integen := n -> n mod 10;// число десятков:var d: integen-> integen := n -> n div 10 mod 10;
// число сотен:
I var s: integer-> integer := n -> n div 100 mod 10;Выполнить проверки и того проще:Where(n -> d(n) = e(n) - 4)
.Where(n -> d(n) = s(n) + 4)И вот мы получили ответ на эту задачу:1 Кордемский, с,63, Задача 3—□XЛТрёхзначное число159Число = 159VПоскольку мы перебрали все трёхзначные числа и нашли только одно, которое
удовлетворяет условиям задачи, то последняя проверка на разность с перевёрну¬
тым числом оказалась бы лишней!Вся программа целиком:
usesSystem;// Кордемский, с.63, Задача 3begin// заголовок окна:Console.Title := 'Кордемский, с.63, Задача 3';Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Трёхзначное число');Console.ForegroundColor := ConsoleColor.Green;Console.WriteLine();// решаем задачу -->
// число единиц:var e: integer-> integer := n -> n mod 10;// число десятков:var d: integer-> integer := n -> n div 10 mod 10;
// число сотен:var s: integer-> integer := n -> n div 100 mod 10;var res:= Range(100, 999).Where(n -> d(n) = e(n) - 4).Where(n -> d(n) = s(n) + 4).Println;// печатаем ответ:Println;var str := 'Число = ' + res.First;
Console.WriteLine(str);Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Таких чисел только два (Range. Where)Задача 1 из книги Удивительный мир чисел
[КА86], страница 63:Есть только два двузначных числа, каждое
из которых равно неполному квадрату раз¬
ности своих цифр.Найдите эти числа.Чтобы облегчить решение, подскажем, что одно число на 11 больше другого.Так как двузначных чисел очень мало, то мы и без подсказки найдём оба искомых
числа!Получаем от метода Range все двузначные числа:
I Range(10, 99)Выделяем из каждого числа единицы и десятки:// число единиц:var e: integer-> integer := n -> n mod 10;// число десятков:var d: integer-> integer := n -> n div 10 mod 10;И сравниваем само число с неполным квадратом разности его цифр
і Where(n -> e(n) * e(n) + d(n) * d(n) - e(n) * d(n) = n)
Задача решена:■ Кордемский, с.63. Задача 1—□XТа тих чисел только дваА37 48Число = 37Число = 48VПолный текст программы:usesSystem;// Кордемский, с.63, Задача 1begin// заголовок окна:Console.Title := 'Кордемский, с.63, Задача 1';
Console.WriteLine('');
Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Таких чисел только два');
Console.ForegroundColor := ConsoleColor.Green;Console.WriteLine();// решаем задачу -->// число единиц:var e: integer-> integer := n -> n mod 10;// число десятков:var d: integer-> integer := n -> n div 10 mod 10;var res:= Range(10, 99).Where(n -> e(n) * e(n) + d(n) * d(n) - e(n) * d(n) = n)
.Println;// печатаем ответ:Println;foreach var n in res do
beginvar str := 'Число = ' + n;Console.WriteLine(str);Console.WriteLine();end;Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Ещё два числа (Range.Where)Задача 2 из книги Удивительный мир чисел [КА86],
страница 63:Сходным свойством обладают ещё два двузначных
числа: каждое равно неполному квадрату суммы
своих цифр.Найдите эти числа, зная, что одно число на 50
больше другого.^144 ^
Эта задача решается точно так же, как предыдущая. Только в методе Where нужно
исправить один знак:usesSystem;// Кордемский, с.63, Задача 2begin// заголовок окна:Console.Title := 'Кордемский, с.63, Задача 2';Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Ещё два числа');Console.ForegroundColor := ConsoleColor.Green;Console.WriteLine();// решаем задачу -->// число единиц:var e: integer-> integer := n -> n mod 10;// число десятков:var d: integer-> integer := n -> n div 10 mod 10;var res:= Range(10, 99).Where(n -> e(n) * e(n) + d(n) * d(n) + e(n) * d(n) = n)
.Println;// печатаем ответ:Println;foreach var n in res do
beginvar str := 'Число = ' + n;Console.WriteLine(str);end;Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Правда, чисел оказалось не два, а три:
■ Кордемский, с.63, Задача 2Ещё два числа13 63 91Число = 13
Число = 63
Число = 91Методы Count и LongCountМетод Count без параметров возвращает число элементов в последовательности.
і function Count(): integer;Если последовательность пустая, то метод вернёт 0:// Countvar sq := Range(1, 10).Pnintln;
Pnintln(sq.Count);sq := Range(10, 1).Pnintln;
Pnintln(sq.Count);Pnintln;llJ Простые методы расширения для последовательностей — □XЛПростые методы расширения для последовательностей123456709 10VВо второй последовательности нет ни одного элемента!
Метод Count с параметром возвращает число элементов в последовательности,
которые удовлетворяют заданному условию predicate:function Count(predicate: T-> boolean): integer;I Println(sq.Count(n -> n < 6));llJ Простые методы расширения для последовательностей — □XлПростые методы расширения для последовательностей1234567S9 10VМетоды LongCount действуют точно так же, но возвращают значение типа int64:
function LongCount(): int64;function LongCount(predicate: T->boolean): int64;Любопытное свойство чисел (Range.Count)Задача 15 (16 - номер задачи в издании
1996 года) из книги Удивительный мир
чисел [КА86], страница 102:Возьмите какое-либо б-значное
число, делящееся на 7, например
325 836. Перенесите последнюю
цифру в начало записи числа. Обра¬
зуется новое число 632 583. Оно
также делится на 7.,40
Докажите самостоятельно, что таким свойством обладает любое 6-значное
число, делящееся на 7.Рекомендация. Представьте заданное число так: 7k = = 10а+ 6 (1). Тогда новое
число примет вид 100 0006 +а (2). Используя (1), докажите делимость (2) на
число 7.Доказать - значит, убедиться, что все 6-значные числа обладают указанным свой¬
ством. Поскольку 6-значных чисел совсем немного, то метод полного перебора
здесь вполне уместен.Поскольку диапазон значений проверяемых чисел точно очерчен, то мы генери¬
руем их в методе Range:і Range(999999, 100000)Все числа, кратные 7, мы пропускаем через первый метод Where:
і Where(n -> n mod 7 = 0)Этим числам назначаем проверки.Чтобы перенести последнюю цифру числа в начало, нужно её выделить. Это легко
сделать, применив к числу операцию деления по модулю:I// последняя цифра числа:var endn: integer-> integer := n -> n mod 10;Число из первых 5 цифр найти ещё проще - нужно просто разделить исходное
число на 10:I// 5-значное число из первых 5 цифр числа num:
var start5: integer-> integer := n -> n div 10;
При формировании нового 6-значного числа мы переносим последнюю цифру в
начало, что соответствует умножению на 100000. К этому произведению следует
добавить 5-значное число из первых пяти цифр исходного числа:I// новое 6-значное число:var newnum: integer-> integer := n -> endn(n) * 100000 + start5(n);Эту операцию можно записать более наглядно:123456 ^исходное 6-значное число
612345 ^новое 6-значное числоПроверяем условие задачи во втором методе Where:
і Where(n -> newnum(n) mod 7 <> 0)И последний метод - Count - возвращает число элементов в отфильтрованной
последовательности.Если оно равно 0, значит, не нашлось ни одного числа, опровергающего утвер¬
ждение в условии задачи.Печатаем на экране результаты проверки:// все числа проверены:
if (res = 0) thenConsole.WriteLine('Утверждение верное')
elseConsole.WriteLine('Утверждение неверное');Вся программа целиком:IusesSystem;
// Кордемский, с.102, Задача 15begin// заголовок окна:Console.Title := 'Кордемский, с.102, Задача 15';
Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Любопытное свойство чисел');
Console.ForegroundColor := ConsoleColor.Green;Console.WriteLine();// последняя цифра числа:var endn: integer-> integer := n -> n mod 10;// 5-значное число из первых 5 цифр числа num:
var start5: integer-> integer := n -> n div 10;// новое 6-значное число:var newnum: integer-> integer := n -> endn(n) * 100000 + start5(n);var res := Range(999999, 100000).Where(n -> n mod 7 = 0).Where(n -> newnum(n) mod 7 <> 0).Count;// все числа проверены:
if (res = 0) thenConsole.WriteLine('Утверждение верное')
elseConsole.WriteLine('Утверждение неверное');Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Так как мы проверили все
6-значные числа, то мо¬
жем уверенно утверждать,
что любое 6-значное число
удовлетворяет условиям задачи.■ Кордемский, с.102. Задача 15—□XЛюбопытное свойство чиселлУтверждение верноеV
Как определил ошибку Чохбилмиш? (Range.Count)Задача 23 (25) из книги Удивительный мир чисел
[КА86], страница 57:Чохбилмиш предложил каждому из двух учени¬
ков задумать какое-либо шестизначное число и
переставить первую цифру в конец записи числа.
Одному сказал: «Найди сумму получившихся чи¬
сел». Другому сказал: «Найди разность».Ученики выполнили действия и написали:913 485 и 167 860.Чохбилмиш не знал, какие числа были задуманы учениками, но сразу опреде¬
лил: «Вы оба ошиблись».Как рассуждал Чохбилмиш?В ответе на задачу утверждается, что сумма любого 6-значного числа с другим 6¬
значным числом, полученным из исходного переносом первой цифры в конец
числа, кратна 11 и аналогично полученная разность - кратна 9.Мы можем проверить эти утверждения с помощью полного перебора, тем более
что самая трудная часть задачи - перенос цифры - нами уже решена в предыду¬
щем проекте. Добавим ещё 2 функции для удобного вычисления суммы и разно¬
сти двух чисел:// сумма:var sum: integer-> integer := n -> n + newnum(n);// разность:var razn: integer-> integer := n -> n - newnum(n);Проверка в методе Where :
Where(n -> (sum(n) mod 11 <> 0) or (razn(n) mod 9 <> 0))Вот и всё решение задачи:usesSystem;// Кордемский, с.57, Задача 23begin// заголовок окна:Console.Title := 'Кордемский, с.57, Задача 23';Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Как определил ошибку Чохбилмиш?');
Console.ForegroundColor := ConsoleColor.Green;Console.WriteLine();// последняя цифра числа:var endn: integer-> integer := n -> n mod 10;// 5-значное число из первых 5 цифр числа num:
var start5: integer-> integer := n -> n div 10;// новое 6-значное число:var newnum: integer-> integer := n -> endn(n) * 100000 + start5(n);
// сумма:var sum: integer-> integer := n -> n + newnum(n);// разность:var razn: integer-> integer := n -> n - newnum(n);var res := Range(100000, 999999).Where(n -> (sum(n) mod 11 <> 0) or (razn(n) mod 9 <> 0))
.Count;// все числа проверены:
if (res = 0) thenConsole.WriteLine('Утверждения верные')
elseConsole.WriteLine('Утверждения неверные');
I Console.WriteLine();Console.FonegnoundColon := ConsoleColon.Red;
end.Проверив все 6-значные числа, мы можем с уверенностью констатировать факт:
оба утверждения верные:■ Кордемский, с.57, Задача 23—□XКак: определил ошибку Чохбилмиш?лУтверждения верныеVМетод SumМетод Sum без параметров возвращает сумму элементов последовательности:function Sum(): decimal;var sq := Range(1, 10).Println;
Println(sq.Sum());■ Простые методы расширения для последовательностей — □XАПростые методы расширения для последовательностей12345678910VМетод Sum с параметром возвращает сумму элементов последовательности, к
каждому из которых применена функция selector:function Sum(selector: T -> decimal)): decimal;
Например, мы хотим найти сумму квадратов элементов. Тогда функцию для вы¬
числения квадратовn -> n*nнужно передать методу Sum:Ivar sq := Range(1, 10).Println;Println(sq.Sum(n -> n*n));■ Простые методы расширения для последовательностей — □XАПростые методы расширения для последовательностей12345678910385VС помощью метода Sum мы легко решим историческую задачу.Гаусс (Range.Sum)Задача 5 из книги Математическая шкатулка [Наги-
бин88], страница 15:Рассказывают, что в начальной школе, где учился маль¬
чик Карл Гаусс, ставший потом знаменитым математи¬
ком, учитель, чтобы занять класс на продолжительное
время самостоятельной работой, дал детям такое зада¬
ние - вычислить сумму всех натуральных чисел от 1 до
100. Но маленький Гаусс это задание моментально вы¬
полнил.Попробуй и ты быстро выполнить это задание.'“)
Нетрудно в этом ряде чисел увидеть арифметическую прогрессию, начинающуюся
с единицы и насчитывающую 100 членов. Разность арифметической прогрессии
равна 1. Зная все параметры этого ряда, мы быстро найдём его сумму:і Сумма = (1 + 100)*100/2 = 5050Учитель, конечно, предполагал, что школяры будут вычислять сумму ряда после¬
довательным прибавлением очередного члена ряда к текущей сумме.Давайте решим эту простую задачу на последовательности.Метод Range генерирует заданную арифметическую прогрессию, а метод Sum
находит сумму всех элементов последовательности:usesSystem;// Нагибин, с.15, Задача 5begin// заголовок окна:Console.Title := 'Нагибин, с.15, Задача 5';Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Гаусс');Console.ForegroundColor := ConsoleColor.Green;Console.WriteLine();// решаем задачу:var summa := Range(1,100).Sum;Console.WriteLine('Сумма ряда равна {0}', summa);Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Всё решение уложилось в одну короткую строчку:
■ Нагибин, с. 15, Задача 5—□XЛГ ауссСумма ряда равна 5050VПодставляя нужные значения в метод Range, вы быстро найдёте сумму любой
арифметической прогрессии. Например, давайте решим такую задачу.Задача 7.1 из книги Математическая шкатулка [Нагибин88], страница 15:
і Как быстро вычислить сумму 1 + 3+ 5+7 + 9 + ... + 997 +999?Добавьте в главный блок новую строку:I// решаем задачу://var summa := Range(1,100).Sum;
var summa := Range(1, 999, 2).Sum;И получите ответ на задачу:■ Нагибин, с. 15, Задача 5—□XГ ауссАСумма ряда равна 250000VЗадача 838 из книги Математическая шкатулка [Нагибин88], страница 128:і Вычислите: 5 + 10 + 15+20 + 25 + ... + 100.И опять одна строка программы решает задачу:I // решаем задачу:
//var summa := Range(1,100).Sum;
//var summa := Range(1, 999, 2).Sum;
var summa := Range(5, 100, 5).Sum;■ Нагибин, с. 15, Задача 5—□XГ ауссАСумма ряда равна 1050V«Избранные» числа (Range.Sum)Задача 12 из книги Удивительный мир чисел [КА86], страница 65:Есть 80 четырёхзначных и 800 пятизначных чисел, не оканчивающихся нулем,
и таких, что если от любого из них вычтем 999 в случае четырёхзначного числа
и 9999 в случае пятизначного числа, то всякий раз получим обращённое число,
т. е. записанное теми же цифрами, но в обратном порядке. Какие это числа?Найдите способ быстрого вычисления суммы этих чисел (без привлечения ком¬
пьютера).Мы проигнорируем замечание в скобках и решим сначала задачу для 4-значных
чисел. Так как условие задачи запрещает числам оканчиваться нулём, то мини¬
мальное 4-значное число равно 1001, а максимальное - 9999. Обозначим их кон¬
стантами:I const MIN = 1001;MAX = 9999;MINUS = 999;Генерируем все 4-значные числа в методе Range:
// решаем задачу:
var res: = Range(MIN, MAX)Отфильтровываем числа, заканчивающиеся нулём:і Where(num -> num mod 10 <> 0)Вычитаем из каждого числа 999 и сравниваем его с обращённым исходным чис¬
лом:і Where(num -> num - MINUS = num.ReverseNum)Здесь мы используем ме¬
тод расширения
ReverseNum из модуля
OlympUnit.Если они совпадают, то
найдено ещё одно реше¬
ние задачи. Печатаем его:і .Println;Как и указано в книге Уди¬
вительный мир чисел,
всего существует 80 таких
чисел.Для поиска 5-значных чисел
необходимо изменить зна¬
чения констант:■ Кордемскин, с:.б5. Задача 12 — □ Xк Избранные;»числа20012111 2221 2331 2441 2551 2661 2771 2881 2991300231123222 3332 3442 3552 3662 3772 3882 3992 4003 41134223 4333 4443 4553 4663 4773 4883 4993 Б004 Б1145224 5334 5444 5554 5664 5774 5884 5994 6005 6115 622Б 633Б 644Б 6БББ 666Б 677Б 688569957006 7116 7226 7336 7446 7556 7666 7776 7886799680078117 8227 8337 8447 8557 8667 8777 8887 8997 90089118 9228 9338 9448 9558 9668 9778 9888 9998Всегочисел;80Суммачисел= 479960num =20011002num =21111112num =22211222num =23311332num =24411442num =25511552num =26611662num =27711772num =28811882num =29911992num =30022003num =31122113num =32222223num =33322333|num =34422443
const MIN = 10001;
MAX = 99999;
MINUS = 9999;5-значных чисел ровно в 10 раз больше, чем 4-значных:llJ Кордемский, с,65, Задача 12 — □ X03mG'i96868 96168 96268 96368 96468 96568 96668 96768 96868 96968 9787S 97178 97278 97378 97478 97578 97678 97778 97878 97978 98888 98188 98288 9838898488 98588 98688 98788 98888 98988 99898 99198 9929899398 99498 !99598 99698 99798 99898 99998Всегочисел:888С умиачисел=47999688hum=28881>18882num=28181>18182num=28281>18282num=28381>18382num=28481>18482num=28581>18582num=28681>18682num=20701>10702num=20801>10802num=20901>10902num=21011>11012num=21111>11112num=21211>11212num=21311>11312num=21411>11412num=21511>11512num=21611>11612num=21711>11712num=21811>11812num=21911>11912num=22021>12022num=22121>12122num=22221>12222Поскольку мы решаем задачу на компьютере, то было бы странно, если бы мы
принялись сумму найденных чисел вычислять вручную! Метод Sum быстро нахо¬
дит сумму:Console.WriteLine('Сумма чисел = ' + res.Sum);
Вся программа целиком:usesSystem, OlympUnit;// Кордемский, с.65, Задача 12
{const MIN = 1001;MAX = 9999;MINUS = 999;}const MIN = 10001;MAX = 99999;MINUS = 9999;begin// заголовок окна:Console.Title := 'Кордемский, с.65, Задача 12';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('«Избранные» числа ');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:var res:= Range(MIN, MAX).Where(num -> num mod 10 <> 0).Where(num -> num - MINUS = num.ReverseNum)
.Println;Console.WriteLine('Всего чисел: ' + res.Count);
Console.WriteLine('Сумма чисел = ' + res.Sum);// печатаем ответ:Console.WriteLine();
foreach var num in res do
beginvar s := 'num = ' + num.ToString();
s += ' > ' + num.ReverseNum.ToString();
Console.WriteLine(s);end;
Console.WriteLine();Console.FonegnoundColon := ConsoleColon.Red;
end.Метод AverageМетод Average без параметров возвращает среднее арифметическое элементов
числовой последовательности:function Average(): decimal;Найдём среднее арифметическое последовательности первых десяти натураль¬
ных чисел:Ivar sq := Range(1, 10).Println;Println(sq.Average());ЁЗ Простые методы расширения для последовательностей —□XЛПростые методы расширения для последовательностей123456789 10
5.5VМетод Average с параметром возвращает среднее арифметическое элементов
числовой последовательности, к каждому из которых применена функция
selector:function Average(selector: T -> decimal)): decimal;Найдём среднее арифметическое квадратных корней из чисел, составляющих по¬
следовательность sq:
var sq := Range(1, 10).Println;
Println(sq.Average(n -> Sqrt(n)));■ Простые методы расширения для последовательностей — □XАПростые методы расширения для последовательностей123456789 102.246828VМетоды Min и MaxМетод Min возвращает минимальный элемент последовательности чисел:
function Min(): число;Второй метод Min возвращает минимальное значение функции преобразования
selector, применённой к каждому элементу последовательности чисел:function Min(selector: T-^исло): число;Методы Max возвращают максимальный элемент последовательности чисел:function Max(): число;function Max(selector: T-^исло): число;Найдём наибольший и наименьший элемент в последовательности случайных чи¬
сел:Ivar sq:= SeqRandom(20,1, 20).ToArray.OrderBy(n -> n).Println;Println('Минимальное значение = ' + sq.Min(sq));Println('Максимальное значение = ' + sq.Max(sq));
■ Методы Min и МакX□1АМетоды Міп и Мах1 2 3 3 4 4 4 5 5 7 8 9 10 1013 13 14 16 16 17Минимальное значение = 1Максимальное значение = 17VЗначение функции преобразования selector определяет минимальный или макси
мальный «элемент» последовательности. Например, мы можем найти длину са
мого короткого и самого длинного числа в последовательности sq:var sq:= SeqRandom(100,1, 10000000).ToArray.OrderBy(n -> n.ToString.Length).Println;Println;Println('Минимальная длина = ' + sq.Min(n -> n.ToString.Length));
Println('Максимальное длина = ' + sq.Max(n -> n.ToString.Length));■ Методы Min и Max — □ XМетоды Min и Max46571 356752 163921 379037 177115 817219 278951 633948 7324750 5466351
2277779 5160378 1919543 6674406 9381324 9734514 6302687 7603653 8381382
9786834 7651555 4961371 5832021 9562596 6730032 6597569 8752347 1809360 4015981 5471261 5942545 3959568 2573207 5233068 3879835 6018722 36090
69 9346462 8759093 8204267 8453417 3620373 9488269 9851781 8745377 8914
816 2800168 2034399 9011139 5529041 6996088 5844547 6686572 6298872 512
3092 9933111 8623003 5117607 8480143 5515391 1450371 6806518 9217834 60
87003 9346336 5123684 2794571 3362603 6125446 6956226 8844561 1333417 4
031182 5473102 5980240 7815216 3797402 6717948 2798221 3852061 9942221
8946458 8115022 5886635 8934196 8756744 6085611 4986066 4228855 7825231
2938027 2170003 2681752 2658374 9568480 7676484 9239829 5648537 403847
5 8572816Минимальная длина = 5
Максимальное длина = 7
Методы First и FirstOrDefaultМетод First без параметров возвращает первый элемент последовательности:
і function First(): T;var sq := Range(1,10).Println;
Println(sq.First);■ Методы First и FirstOrDefault—□XМетоды First и FirstOrDefaultА1234567S9 10VМетод First с параметром возвращает первый элемент последовательности, удо¬
влетворяющий условию predicate:і function First(predicate: T->boolean): T;I var sq := Range(1,10).Println;Println(sq.First(n -> n > 5));■ Методы First и FirstOrDefault—□XМетоды First и FirstOrDefaultЛ123456739 10VВ этом примере метод First возвращает первый элемент последовательности, ко¬
торый больше 5, то есть 6.
Методы FirstOrDefault действуют точно так же, но, если последовательность пу¬
стая (не содержит элементов), то они возвращают значение по умолчанию. Для
числовых типов это 0:function FirstOrDefault(): T;function FirstOrDefault(predicate: T->boolean): T;Создаём пустую последовательность, берём методом First её первый элемент и
печатаем на экране:Ivar sq := Range(10,1).Println;Println(sq.First);Так как у пустой последовательности нет первого элемента, то компилятор выдаёт
сообщение об ошибке:19 jvar sq := Kange^iy,1J.Hrintln;201 Println(sq.First)'л л !сСписок ошибокСтрока Описание1 20 Ошибка времени выполнения: Последовательность не содержат элементовНа этом выполнение программы завершается.Естественно, такое поведение программы нельзя считать нормальным. Если у вас
есть опасения, что последовательность может оказаться пустой, всегда приме¬
няйте методы FirstOrDefault. В этом случае ошибка в программе не возникнет, а
метод FirstOrDefault вернёт нуль:Ivar sq := Range(10,1).Println;Println(sq.FirstOrDefault);
ill Методы First и FirstOrDefault — □ XНо если последовательность не пустая, и первый элемент равен нулю, то метод
FirstOrDefault также вернёт нуль. Это может ввести программу в заблуждение. Ис¬
пользуйте при необходимости метод Count, чтобы распознать пустую последова¬
тельность.Многие задачи имеют единственное решение. Чтобы извлечь его из последова¬
тельности, применяют метод First или ElementAt(0), что то же самое.Назойливый остаток (Range.First)В книге Бориса Кордемского и Аскера Ахадова Удиви
тельный мир чисел [КА86] вы найдёте немало инте
ресных задач, в том числе и на делимость. На стра
нице 86 авторы предлагают решить задачу Назойли
вый остаток:Некоторые числа, кратные числу 7, при делении на2, на 3, на 4, на 5 и на 6 дают остаток 1.Найдите наименьшее из таких чисел.Эта же задача напечатана в книге Фёдора Нагибина и Евгения Канина Матема¬
тическая шкатулка [Нагибин88], задача 42, страницы 18-19, но в более занима¬
тельной форме:I Колхозница привезла на рынок для продажи корзину яиц. Продавала она их по
одной и той же цене. После продажи яиц колхозница пожелала проверить,Методы First и FirstOrDefault
верно ли она получала деньги. Но вот беда: она забыла, сколько у неё было
яиц. Вспомнила она только, что когда перекладывала яйца по 2, то оставалось
одно яйцо; одно яйцо оставалось также при перекладывании яиц по 3, по 4, по
5, по 6. Когда же она перекладывала яйца по 7, то не оставалось ни одного.Помоги колхознице сообразить, сколько у неё было яиц.Поскольку речь идёт о натуральных числах, то мы можем начать наши поиски с
единицы. Метод Step генерирует бесконечную последовательность натуральных
чисел:і 1.Step()Метод Where пропускает только те из них, которые удовлетворяют условиям за¬
дачи:Where(n -> (n mod 7 = 0) and
(n mod 2 = 1) and
(n mod 3 = 1) and
(n mod 4 = 1) and
(n mod 5 = 1) and
(n mod 6 = 1))И наконец, метод First останавливает решение задачи, когда будет найдено пер¬
вое (наименьшее) число, удовлетворяющее условиям задачи.Весь код программы:usesSystem;// Кордемский, с.86, Задача 8begin// заголовок окна:Console.Title := 'Кордемский, с.86, Задача 8';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Назойливый остаток');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:
var res:= 1.Step().Where(n ->.First;(nmod7= 0)and(nmod2= 1)and(nmod3= 1)and(nmod4= 1)and(nmod5= 1)and(nmod6= 1))// печатаем ответ:Console.WriteLine('Искомое число равно ' + res);
Println(NewLine);Console.ForegroundColor := ConsoleColor.Red;
end.На рисунке вы видите ответ на эту задачу:■ Кордемский, с,36, Задача fl—□XНазойливый остатокЛИскомое число равно 301VА есть ли ещё и другие числа, которые имеют назойливый остаток?Тут, конечно, следует учесть, что бесконечный цикл уже не годится, потому что он
действительно станет бесконечным, если искомых чисел очень много (а это не¬
трудно предвидеть). Поэтому мы объявляем константу с верхней границей по¬
иска://макс. число:
const MAX = 10000;
Оператор для поиска чисел изменился несущественно:.Where(n -> (n mod7 = 0)and(n mod2 = 1)and(n mod3 = 1)and(n mod4 = 1)and(n mod5 = 1)and(n mod6 = 1)).Println(NewLine);Запускаем приложение и видим, что среди первой десятки тысяч натуральных чи¬
сел довольно много подходящих под условие задачи:1 Кордемский, с.Аб, Задача S□XНазойливый остаток3017211141156119S124012S213241366140S145014921534157616181660170217441786182818701912195419961
Также нетрудно подметить такую закономерность. Если обозначить через n номер
искомого числа, то все числа с назойливыми остатками можно легко найти по
формуле:і num = 301 + 420(n-1)Во Франции бытует подобная задач (Математический фольклор, ЗадачаД17):Женщина несла на базар две корзины яиц. Прохожий случайно толкнул её, кор¬
зинки упали и яйца разбились. Виновник извинился, предложил возместить
ущерб и спросил:- Сколько яиц было в корзинах?- Не помню точно, - ответила женщина, - но знаю, когда вынимала их по 2, по 3,
по 4, по 5 или по 6 яиц, то в корзине оставалось по 1 яйцу, а когда вынимала по
7, то не оставалось ни одного.Сколько яиц было в корзинах?Популярна яичная тема и в Болгарии (Математический фольклор, Задача 87):Женщина несла на базар две корзины яиц. Её нечаянно толкнул парень, кор¬
зины упали, а яйца разбились. Парень, чтобы заплатить, спросил, сколько было
всего яиц.- Я их не считала, но когда складывала в корзины по 2, по 3, по 4, по 5, по 6, тоЛ w “7всякий раз оставалось по 1 яйцу, а когда складывала по 7 - не оставалось ни
одного.Сколько яиц было в корзинах?
И такие есть числа (Range.First)Задача 4-1 из книги Удивительный мир чи
сел [КА86], страница 63:Какое двузначное число в 19 раз больше
числа его единиц?Чтобы решить задачу, достаточно пере¬
брать все двузначные числа и проверить
условие задачи:usesSystem;// Кордемский, с.63, Задача 4-1begin//заголовок окна:Console.Title := 'Кордемский, с.63, Задача 4-1';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('И такие есть числа');Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();var res:= Range(10,99).Where(n -> n = n mod 10*19).Println.First;var s := 'Число = ' + res.ToString;Console.WriteLine(s);Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Как вы видите на рисунке, задача имеет единственное решение: искомое число
равно 95:
1 Кордемский,с,63, Задача 4-1—□XИ такие естьчислаЛ95Число = 95VШестизначное число (Range.First)Задача 4 из книги Удивительный мир чисел [КА86],
страница 44:Ученику понадобилось написать наибольшее из
шестизначных чисел, кратных 11 и чтобы цифра 6
была первой слева. Как надо действовать уче¬
нику для быстрого выполнения задания, если
признаков делимости на 11 он ещё не знает?Сообщим, что искомое число обладает забавной особенностью: если каждую
его цифру повернуть на 180° в плоскости бумаги, оставляя её на прежнем ме¬
сте, то образовавшееся число окажется дважды кратным 11 (делится на 11 и
частное также делится на 11).Выявите ещё одну особенность чисел: найденного и с повёрнутыми цифрами.Легко догадаться, что нужно искать решение задачи среди шестизначных чисел,
начинающихся с шестёрки. Причём перебирать числа следует задом наперёд, то
есть от наибольшего к наименьшему, чтобы сразу же найти наибольшее из всех
возможных чисел в заданном диапазоне. В методе Where мы отбираем числа,
удовлетворяющие условиям задачи, а метод First присваивает первое прошедшее
отбор число переменной res:Where(n -> n mod 11 = 0)
Вся программа полностью:usesSystem;// Кордемский, с.44, Задача 4begin// заголовок окна:Console.Title := 'Кордемский, с.44, Задача 4';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Шестизначное число');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:var res:= Range(699999, 600000, -1).Where(n -> n mod 11 = 0). First;var s := 'Наибольшее число равно ' + res.ToString;
Console.WriteLine(s);Console.WriteLine();if (966669 mod 121 = 0) thenConsole.WriteLine('966669 : 121 = ' + 966669 div 121)
elseConsole.WriteLine('Число 966669 на 121 не делится!');
Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.А вот и ответ:■ Кордемский, с.44. Задача 4□ XШестизначное число
Наибольшее число равно 699996
По условию задачи, переворачиваем все цифры найденного числа:і 699996 ^ 966669Проверяем, делится ли перевёрнутое число на 11 * 11 = 121:if (966669 mod 121 = 0) thenConsole.WriteLine('966669 : 121 = ' + 966669 div 121)elseConsole.WriteLine('Число 966669 на 121 не делится!');
Рисунок подтверждает: перевёрнутое число делится на 121:А ещё одной особенностью этих чисел является их палиндромичность - они оди¬
наково читаются и в прямом, и в обратном направлении.Три велосипедиста (Step.First)Задача 16 из книги Удивительный
мир чисел [КА86], страница 55:Три велосипедиста начали с об¬
щего старта движение по круговой
дорожке. Первый делает полный круг за 21 мин, второй - за 35 мин, а третий -
за 15 мин.■ Кордемский, с,44, Задача 4—□XШестизначное числоНаибольшее число равно 699996966669 : 121 = 7989т)
I Через сколько минут они ещё раз окажутся вместе в начальном пункте?Задача решается элементарно, если догадаться, что велосипедисты окажутся в
точке старта через целое число кругов. А это случится, когда пройдёт время t (в
минутах), которое нацело делится на 21, 35 и 15.Запустим часы и будем ждать, пока не пройдёт столько времени (в минутах),
чтобы оно делилось без остатка на означенные числа:usesSystem;// Кордемский, с.55, Задача 16begin// заголовок окна:Console.Title := 'Кордемский, с.55, Задача 16';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Три велосипедиста');Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:
var t := 1.Step().Where(n -> (n mod 21 = 0) and
(n mod 35 = 0) and
(n mod 15 = 0)).First;// печатаем ответ:Println;Console.WriteLine('Велосипедисты встретятся через {0} мин.', t);Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;end.Встреча произойдёт через 105 минут:
■ Кордемский, с.55, Задача 16 —□XТри велосипедистаАВелосипедисты встретятся через 105 мин.VМетоды Last и LastOrDefaultМетоды Last и LastOrDefault похожи на предыдущие методы, но возвращают по¬
следний элемент последовательности:function Last(): T;function Last(predicate: T->boolean): T;
function LastOrDefault(): T;function LastOrDefault(predicate: T->boolean): T;var sq := Range(1,10).Println;
Println(sq.Last);■ Методы Last и LastOrDefault—□XМетоды Last и LastOrDefaultА123456709 10
10Vvar sq :=Range(1,10).Println;
Println(sq.Last(n ->
< 5));* Методы La:t и LastOrDefault□XnМетоды Last и LastOrDefault
123456709 10
var sq := Range(10,1).Println;
Println(sq.LastOrDefault(n -> n < 5));■ Методы Last и LastOrDefault—□XМетоды Last и LastOrDefaultЛVМетоды ElementAt и ElementAtOrDefaultМетоды ElementAt и ElementAtOrDefault возвращают элемент последовательно¬
сти по его номеру (индексу). Нумерация начинается с нуля:і function ElementAt(index: integer): T;I var sq := Range(1,10).Println;Println(sq.ElementAt(7));ill Методы ElementAt и ElementAtOrDefault —□XМетоды ElementAt и ElementAtOrDefaultЛ123456739 10VБудьте внимательны: элемент с индексом 7 занимает восьмое место в последо¬
вательности!Метод ElementAtOrDefault действует так же, как ElementAt, но, если указанный но¬
мер лежит за границами последовательности, то он возвращает значение по
умолчанию:
I function ElementAtOrDefault(index: integer): T;I var sq := Range(1,10).Println;
Println(sq.ElementAtOrDefault(10));■ Методы ElementAt иElementAtOrDefault —□XМетоды ElementAt иElementAtOrDefaultЛ1 23456789
010VИндексу 10 соответствует 11-й элемент последовательности. Но она содержит
только 10 элементов, поэтому метод ElementAtOrDefault возвращает значение по
умолчанию, то есть 0.Пятизначный куб (Range.ElementAt)Задача 556 из книги Математическая шкатулка [Нагибин88]:Задумано пятизначное число, являющееся кубом натурального числа.Восстановите задуманное число, если известно, что оно должно делиться на 3
и последняя цифра его 6.Проще не извлекать кубические корни из чисел, а возводить их в куб. Пределы
изменения основания степени легко найти:// минимальное число:var minn:= Trunc(Power(10000, 1/3));// максимальное число:var maxn:= Trunc(Power(99999, 1/3));
Метод Range генерирует последовательность пятизначных чисел:
і Range(minn, maxn)Из которой мы отбираем числа, удовлетворяющие условиям задачи:і Where(n -> (n*n*n mod 3 = 0) and (n*n*n mod 10 = 6))Если вы распечатаете последовательность отфильтрованных чисел, то увидите,
что в ней единственное число. Чтобы напечатать ответ на экране, мы извлекаем
его:і ElementAt(0);И присваиваем переменной res.Задача решена:usesSystem;// Нагибин 556begin// заголовок окна:Console.Title := 'Нагибин 556';Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Пятизначный куб');Console.ForegroundColor := ConsoleColor.Green;Console.WriteLine();// минимальное число:var minn:= Trunc(Power(10000, 1/3));// максимальное число:var maxn:= Trunc(Power(99999, 1/3));// решаем задачу:
var res:= Range(minn, maxn).Where(n -> (n*n*n mod 3 = 0) and (n*n*n mod 10 = 6))
.ElementAt(0);// печатаем ответ:Println( res, 'в кубе = ', (res*res*res));Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Рисунок показывает, что задумано было число 46 656 (в книге указан неверный
ответ - 45 656):Репетитор (Range.ElementAt)В те далёкие времена, когда компьютеров ещё и в помине не
было, школьники уже вовсю решали задачки по математике,
что давалось им нелегко. Этот познавательный процесс хо¬
рошо описан Антоном Павловичем Чеховым в рассказе Репе¬
титор.Я надеюсь, что вы с удовольствием прочитаете этот рассказ
Чехова от начала до конца, поэтому перейдём непосред¬
ственно к задаче:I Теперь по арифметике... Берите доску. Какая следующая задача?Петя плюёт на доску и стирает рукавом. Учитель берёт задачник и дик¬
тует:Пятизначный шуб
36 в кубе = 46656^180 ^
I - «Купец купил 138 арш. чёрного и синего сукна за 540 руб. Спрашивается,
сколько аршин купил он того и другого, если синее стоило 5 руб. за аршин, а
чёрное 3 руб.?»Тяготы репетиторского труда мы пропускаем и читаем финал этой арифметиче¬
ской истории:- И без алгебры решить можно, - говорит Удодов, протягивая руку к счётам
и вздыхая. - Вот, извольте видеть...Он щёлкает на счётах, и у него получается 75 и 63, что и нужно было.Итак, ответ на задачу известен, и нам только и остаётся, что заменить старые
счёты современными, то есть компьютером.Обозначаем длину (length) сукна и стоимость (cost) соответствующими констан¬
тами:const//общая длина сукна в аршинах:LENGTH: integer = 138;//общая стоимость сукна в рублях:COST: integer = 540;На COST рублей можно купить не более maxBlue аршин синего сукна:begin// заголовок окна:Console.Title := 'Репетитор';Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Репетитор');Console.ForegroundColor := ConsoleColor.Green;Console.WriteLine();// макс. длина синего сукна:
var maxBlue := COST div 5;
Из последовательности чисел 0..maxBlue нам нужно отобрать такие, которые удо¬
влетворяют условиям задачи.Условие задачи записываем в методе расширения Where. В итоге мы получаем
последовательность, состоящую из одного элемента. Нужно взять его методом
ElementAt, чтобы переменная blue имела тип integer:// решаем задачу:var blue := Range(0, maxBlue).Where(n -> n * 5 + (LENGTH - n) * 3 = COST)
.ElementAt(0);Writeln('^Hero сукна (аршин): ', blue);Writeln(^pHoro сукна (аршин): ' + (LENGTH - blue));Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;end.Запускаем программу и получаем ответ:■ Репетитор—□XРепетиторАСинего сукна (аршин): 6В
Чёрного сукна (аршин): 75V
Американское наследство (Range.ElementAt)Задача про американские деньги:Отец оставил сыновьям Чарлзу и
Роберту 100 долларов.Если одну треть части Чарлза вы¬
честь из одной четверти части
Роберта, то останется 11 долла¬
ров.Сколько долларов получил каждый изЗадача про американское наследство решается так же, как про русское сукно:usesSystem;// Фольклор Д39begin// заголовок окна:Console.Title := 'Математический фольклор. Задача Д39';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Американское наследство');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:var Charles := Range(0, 100).Where(n -> (100 - n) div 4 - n div 3 = 11)
.ElementAt(0);Writeln(' Чарлз получил: ', Charles);Writeln(' Роберт получил: ' + (100 - Charles));Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;братьев?
end.А вот и юридический отчёт о проделанной нами работе:■ Математический фольклор. Задача Д39—□XАмериканское наследствоАЧарлз получил: 24
Роберт получил: 76VЧетыре числа (Step.ElementAt)Задача 341 из книги Математическая шкатулка [Нагибин88]:Произведение четырёх последовательных натуральных чисел
равно 3024.Найдите эти числа.Понятно, что задача имеет единственное решение. Если мы найдём 4 последова¬
тельных числа, произведение которых равно 3024, то следующая четвёрка даст
большее произведение.Условие задачи проверяем в методе Where. При первом же выполнении условия
мы извлекаем из отфильтрованной последовательности первое число, и на этом
генерирование чисел заканчивается:usesSystem;// Нагибин 341^184 ^
begin// заголовок окна:Console.Title := 'Нагибин 341';Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Четыре числа');Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:
var m := 1;
var n := m.Step().Where(i -> i * (i + 1) * (i + 2) * (i + 3) = 3024)
.ElementAt(0);// печатаем ответ:Println(n, (n + 1), (n + 2), (n + 3));Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.На рисунке вы видите, что в произведении участвуют числа 6, 7, 8 и 9:■ Нагибин 341—□XЧетыре числаЛ6 7 8 9VРазность кубов (Step.ElementAt)Задача 561 из книги Математическая шка¬
тулка [Нагибин88]:Разность кубов двух последовательных нату¬
ральных чисел равна 331.S-S^x+ylx-y)
[x±yf = xJ ±2xy + yJ
х3 + у3 = (х+у)(х2-ф+у2)
л:3-у3={х-уіх2 + ху + у2)Восстановите эти числа.2
Генерируем бесконечную последовательность натуральных чисел - до тех пор,
пока не найдём такую пару, которая удовлетворяет условию задачи:usesSystem;// Нагибин 561begin// заголовок окна:Console.Title := 'Нагибин 561';Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Разность кубов');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:var m:= 1;var res:= m.Step().Where(n -> (n+1)*(n+1)*(n+1) - n*n*n = 331)
.ElementAt(0);// печатаем ответ:Println( res, 'в кубе = ', (res*res*res));
res += 1;Println(res, 'в кубе = ', (res*res*res));
Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Простой ответ на простую задачу:■ Нагибин 561—□XлРазность кубов10 В Кубе = 100011 в кубе = 1331V
Двузначное число (Range.ElementAt)Задача 563 из книги Математическая шкатулка
[Нагибин88]:I Найдите двузначное положительное число, равное
произведению суммы и разности его цифр.Последовательность двузначных чисел генерирует метод Range:
і Range(10,99)Для каждого числа нужно найти разность и сумму его цифр. Чтобы не усложнять
проверку в методе Where, мы напишем 2 функции:Ivar sub: integer -> integer := x -> Abs(x div 10 - x mod 10);
var sum: integer -> integer := x -> x div 10 + x mod 10;Здесь мы должны проследить, чтобы разность цифр была неотрицательной. Те¬
перь задача решается без проблем:usesSystem;// Нагибин 563begin// заголовок окна:Console.Title := 'Нагибин 563';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Двузначное число');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// функции для вычисления разности и суммы цифрPhilips StadionPSV FEYENOORD180
// заданного числа:var sub: integer -> integer := x -> Abs(x div 10 - x mod 10);
var sum: integer -> integer := x -> x div 10 + x mod 10;// находим число:
var res:= Range(10,99).Where(n -> sum(n)*sub(n) = n).Println
.ElementAt(0);// печатаем ответ:Console.WriteLine('{0} = {1} * {2}', res, sum(res), sub(res));
Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Метод Println распечатывает отфильтрованную последовательность, чтобы мы
могли узнать, сколько решений имеет задача.А решение единственное: двузначное число равно 48. Сумма его цифр - 12, а раз¬
ность - четыре:ёЗ Нагибин 563 — □ XКвадратный куб (Range.ElementAt)Задача 584 из книги Математическая шкатулка [Наги-
бин88]:I Какое трёхзначное число равно кубу цифр его единиц, а
также квадрату числа, составленного из второй и первой
цифр?Двузначное число
4S40 = 12 * 4^188 ^
Генерируем все трёхзначные числа в методе Range:
і Range(100,999)Условия довольно сложные, поэтому пишем отдельные функции.Последняя цифра числа равна остатку от его деления на 10:I var ed: integer -> integer := x -> x mod 10;Её нужно возвести в куб:і var cube: integer -> integer := x -> x * x * x;Немного труднее переставить местами первую и вторую цифры числа:Ivar n21: integer -> integer := x ->(x div 10 mod 10) * 10 + x div 100;А число мы возводим в квадрат:
і var quad: integer -> integer := x -> x * x;Теперь нам не составит труда записать условия задачи в методе Where:Where(n -> (cube(ed(n)) = n) and (quad(n21(n)) = n))Если очередное трёхзначное число i выдержало проверки, то мы печатаем ответ
на экране:IusesSystem;
/ /Нагибин 584begin// заголовок окна:Console.Title := 'Нагибин 584';Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Квадратный куб');Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:var ed: integer -> integer := x -> x mod 10;
var cube: integer -> integer := x -> x * x * x;
var n21: integer -> integer := x ->(x div 10 mod 10) * 10 + x div 100;
var quad: integer -> integer := x -> x * x;
var res:= Range(100,999).Where(n -> (cube(ed(n)) = n) and (quad(n21(n)) = n)).Println.ElementAt(0);// печатаем ответ:Console.WriteLine();Console.WriteLine('Число = {0}', res);Console.WriteLine('{0} в кубе = {1}', ed(res), res);
Console.WriteLine('{0} в квадрате = {1}', n21(res), res);Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Задача имеет единствен¬
ное решение:729 = 93 = 272■ Нагибин 5S4—□XКвадратный кубл729Число = 729
9 в кубе = 729
27 в квадрате = 729V'”)
Четырёхзначное число (Range.ElementAt)Задача 620 из книги Математическая шкатулка [Нагибин88]:I Найдите нечётное четырёхзначное число, две средние цифры которого обра¬
зуют число в пять раз больше числа тысяч и втрое больше числа единиц этого
числа.Весь «фокус» этой задачи заключается в том, чтобы правильно извлечь цифры из
числа:// две средние цифры:var n23: integer -> integer := x -> x div 10 mod 100;// число тысяч:var n1: integer -> integer := x -> x div 1000;// число единиц:var n4: integer -> integer := x -> x mod 10;Так как мы не можем быть уверены, что задача имеет единственное решение, то
печатаем всю отфильтрованную последовательность.Генерируем последовательность нечётных четырёхзначных чисел методом
Range:і Range(1001,9999,2)Начинаем с первого нечётного четырёхзначного числа и заканчиваем последним- 9999. Шаг в методе Range равен двум.С нашими функциями условия в методе Where записываются вполне понятно:
і Where(n -> (n23(n) = n1(n) * 5) and (n23(n) = n4(n) * 3))
Вся программа целиком:usesSystem;// Нагибин 620begin// заголовок окна:Console.Title := 'Нагибин 620';Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Четырёхзначное число');Console.ForegroundColor := ConsoleColor.Green;Console.WriteLine();// решаем задачу -->// две средние цифры:var n23: integer -> integer := x -> x div 10 mod 100;// число тысяч:var n1: integer -> integer := x -> x div 1000;// число единиц:var n4: integer -> integer := x -> x mod 10;var res:= Range(1001,9999,2).Where(n -> (n23(n) = n1(n) * 5) and (n23(n) = n4(n) * 3)).Println.ElementAt(0);// печатаем ответ:Console.WriteLine();Console.WriteLine('Число = {0}', res);Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Ответ на задачу показан
на рисунке.■ Нагибин 620—□XЧетырёхзначное числол3155Число = 31ББV
Зачёркнутая цифра (Range.ElementAt)Задача 600 из книги Математическая шкатулка [Наги-
бин88]:В трёхзначном числе зачеркнули цифру сотен, затем
полученное двузначное число умножили на 7 и полу¬
чили вновь исходное трёхзначное число.Какое это число?Очень простая задача с простым же условием в методе Where:usesSystem;// Нагибин 600begin// заголовок окна:Console.Title := 'Нагибин 600';Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Зачёркнутая цифра');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:var res:= Range(100,999).Where(n -> n mod 100 * 7 = n).Println.ElementAt(0);// печатаем ответ:Console.WriteLine();Console.WriteLine('Число {0} = {1} * 7', res, res mod 100);
Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.А вот и ответ на задачу:■ Нагибин 600—□XЗачёркнутая цифраАЗБ0Число ЗБ0 = Б0 * 7VЗачёркнутая цифра 2 (Range.ElementAt)Задача 587 из книги Математическая шкатулка [Наги-
бин88]:В трёхзначном числе зачеркнули среднюю цифру. Полу¬
ченное двузначное число оказалось в 6 раз меньше ис¬
ходного трёхзначного.Какое такое трёхзначное число.Cамая «сложная» часть задачи заключается в зачёркивании средней цифры. В ре¬
зультате получается двузначное число, которое мы вычисляем в функции n2:I //двузначное число:var n2: integer -> integer := x -> x div 100 * 10 + x mod 10;Остальная часть программы не представляет ни малейшего труда:usesSystem;
// Нагибин 587begin//заголовок окна:Co nsole.Title := 'Нагибин 587';Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Зачёркнутая цифра 2');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу -->// двузначное число:var n2: integer -> integer := x -> x div 100 * 10 + x mod 10;var res:= Range(100,999).Where(n -> n = n2(n) * 6).Println.ElementAt(0);// печатаем ответ:Console.WriteLine();Console.WriteLine('Число {0} = {1} * 6', res, n2(res));
Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Вот и вся задача:VЗачёркнутая цифра 2
10SЧисло 10S = 18 * 6
Метод ReverseМетод Reverse возвращает последовательность, в которой элементы располага¬
ются в обратном порядке:і function Reverse(): sequence of T;I var sq := Range(1, 10).Println;
sq.Reverse.Println;■ Метод Reverse—□XАМетод Reverse12B4567S9 1010 9S7654321VМетоды OrderBy и OrderByDescendingМетод OrderBy возвращает коллекцию типа lOrderedEnumerable, отсортирован¬
ную в порядке возрастания значения ключа функцией keySelector:I function OrderBy<Key>(keySelector: T->Key):System.Linq.IOrderedEnumerable<T>;При равенстве значения ключей порядок элементов сохраняется (строгая сорти¬
ровка). Функция keySelector определяет условия сортировки. Например, если мы
хотим отсортировать коллекцию по значению элементов, то условие должно быть
самым простым - (x->x). Числа сортируются по величине, а слова - в алфавитном
порядке.Создаём последовательность из 20 случайных чисел:
I var sq := SeqRandom(20, 1, 30).ToArray.Println;И сортируем их по величине:
sq.OrderBy(n -> n).Println;* М егоды О rd егВу и О rd егВу D еьс en d in g□XМетоды OrderEy и Order'EyDestending2 18 11 13 6 Б 2 1 26 23 30 27 27 2Б 28 1 6 27 18 22
1 1 2 2 Б 6 6 11 13 18 18 22 23 2Б 26 27 27 27 28 30Задавая более сложные условия, можно, например, отсортировать слова
длине:Ivar lstStrAll := ReadAllLines('OSH-W97.txt');
var lstStr:= lstStrAll.Take(8).Println;
lstStr.OrderBy(s -> s.Length).Println;■ Методы OrderBy и QrderByDescendingAVМетоды DndenBy и DrderByDescendingАБАЖУР АБАЗИНЕЦ АБАЗИНКА АББАТ АББАТИСА АББАТСТВО АББРЕВИАТУРА АБЕРРАЦИЯ
АББАТ АБАЖУР АБАЗИНЕЦ АБАЗИНКА АББАТИСА АББАТСТВО АБЕРРАЦИЯ АББРЕВИАТУРАИли по последней букве:lstStr.OrderBy(s -> s.Last()).Println;
■ Методы OrderBy и OrderByD esc endingМетоды OrderBy и OrderByDescendingАБАЖУР АБАЗИНЕЦ АБАЗИНКА АББАТ АББАТИСА АББАТСТВО АББРЕВИАТУРА АБЕРРАЦИЯ
АБАЗИНКА АББАТИСА АББРЕВИАТУРА АББАТСТВО АБАЖУР АББАТ АБАЗИНЕЦ АБЕРРАЦИЯСортировка слов в «кроссвордном» порядке легко достижима последовательным
применением методов OrderBy с соответствующими условиями:lstStr.OrderBy(s -> s).OrderBy(s -> s.Length).Println;■ Методы OrderBy и OrderBy Descending — □ XМетоды OrderBy и OrderByDescendingЙБАЖУР АБАЗИНЕЦ АБАЗИНКА АББАТ АББАТИСА АББАТСТВО АББРЕВИАТУРА АБЕРРАЦИЯ
ЙББАТ АБАЖУР АБАЗИНЕЦ АБАЗИНКА АББАТИСА АББАТСТВО АБЕРРАЦИЯ АББРЕВИАТУРАI—чВ отличие от простои сортировки по длине здесь слова равной длины дополни¬
тельно отсортированы по алфавиту.А вот так просто можно отсортировать буквы в словах:Ivar chs := lstStr.Select(str ->new string(str.ToCharArray.OrderBy(c -> c).ToArray))
.Println;■ Методы OrderBy и OrderByDescendingVМетоды OrderBy и OrderByDescendingАБАЖУР АБАЗИНЕЦ АБАЗИНКА АББАТ АББАТИСА АББАТСТВО АББРЕВИАТУРА АБЕРРАЦИЯ
ААБЖРУ ААБЕЗИНЦ АААБЗИКН ААББТ АААББИСТ ААББВОСТТ АААББВЕИРРТУ ААБЕИРРЦЯ
Второй метод OrderBy сортирует заданную последовательность, используя задан¬
ный компаратор comparer:function OrderBy<Key>(keySelector: T->Key; comparer: IComparer<Key>):System.Linq.IOrderedEnumerable<T>;В методе Compare можно задать любые, самые сложные условия сравнения каж¬
дой пары слов. Например, для сравнения мы можем сравнивать слова по длине,
а если они имеют одинаковую длину, то по алфавиту:typeStrComparer = class(IComparer<string>)
publicfunction Compare(s1, s2: string): integer;
beginvar len1 := s1.Length;
var len2 := s2.Length;
if (len1 <> len2) thenResult:= s1.Length.CompareTo(s2.Length)elseResult := s1.CompareTo(s2);
end;
end;Чтобы сделать отсортированный список более наглядным, мы сначала выберем
слова, в которых не более 7 букв, а затем сравним их с помощью нашего компа¬
ратора:lstStr := lstStrAll.Take(30).Println;Println;lstStr.Where(s -> s.Length < 8).OrderBy(s -> s, new StrComparer()).Println;
■ Методы OrderBy и OrderByD esc endingМетоды OrderBy и OrderByDescendingАБАЖУР АБАЗИНЕЦ АБАЗИНКА АББАТ АББАТИСА АББАТСТВО АББРЕВИАТУРА АБЕРРАЦИЯАБАЖУР АБАЗИНЕЦ АБАЗИНКА АББАТ АББАТИСА АББАТСТВО АББРЕВИАТУРА АБЕРРАЦИЯ АБЗА
Ц АБИССИНЕЦ АБИССИНКА АБИТУРИЕНТ АБИТУРИЕНТКА АБОНЕМЕНТ АБОНЕНТ АБОНЕНТКА АБО
РДАЖ АБОРИГЕН АБОРИГЕНКА АБОРТ АБОРТИВНОСТЬ АБРАЗИВ АБРАКАДАБРА АБРЕК АБРИКОС
АБРИС АБСЕНТЕИЗМ АБСОЛЮТ АБСОЛЮТИЗМ АБСОЛЮТНОСТЬАББАТ АБЗАЦ АБОРТ АБРЕК АБРИС АБАЖУР АБОНЕНТ АБОРДАЖ АБРАЗИВ АБРИКОС АБСОЛЮТМетоды OrderByDescending возвращают коллекцию типа lOrderedEnumerable, от¬
сортированную в порядке убывания значения ключа функцией keySelector:function OrderByDescending<Key>(keySelector: T->Key):System.Linq.IOrderedEnumerable<T>;function OrderByDescending<Key>(keySelector: T->Key;comparer: IComparer<Key>):
System.Linq.IOrderedEnumerable<T>;Методы ThenBy и ThenByDescendingМетод ThenBy применяют для дополнительной сортировки последовательности
типа lOrderedEnumerable, которую он получает после выполнения методов
OrderBy или OrderByDescending, по возрастанию значения ключа:і function ThenBy<Key>(keySelector: T->Key): System.Linq.IOrderedEnumerable<T>;При равенстве значения ключей порядок элементов сохраняется (строгая сорти¬
ровка).”)
Опять выполним кроссвордную сортировку для слов из списка IstStr, которые не
длиннее 8 букв (Where). Сначала мы сортируем слова по длине (OrderBy), а затем- в пределах слов равной длины - по алфавиту (ThenBy):var lstStrAll := ReadAllLines('OSH-W97.txt');
var lstStr := lstStrAll.Take(30).Println;Println;lstStr.Where(s -> s.Length < 9).OrderBy(s -> s.Length).ThenBy(s -> s).Println;llJ Методы ThenBy и ThenByDescending — □ XМетоды ThenBy и ThenByDescendingАБАЖУР АБАЗИНЕЦ АБАЗИНКА АББАТ АББАТИСА АББАТСТВО АББРЕВИАТУРА АБЕРР
АЦИЯ АБЗАЦ АБИССИНЕЦ АБИССИНКА АБИТУРИЕНТ АБИТУРИЕНТКА АБОНЕМЕНТ АБО
НЕНТ АБОНЕНТКА АБОРДАЖ АБОРИГЕН АБОРИГЕНКА АБОРТ АБОРТИВНОСТЬ АБРАЗИ
В АБРАКАДАБРА АБРЕК АБРИКОС АБРИС АБСЕНТЕИЗМ АБСОЛЮТ АБСОЛЮТИЗМ АБСО
ЛЮТНОСТЬАББАТ АБЗАЦ АБОРТ АБРЕК АБРИС АБАЖУР АБОНЕНТ АБОРДАЖ АБРАЗИВ АБРИКОС
АБСОЛЮТ АБАЗИНЕЦ АБАЗИНКА АББАТИСА АБОРИГЕНВторой метод ThenBy дополнительно сортирует заданную последовательность,
используя заданный компаратор comparer:I function ThenBy<Key>(keySelector: T->Key; comparer: IComparer<Key>):System.Linq.IOrderedEnumerable<T>;Методы ThenByDescending дополнительно сортируют заданную последователь¬
ность в порядке убывания значения ключа:function ThenByDescending<Key>(keySelector: T->Key):System.Linq.IOrderedEnumerable<T>;function ThenByDescending<Key>(keySelector: T->Key; comparer: IComparer<Key>):System.Linq.IOrderedEnumerable<T>;
Методы Take и TakeWhileМетод Take возвращает первые count элементов последовательности:
function Take(count: integer): sequence of T;Создаём последовательность из 20 элементов и отбираем из неё первые 10 эле¬
ментов методом Take:Ivar sq := Range(1,20).Println;
sq.Take(10).Println;■ Метод Take- □ XМетод Take1 2 3 4 5 6 7S 9 10 11 12 13 14 15 16 17 IS 19 201 2 3 4 5 6 7S 9 10VЕсли значение параметра count больше числа элементов в последовательности,
то метод Take вернёт всю последовательность.Метод TakeLast возвращает последние count элементов последовательности:
function TakeLast(count: integer): sequence of T;Создаём такую же последовательность, что и в первом примере, но отбираем 10
последних элементов:I Println;
sq := Range(1,20).Println;
sq.TakeLast(10).Println;
■ Метод TakeМетодTake12 34 5 67 S 91011121314151617IS192012 34 5 67 S 91012 34 5 67 S 91011121314151617IS192011 1213 1415 1617IS1920Метод TakeWhile возвращает элементы последовательности, удовлетворяющие
заданному условию, пока не встретится первый элемент, не удовлетворяющий
этому условию:і function TakeWhile(predicate: T->boolean): sequence of T;Отберём из последовательности первые нечётные числа:Ivar sq := Range(1,20).Println;
sq.TakeWhile(n -> n mod 2 <> 0).Println;Метод Take — □ XМетодTake12 34 5 6 7 S 9 10 11 12 13 14 15 16 17 IS 19 2011 - нечётное число. Напечатано.2 - чётное число - метод TakeWhile заканчивает работу.Второй метод TakeWhile возвращает элементы последовательности, удовлетворя¬
ющие заданному условию с учётом индексов, пока не встретится первый элемент,
не удовлетворяющий этому условию:function TakeWhile(predicate: (T,integer)->boolean): sequence of T;
Из той же последовательности натуральных чисел выберем такие, которые имеют
значение меньше 9 и индекс меньше 12:Ivar sq := Range(1,20).Println;sq.TakeWhile((n, i) -> (i < 12) and (n < 9)).Println;Методы Take и TakeWhile — □ XМетоды TakeиTakeWhile1 2 В 4 5 67В 9 10 11 12 13 14 15 16 17 IB 19 201 2 В 4 5 67Индийские обезьяны 2 (Step. Take)Квадрат восьмой части стада обезьян
играют в роще, а остальные 12 - на
горке.Сколько обезьян в стаде?Эта задача решается с помощью квадрат- I■'-ч w Ік sJ «■№*- -1М !Ру%-у ■ 44. ИСШйЯт 1 f** Iного уравнения, у которого 2 деиствитель- Мі^Ь •я
ных корня. Если задача может иметь несколько решений, то лучше заменить ме¬
тод SkipWhile методом Where, который из бесконечной последовательности от¬
берёт только те числа, которые удовлетворяют заданному условию. При этом ме¬
тод Step будет выдавать бесконечную последовательность. У задачи ровно 2 ре¬
шения, поэтому после метода Where нужно поставить метод Take(2). Так мы полу¬
чим оба решения и остановим генерирование элементов.Если условие длинное, то его можно записать как функцию:var f: integer -> boolean := n -> n - (n div 8) * (n div 8) = 12;
Число обезьян заведомо кратно 8, поэтому генерируем последовательность от 0
с шагом 8:usesSystem;// Фольклор Д12begin// заголовок окна:Console.Title := 'Математический фольклор. Задача Д12';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Индийские обезьяны 2');Console.ForegroundColor := ConsoleColor.Green;Console.WriteLine();// решаем задачу
var m := 0;var f: integer -> boolean := n -> n - (n div 8) * (n div 8) = 12;Print(' Обезьян было: ');m.Step(8).Where(f).Take(2).Println;Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Два ответа на одну задачу:■ Математический фольклор. Задача Д12—□XИндийские обезьяны 2АОбезьян было: 16Обезьян было: 48V
Жизнь Демохара (Step.Take)Множество занимательных задач связано с вычислением
возраста. Одна из первых появилась в Греции в конце IV
века:Демохар четверть жизни прожил мальчиком, пятую
часть - юношей, одну треть - зрелым мужчиной и 13
лет пожилым.Сколько лет прожил Демохар?Простейшая тренировочная задача. Очевидно, что возраст Демохара нацело де¬
лится на 3, 4 и 5. Но мы предположим, что это не так и генерируем последова¬
тельность чисел типа double. Условие задачи удобно записать в виде функции:і var f: double -> boolean := n -> n / 4 + n / 5 + n / 3 + 13 = n;Вся программа целиком:usesSystem;// Математический фольклор. Задача Д9begin// заголовок окна:Console.Title := 'Математический фольклор. Задача Д9';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Жизнь Демохара');Console.ForegroundColor := ConsoleColor.Green;Console.WriteLine();// решаем задачу
var v := 0.0;var f: double -> boolean := n -> n / 4 + n / 5 + n / 3 + 13 = n;^206 ^
Print(' Демохар прожил: ');
v.Step(1).Where(f).Take(1).Println;Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Демохар прожил 60 лет:■ Математический фольклор, Задача Д9—□XЖизнь ДемохараЛДемохар прожил: 60VИндийское число (Step.Take)Условие задачи:Если некоторое число умножить на 5, от
произведения отнять его треть, остаток
разделить на 10 и к полученному числу при¬
бавить последовательно одну треть, одну
вторую и одну четвёртую первоначального
числа, то получится 68.Какое это число?Мы можем только посочувствовать древним индийцам, у которых не было ком¬
пьютеров, чтобы решать такие задачи.Задача очень простая. Главное - правильно записать её условие:”0
var f: integer -> boolean := n -> (n * 5 - n * 5 div 3)div 10 + n div 3 + n div 2 + n div 4 = 68;Вот и вся задача:usesSystem;// Фольклор Д7begin// заголовок окна:Console.Title := 'Математический фольклор. Задача Д7';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Индийское число');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу
var m := 1;var f: integer -> boolean := n -> (n * 5 - n * 5 div 3)
div 10 + n div 3 + n div 2 + n div 4 = 68;Print(' Число равно: ');
m.Step(1).Where(f).Take(1).Println;Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Искомое число должно
делиться нацело на 2, 3 и
4, поэтому мы могли бы
значительно сократить
перебор, но в данном
случае этого не требу¬
ется.■ Математический фольклор, Задача Д7—□XЛИндийское числоЧисло равно: 48V
Пифагорейское число (Step.Take)Знаменитого Пифагора спросили:- Сколько учеников посещают твою школу и слу¬
шают твои беседы?Пифагор ответил:- Половина моих учеников изучает математику,
четверть - музыку, седьмая часть проводит
время в молчаливом размышлении, остальную часть составляют 3 ученика.Сколько учеников было у Пифагора?Эта задача напечатана и в книге Увлекательная математика. Античные этюды.
Задача 5. Пифагор Самосский (ок. 508-501 гг. до н.э.):Поликрат (известный из баллады Шиллера тиран с острова Самос) однажды
спросил на пиру у Пифагора, сколько у того учеников.- Охотно скажу тебе, о Поликрат, - отвечал Пифагор. - Половина моих уче¬
ников изучает прекрасную математику, четверть исследует тайны вечной
природы, седьмая часть молча упражняет силу духа, храня в сердце учение.
Добавь ещё к ним трёх юношей, из которых Теон превосходит прочих своими
способностями.Сколько учеников было у Пифагора?Очевидно, что число учеников должно быть кратно 4 и 7, но тогда нечего решать.
Поэтому мы проигнорируем это наблюдение и выберем для переменной m тип
double.Условие легко выводится из текста задачи:і var f: double -> boolean := n -> n / 2 + n / 4 + n / 7 + 3 = n;
И всё равно решение задачи очень простое:usesSystem;// Фольклор Д4begin// заголовок окна:Console.Title := 'Математический фольклор. Задача Д4';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Пифагорейское число');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу
var m := 1.0;var f: double -> boolean := n -> n / 2 + n / 4 + n / 7 + 3 = nPrint(' Учеников было: ');
m.Step(1).Where(f).Take(1).Println;Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.У Пифагора было 28 учеников:llJ Математический фольклор, Задача Д4 — □ XПифагорейское число
Учеников было: 28
Диофантово число (Step.Take)Задача древнегреческого математика Диофанта
Александрийского, жившего в третьем веке:По двум данным числам 200 и 5 найти третье
число, которое, если его умножить на одно из
них, даёт полный квадрат, а если его умножить
на другое число, даёт квадратный корень из
этого квадрата.Генерируем бесконечную последовательность, начи¬
ная с 1. Условие задачи переводим в лямбда-выраже¬
ние:var f: integer -> boolean := n -> (n * 200).IsQuadrat and((n * 200).Sqrt = 5 * n);Здесь мы использовали метод расширения IsQuadrat для типа integer. Других
сложностей в задаче нет:usesSystem, OlympUnit;// Увлекательная математика АЭ14begin// заголовок окна:Console.Title := 'Увлекательная математика. Задача АЭ14';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Диофантово число');Console.ForegroundColor := ConsoleColor.Green;Console.WriteLine();// решаем задачу
var m := 1;
var f: integer -> boolean := n -> (n * 200).IsQuadrat and((n * 200).Sqrt = 5 * n);Print(' Число = ');
m.Step(1).Where(f).Take(1).Println;Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Искомое число равно 8:■ Увлекательная математика, Задача АЭ14—□XДиофантово числоЛЧисло = SVГодится также число 0, но оно явно не древнегреческое.Если не копировать полностью требование задачи, то условие можно записать
проще:і var f: integer -> boolean := n -> 5 * n = (n * 200).Sqrt;Индийские квадраты (Step.Take)Индийская задача
арифметики:века из «Бахшалийской» рукописнойНайти число, которое от прибавления 5 или отнятия11 обращается в полный квадрат.
Искомое число не меньше 11. Ищем его в бесконечной последовательности.
Условие задачи записывается очень просто:Ivar f: integer -> boolean := n -> (n + 5).IsQuadrat and(n - 11).IsQuadrat;Задача имеет 2 решения, поэтому ставим двойку в вызове метода Take:usesSystem, OlympUnit;// Наука и жизнь 1963-03-38-4begin// заголовок окна:Console.Title := 'Наука и жизнь 1963-03-38-4';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Индийские квадраты');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу
var m := 11;var f: integer -> boolean := n -> (n + 5).IsQuadrat and(n - 11).IsQuadrat;Print(' Число = ');
m.Step(1).Where(f).Take(2).Println;Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Вот 2 числа, удовлетворяющие условиям задачи:
■ Наука и жизн ь 1963 -03- 33-4Індийские квадратыЧисло = 11
Число = 2ѲАVПри n = 11 мы получаем квадраты 16 и 0, а при n = 20 - 25 и 9.Половина и треть (Step.Take)Задача 489 из книги Математическая
шкатулка [Нагибин88]:Запишите наименьшее натуральное
число, половина и треть которого
были бы соответственно квадратом и
кубом некоторых натуральных чисел.1 1 1A—уВ1i- - половина
21 1lA—VJВ13- третьТак как искомое число одновременно делится на 2 и на 3, то его следует искать
среди чисел, кратных 6. Мы не ограничимся поиском единственного числа с озна¬
ченными свойствами, а найдёт 7 вариантов решения задачи среди чисел до 100
миллионов:usesSystem, OlympUnit;// Нагибин 489begin// заголовок окна:Console.Title := 'Нагибин 489';
Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Половина и треть');
Console.ForegroundColor := ConsoleColor.Green;var m:= 6;var res:= m.Step(6).Where(n-> (n div 2).IsQuadrat and
(n div 3).IsCube).Take(7);// печатаем ответ:Println;foreach var n in res do
beginPrintln(n, Sqrt(n div 2), Power((n div 3), 1.0 / 3.0));
end;Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Задача - совершенно компьютерная, поэтому он справляется с ней в считанные
мгновения. Наименьшее число - 648:* Нагибин 439□XПоловина и треть648 1S 6
41472 144 24
472392 486 Б4
2654208 1152 96
10125000 2250 150
302330SS 3SSS 216
76236552 6174 294
Методы Skip и SkipWhileМетод Skip пропускает первые count элементов последовательности и возвра¬
щает остальные:function Skip(count: integer): sequence of T;Создаём последовательность из 20 элементов, пропускаем первые 10 методом
Skip и печатаем оставшиеся:Ivar sq := Range(1,20).Println;
sq.Skip(10).Println;ill Методы Skip и SkipWhile — □ XМетоды Skip и SkipWhileI 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 IS 19 20II 12 13 14 1Б 16 17 IS 19 20Если значение параметра count больше числа элементов в последовательности,
то метод Skip вернёт пустую последовательность.Метод SkipLast возвращает последовательность без последних count элементов:
function SkipLast(count: integer): sequence of T;Создаём такую же последовательность, что и в первом примере, но печатаем по¬
следовательность без 10 последних элементов:I Println;
sq := Range(1,20).Println;
sq.SkipLast(10).Println;
■ Методы Skip и Skip WhileМетоды Skipи SkipWhile1 2 3 4 5 67 8 9 10 11121314151617IS192011 12 13 1415 16 17 1819201 2 3 4 5 67 8 9 10 11121314151617IS19201 2 3 4 5 67 8 9 10Метод SkipWhile пропускает начальные элементы последовательности, удовле¬
творяющие заданному условию, а потом возвращает оставшиеся элементы:function SkipWhile(predicate: T->boolean): sequence of T;Пропускаем первые нечётные числа и печатаем все остальные:Ivar sq := Range(1,20).Println;
sq.SkipWhile(n -> n mod 2 <> 0).Println;ill Методы Skip и SkipWhile — □ XМетоды SkipиSkipWhile1 2 3 4 5 67S 9 10 11 12 13 14 15 16 17 IS 19 202 3 4 5 6 7S9 10 11 12 13 14 15 16 17 IS 19 20Второй метод SkipWhile пропускает начальные элементы последовательности,
удовлетворяющие заданному условию с учётом индексов, а потом возвращает
оставшиеся элементы:function SkipWhile(predicate: (T,integer)->boolean): sequence of T;
В той же последовательности натуральных чисел пропускаем такие, которые
имеют значение меньше 9 и индекс меньше 12:var sq := Range(1, 20).Println;sq.SkipWhile((n, i) -> (i < 9) and (n < 12)).Println;ill Методы Skip и SkipWhile — □ XМетоды Skip и SkipWhile1 2 В 4 5 6 7 S 9 10 11 12 IB 14 15 16 17 IS 19 20
10 11 12 13 14 15 16 17 IB 19 20Наименьшее число (Step.SkipWhile)В журнале Наука и жизнь, №2 за 1968 год, на странице 57
напечатана такая задача:НАЙТИ
НАИМЕНЬШЕЕ ЧИСЛОНайдите наименьшее чис¬
ло, которое при делении на
2, 3. 4, 5 и 6 дает остатки
соответственно 1, 2, 3, 4
и 5.Очень простая задача на сообразительность:НАИТИ НАИМЕНЬШЕЕ
ЧИСЛО- Прибавим к искомому чис-
л> 1. Получеиния сумма
должна делиться одновре¬
менно на 2. 3. А. 5 и 6. Наи¬
меньшее такое число 60.
Следоиатсльно, искомое ч іс
ло:60 — 1 - 59.
Мы не знаем диапазона для поиска чисел, но понятно, что искомое число - нату¬
ральное, поэтому мы будем искать его в бесконечной последовательности.Бесконечную последовательность натуральных чисел можно получить с помощью
метода расширения Step для чисел типа integer. Начало последовательности за¬
даём в переменной n:і var n := 1;По умолчанию шаг изменения элементов последовательности равен 1, что нам и
нужно.Распечатать бесконечную последовательность нельзя, но метод Step работает
верно.Так как нас интересует число, которое удовлетворяет условиям задачи, то все про¬
чие мы должны пропустить. Для этого предназначен метод расширения SkipWhile,
которому нужно передать условие задачи тем же способом, что и методу Where:.SkipWhile(n -> (n mod 2 <> 1) or
(n mod 3 <> 2) or
(n mod 4 <> 3) or
(n mod 5 <> 4) or
(n mod 6 <> 5))Метод SkipWhile пропускает элементы последовательности, которые не удовле¬
творяют условию задачи, до тех пор, пока не встретится элемент, который ему
удовлетворяет. После этого он будет оставлять все следующие элементы, которых
бесконечно много. Но нам нужен только 1 элемент - то число, которое первым
подошло под условие задачи. А оно и есть первое в той последовательности, ко¬
торую мы получили от метода SkipWhile. Начало последовательности можно по¬
лучить от метода расширения Take, которому нужно передать число элементов,
которые мы желаем получить. Нам нужен только 1 элемент, поэтому в скобках
указываем единицу. Метод Take останавливает дальнейшее генерирование эле¬
ментов последовательности.
Задача решена:usesSystem;// Наука и жизнь, №2 за 1968 год, стр. 57begin// заголовок окна:Console.Title := 'Наука и жизнь, №2 за 1968 год, стр. 57';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Наименьшее число');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу
var n := 1;
var sq := n.Step.SkipWhile(n -> (n mod 2 <> 1) or
(n mod 3 <> 2) or
(n mod 4 <> 3) or
(n mod 5 <> 4) or
(n mod 6 <> 5)).Take(1).Println;Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Искомое число оказалось очень небольшим:ІІ3 Наука и жизнь, №2 за 196S год. стр, 57—□XНаименьшее числолИскомое число = 59VЛегко заметить, что метод расширения Step действует аналогично бесконечному
циклу while.
Индийские пчёлы (Step.SkipWhile)А эту задачу придумали в Древней Индии:Пчёлы числом, равным квадратному корню
из половины их числа во всём рое, сели на
куст жасмина, 8/9 пчёл полетели назад к
рою. И только одна пчела из того же роя
кружилась над цветком лотоса, привлечён¬
ная жужжанием подруги, неосторожно уго¬
дившей в ловушку сладко благоухающего цветка.Сколько всего пчёл было в рое?Так число пчёл - целое, то в рое должно быть столько пчёл, чтобы их число нацело
делилось и на 2 и на 9, то есть на 18.Как и в предыдущей задаче, создаём бесконечную последовательность и пропус¬
каем все элементы, которые не удовлетворяют условию задачи. Первое подходя¬
щее число мы извлекаем методом Take и печатаем его на экране:usesSystem;// Увлекательная математика АЭ3begin// заголовок окна:Console.Title := 'Увлекательная математика АЭ3';Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Индийские пчёлы');Console.ForegroundColor := ConsoleColor.Green;Console.WriteLine();var b := 0;
b.Step(18)
.SkipWhile(d -> (d div 2).Sqrt + d * 8 div 9 + 2 <> d)
.Take(1).Println;Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Пчёл в рое было 72:■ Уел екател ьн ая м атем эти ка АЗ 3—□XАИндийские пчёлыПчел было: 72VИндийские обезьяны (Step.SkipWhile)Очень старая индийская задача про обе¬
зьян. Подобные задачи были известны в Ин¬
дии ещё в XII веке.Сколько обезьян в стаде, если квадрат
одной пятой их без 3 скрылось в пещере,
а одна залезла на дерево?Задача решается аналогично предыдущим. В методе Step нужно учесть, что число
обезьян кратно 5. Начальное значение переменной m равно 15, чтобы избежать
отрицательных чисел:usesSystem;//Фольклор Д10^222 ^
begin//заголовок окна:Console.Title := 'Математический фольклор. Задача Д10';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Индийские обезьяны');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу
var m := 15;Print(' Обезьян было: ');
m.Step(5).SkipWhile(n -> (n div 5 - 3).Sqr + 1 <> n).Take(1).Println;Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.стаде было 50 обезьян:■ Математический фольклор. Задача Д10 — □ XІндийские обезьяны
Обезьян было: 50
Дедушка и внучка (Step.SkipWhile)Задача 10 (10.1) из книги Удивительный мир чи¬
сел [КА86], страница 52:Сколько дедушке лет, столько месяцев
внучке. Дедушке с внучкой вместе 91 год.Сколько лет дедушке и сколько внучке?Метод Step генерирует последовательность натуральных чисел, а метод SkipWhile
пропускает все числа, не удовлетворяющие условию задачи.Так как нас интересует первое же подходящее число, то мы вызываем метод First
и получаем его в переменной res:usesSystem;//Кордемский, с.52, Задача 10begin//заголовок окна:Console.Title := 'Кордемский, с.52, Задача 10';Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Дедушка и внучка');Console.ForegroundColor := ConsoleColor.Green;Console.WriteLine();var res:= 0.Step().SkipWhile(x -> x + 12 * x <> 91 * 12).First;Console.WriteLine('Возраст внучки в годах = {0}', res div 12);
Console.WriteLine('Возраст дедушки в годах = {0}',91 - res div 12);
Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Задача решается очень просто:1 Кордемский, с,52, Задача 10—□XДедушка и внучкаЛВозраст внучки в годах = 7
Возраст дедушки в годах = 84VНа одно делится, на другое нет (Step.SkipWhile)Задача 2 из книги Удивительный мир чисел [КА86], страница 84:I Найдите наименьшее число, которое делится на 77, а при делении на 74 даёт в
остатке 48.Простая переборная задача - как раз для решения на компьютере. Поскольку ис¬
комое число кратно 77, то его можно представить в виде:I num = 77*nЗначение переменной n нужно изменять от 1 до ... - пока задача не будет решена.
Здесь вполне уместно использовать метод Step:і 1.Step()Метод SkipWhile пропускает все числа, не удовлетворяющие условиям задачи:
I SkipWhile(n -> 77 * n mod 74 <> 48)Метод First прерывает бесконечную последовательность, как только искомое
число будет найдено:usesSystem;// Кордемский, с.84, Задача 2begin// заголовок окна:Console.Title := 'Кордемский, с.84, Задача 2';
Console.WniteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('На одно делится, на другое нет');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:
var num := 1.Step().SkipWhile(n -> 77 * n mod 74 <> 48). First;//печатаем искомое число:Console.WriteLine('Искомое число равно {0}', num * 77);
Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;end.Ответ на задачу показан на рисунке:ёЗ Кордемский, с.84, Задача 2 — □ XНа одно делится, на другое нет
Искомое число равно 1232
Кубическое число (Step.SkipWhile)Задача 1 из книги Удивительный
мир чисел [КА86], страница 89:Найдите число, куб которого -
данное число:1) М = 996 703 628 669;2) N = 1 011 443 374 872.Самый простой способ решения - извлечь из заданных чисел кубический корень.Но поскольку кубические корни даже из очень больших чисел невелики, то можно
найти их простым перебором, даже начиная с нуля!Однако методы расширения для последовательностей не умеют работать с чис¬
лами типа int64, поэтому пишем простую функцию cube:// решаем задачу:var num : int64 := 996703628669;//var num : int64 := 1011443374872;var cube: int64 -> boolean:= i -> i * i * i < num;var res := 0.Step().SkipWhile(i -> cube(i) = true).First;Вот и вся программа:usesSystem;//Кордемский, с.89, Задача 1begin//заголовок окна:Console.Title := 'Кордемский, с.89, Задача 1';Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Ѳ
Console.WriteLine('Кубическое число');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:var num : int64 := 996703628669;//var num : int64 := 1011443374872;var cube: int64 -> boolean:= i -> i * i * i < num;var res := 0.Step().SkipWhile(i -> cube(i) = true).First;// печатаем ответ:Console.WriteLine('Кубический корень из числа {0} = {1}',num, res);Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;end.Точные значения искомых корней:ЁЗ Кордемский, с.&9, Задача 1 —X□Лкубическое числоКубический корень из числа 996703628669= 9989V■ Кордемский, C.S9, Задача 1 —□ XАКубическое числоКубический корень из числа 1011443374872= 10038V^228 ^
Метод SelectМетод Select возвращает последовательность элементов исходной последова¬
тельности, к которым была применена функция selector:function Select(selector: T -> TRes): sequence of TRes;С помощью этого метода мы легко найдём квадраты и квадратные корни элемен¬
тов заданной последовательности:Ivar sq := Range(1,10).Println;
sq.Select(n -> n * n).Println;
sq.Select(n -> Sqrt(n)).Println;■ Метод SelectX□1Метод SelectЛ123456789101 4 9 16 25 36 49 6481 1001 1.4142135623731 1.73205000756S0S 22.23606797749979 2.4494S97427S31S 2.64575131106459 2.828427124746193 3.16227766016033VВторой метод Select возвращает последовательность элементов исходной после¬
довательности с учётом индекса, к которым была применена функция selector:function Select(selector: (T,integer)->TRes): sequence of TRes;Создаём последовательность натуральных чисел и находим произведение каж¬
дого элемента с его индексом:Ivar sq := Range(1,20).Println;
sq.Select((n, i) -> n * i).Println;
■ Метод SelectКузнечики (Range.Select)В седьмом номере журнала Квантик за 2016 год,
на странице 32 напечатана вот такая конкурсная задача:31. Двадцать пять ребят пошли в лес
и стали ловить кузнечиков. Несколько
ребят поймали по одному кузнечику,
половина оставшихся ребят поймали
по два кузнечика, а остальные не смог¬
ли поймать ни одного. Сколько всего
кузнечиков поймали ребята?Можно решить задачу алгебраически, обозначив буквой n число ребят, которые
поймали по одному кузнечику. Но на паскале задачу лучше решать простым пе¬
ребором.Мы знаем, что х не меньше 1 и не больше 25. Причём это число нечётное, иначе
число оставшихся ребят также будет нечётным, а от такого числа нельзя взять
ровно половину.В методе Range мы изменяем значение переменной n на 2, чтобы оно всегда было
нечётным. Число кузнечиков вычисляем в методе Select по формуле:n + (25 - n) div 2 * 2
usesSystem;// Кузнечикиbegin// заголовок окна:Console.Title := 'Квантик, 2016, #7, c. 32';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Кузнечики');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:Range(1,25,2).Select(n -> n + (25 - n) div 2 * 2)
.Println(NewLine);Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Запускаем программу и смотрим на список:I Кб антик, 2G16, #7, с, 32 - □ XКузнечикиЧислокузнечиков=25Числокузнечиков=25Числокузнечиков=25Числокузнечиков=25Числокузнечиков=25Числокузнечиков=25Числокузнечиков=25Числокузнечиков=25Числокузнечиков=25Числокузнечиков=25Числокузнечиков=25Числокузнечиков=25Числокузнечиков=25
Оказывается, каким бы ни было число ребят, поймавших по 1 кузнечику, общее
число кузнечиков всегда равняется 25.А теперь легко догадаться, почему так происходит. Выражение
х + (25 - х) / 2 * 2при любом значении х равно 25:х + (25 - х) = 25Задача пустяковая, а ведь не сразу это поймёшь!Задача 9-1 из книги Математическая шкатулка [Нагибин88], страница 16:
Найдите простой приём вычислений и воспользуйтесь им для вычисленияЭтот ряд - почти арифметическая прогрессия: в знаменателе первый сомножи¬
тель изменяется от 1 до 9 с приращением 1. Второй сомножитель равен первому,
плюс 1. Произведение этих сомножителей, а значит и дробей, мы легко найдём в
методе Select:Range(1,9).Select(n -> 1.0/(n*(n+1)))Осталось вычислить сумму дробей:Дробный ряд (Range.Select)суммы:
usesSystem;// Нагибин, с.16, Задача 9-1begin// заголовок окна:Console.Title := 'Нагибин, с.16, Задача 9-1';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Дробный ряд');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:var summa:= Range(1,9).Println.Select(n -> 1.0/(n*(n+1))).Println.Sum;// печатаем ответ:Console.WriteLine('Сумма ряда равна {0}', summa);
Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Рисунок показывает, что сумма равна 0,9, и подсказывает нам, что у этой задачи
имеется и красивое некомпьютерное решение:■ Нагибин, с.16, Задача 9-1□XДробный ряд
1234567890.5 0.166666666666667 0.0833333333333333 0.05 0.0333333333333333 0.0238095238095238 0.0178571428571429 0.0138888888888889 0.01
11111111111111
Сумма ряда равна 0,9
Ещё один дробный ряд (Range.Select)Задача 9-2 из книги Математическая шкатулка [Нагибин88], страница 16:Найдите простой приём вычислений и воспользуйтесь им для вычисления
суммы:Задача очень похожа на предыдущую, поэтому нужно исправить только аргу¬
менты в методе Range:і Range(10,99)Вся программа полностью:System;// Нагибин, с.16, Задача 9-2begin// заголовок окна:Console.Title := 'Нагибин, с.16, Задача 9-2';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Ещё один дробный ряд');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:var summa:= Range(10,99).Println98-S9 J 99 100uses.Select(n -> 1.0/(n*(n+1))).Println.Sum;
// печатаем ответ:Console.WriteLine('Сумма ряда равна {0}', summa);
Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end..■ Нагибин, с.1 б^, Задача 9-2—□XЕщё один дробный рядЛСумма ряда равна 0,09VТрёхзначное число 2 (Step.Select)Задача 597 из книги Математиче¬
ская шкатулка [Нагибин88], стра¬
ницы 97-98:Сколько слагаемых суммы 1 + 2 +3 + 4 + 5 + ... надо взять, чтобы по¬
лучить трёхзначное число, состо¬
ящее из одинаковых цифр?Числовой ряд представляет собой арифметическую прогрессию, сумму которой
мы умеем находить:I// сумма арифметической прогрессии:var summa: integer -> integer := n -> (1 + n) * n div 2;Нужно только не пропустить момент, когда эта сумма превратится в трёхзначное
число с одинаковыми цифрами. Искать заданное число можно в бесконечной по¬
следовательности, генерируемой методом Step, начиная с единицы:ШШШ.БПЛСТ ЭЛ№А РОССПП■ гН 5922398
1.Step()В методе Select мы находим сумму n первых элементов последовательности:
і Select(n -> (n,summa(n)))Но нам нужно запомнить и число элементов n, поэтому мы создаём кортеж: число
элементов - их сумма. В этом кортеже число элементов имеет индекс 0, а сумма- индекс 1.Метод SkipWhile пропускает все суммы, которые меньше 100:
і SkipWhile(n -> n[1] < 100)Нам также нужны функции для выделения цифр из трёхзначного числа:// число сотен:var s: integer -> integer := x -> x div 100;// число десятков:var d: integer -> integer := x -> x div 10 mod 10;// число единиц:var e: integer -> integer := x -> x mod 10;Метод Where пропускает дальше только суммы с одинаковыми цифрами:Where(n -> (e(n[1]) = d(n[1])) and (e(n[1]) = s(n[1])))Так как интересует первое число, удовлетворяющее условиям задачи, то мы вы¬
бираем первый элемент отфильтрованной последовательности:ElementAt(0)
Как вы помните, это кортеж из двух элементов. Элемент с индексом 0 - это число
слагаемых, а элемент с индексом 1 - сумма:// печатаем ответ:Console.WniteLine('Число слагаемых равно {0}', nes[0]);
Console.WniteLine('Трёхзначное число равно {0}', nes[1]);Вся программа целиком:usesSystem;// Нагибин, с.97-98, Задача 597begin// заголовок окна:Console.Title := 'Нагибин, с.97-98, Задача 597';
Console.WniteLine('');Console.FonegnoundColon := ConsoleColor.Red;
Console.WniteLine('Трёхзначное число');
Console.FonegnoundColon := ConsoleColor.Green;
Console.WriteLine();// число сотен:var s: integen -> integen := x -> x div 100;// число десятков:var d: integen -> integen := x -> x div 10 mod 10;// число единиц:var e: integen -> integen := x -> x mod 10;// сумма арифметической прогрессии:var summa: integen -> integen := n -> (1 + n) * n div 2;// решаем задачу:
var nes := 1.Step().Select(n -> (n,summa(n))).SkipWhile(n -> n[1] < 100).Where(n -> (e(n[1]) = d(n[1])) and
(e(n[1]) = s(n[1])))
.ElementAt(0);// печатаем ответ:
Console.WriteLine('Число слагаемых равно {0}', res[0]);
Console.WriteLine('Трёхзначное число равно {0}', res[1]);Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Задача имеет единственное решение:■ Нагибин, с.97-98. Задача 597—□XТрёхзначное числолЧисло слагаемых равно 36
Трёхзначное число равно 666VНаименьшее число (Step.Select)I Найдите наименьшее число, которое начинается с 1993 и
делится на все числа от 1 до 9.Понятно, что задача, скорее, на сообразительность, чем пере¬
борная, но именно с помощью перебора её очень просто ре¬
шить, что мы и сделаем в этом проекте.Будем считать, что переменная num равна началу числа:
і var num:= 1993;Легко заметить, что число 19930 на 9 не делится, поэтому сзади будем пристав¬
лять числа, начиная с 1.
Если начало числа незыблемо и непоколебимо, то окончание числа изменяется
от нуля и ... пока мы не найдём решения задачи. Эта неопределённость диктует
нам применение бесконечной последовательности для поиска заданного числа:і var res:= 1.Step()Элементы последовательности мы не можем просто приставить в хвост к числу
1993 - сначала его следует умножить на 10, если элемент имеет значение меньше
10, на 100, если 10..99, и так далее.Показатель степени равен Trunc(Log10(n)) + 1, поэтому в методе Select мы легко
найдём очередное число, которое начинается с 1993:Select(n -> Trunc(num * Power(10, Trunc(Log10(n)) + 1) + n))Чтобы проверить его делимость на ряд чисел 1..9, достаточно привлечь к испыта¬
ниям только числа 5, 7, 8 и 9, остальные удовлетворятся автоматически:.Where(n -> n mod 5 = 0).Where(n -> n mod 7 = 0).Where(n -> n mod 8 = 0).Where(n -> n mod 9 = 0)Как только мы обнаружим первое число, удовлетворяющее условию задачи,
сразу печатаем ответ на экране и завершаем бесконечную последовательность:.Take(1)
.Println;Наименьшее число
найдено:■ Савин, Задач-а 1.35—□XНаименьшее число
1993320ЛНаименьшее число = 1993320
Частное = 791VВот вся программа целиком:
usesSystem;// Савин 1-35begin// заголовок окна:Console.Title := 'Савин. Задача 1.35';
Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Наименьшее число');
Console.ForegroundColor := ConsoleColor.Green;var num:= 1993;
var res:= 1.Step().Select(n -> Trunc(num * Power(10,Trunc(Log10(n)) + 1) + n)).Where(n -> n mod 5 = 0).Where(n -> n mod 7 = 0).Where(n -> n mod 8 = 0).Where(n -> n mod 9 = 0)
.Take(1).Println;Println;Console.WriteLine('Наименьшее число = ' + res.First);
Console.WriteLine('Частное = ' + res.First div 5 div 7Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.При известном любопытстве можно найти ещё несколько решений этой задачи,
если не требовать, чтобы числа были наименьшими из возможных (рис. сверху).Чем хороши компьютерные программы, так это тем, что с их помощью можно за¬
просто перерешать множество подобных задач. Например, исправив значение
единственной переменной num, мы тут же получим ответ на задачу для 2017 года
(рис. снизу).div 8 div 9);
■ Савин. Задача 1.35 — □ XНаименьшее число1993320 19933200 19935720 19938240 199311840 199314360 1993168
80 199319400 199321920 199324440Наименьшее
Частное =
Наименьшее
Частное =
Наименьшее
Частное =
Наименьшее
Частное =
Наименьшее
Частное =
Наименьшее
Частное =
Наименьшее
Частное =
Наименьшее
Частное =
Наименьшее
Частное =
Наименьшее
Частное =7909279093790947909Б79096790971993320199332001993572019938240199311840199314360199316880199319400199321920199324440Наименьшее число20172600 20175120 20177640 201710880 201713400 201715920 201718440
201720960 201723480 201726000Наименьшее
Частное =
Наименьшее
Частное =
Наименьшее
Частное =
Наименьшее
Частное =
Наименьшее
Частное =
Наименьшее
Частное =
Наименьшее
Частное =
Наименьшее
Частное =
Наименьшее
Частное =
Наименьшее
Частное =80044800458004680047800488004980050201726002017512020177640201710880201713400201715920201718440201720960201723480201726000
Enigma 1436: One more step Кубы (Select)В журнале New Scientist #2597 от 31-го марта 2007 года была напечатана задача
Enigma 1436:I invite you to consider the following series of numbers: 1, 3, 7, 13, 21...Then find the following:(a) The six-hundredth member of the series.(b) A member of the series above the first with less than five digits which is a perfect
cube.(c) A member which is a five-digit palindrome which can also be read as a binary num¬
ber.(d) The smaller of the two consecutive members which are 1000 apart.Рассмотрим следующий ряд чисел: 1, 3, 7, 13, 21 ...Найдите:(a) Шестисотый член ряда.(b) Член ряда, который:
больше первого,состоит менее чем из пяти цифр и
является кубом целого числа.(c) Член ряда, который:является палиндромом с пятью цифрами,
может быть прочитан как двоичное число.(d) Два наименьших последовательных члена ряда, разность между которыми
равняется 1000.Прежде чем решать задачу, мы должны научиться вычислять члены ряда.
Обозначим ряд буквой a, тогда первый член равняется:
Обозначим разность между членами ряда буквой d, тогда:d1 = 3-1 = 2d2 = 7-3 = 4d3 = 13-7 = 6d4 = 21-13 = 8dn = 2 * n,где n - номер члена ряда.Теперь мы легко найдём любой член ряда:I an = an-1 + dnДля вывода нерекуррентной формулы воспользуемся методом исчисления ко¬
нечных разностей, который описан в книге Мартина Гарднера [ГМ72], на страни¬
цах 81-95, а также в книге Как решать комбинаторные задачи на компьютере:1 3 7 13 212 4 6 82 2 2Откуда:an = n2 - n + 1
(1)Переходим к решению задачи-Подзадачу (а) мы решаем, просто генерируя 600 элементов заданной последова¬
тельности:Ivar sq := SeqGen(601, i -> i*i - i + 1);//.Pnintln;
var a := sq.Last;Pnintln(a);
■ New Scienti:t#2597Enigma 1436: One more step
359401Для решения подзадачи (b) нам потребуется функция IsCube из модуля OlympUnit,
которая умеет определять, является ли заданное целое число точным кубом. Все
условия подзадачи (b) легко записать в методе Where:Ivar b := sq.Skip(2).Where(i -> (i.NumDigit < 5) and (i.IsCube)).Println;Здесь:• Номер n искомого члена ряда больше единицы:
і Skip(2)• Искомый член a ряда состоит менее чем из пяти цифр:
і i.NumDigit < 5• Искомый член ряда a - точный куб:
і i.IsCube■ New Scientist #2597—□XEnigma 1436: One more stepA359401343V
Подзадачу (с) мы решаем аналогично, но условие прекращения цикла должно
включать проверки, что очередной член ряда:• пятизначное число• палиндром• состоит только из цифр 0 и 1:var c := sq.Where(i -> (i.NumDigit = 5) and (i.IsPalindrome));
var c2 := c.Select(i -> new string(i.ToString.ToCharArray)).Where(s -> s.IndexOfAny(new char[]('2', '3', '4', '5', '6', '7', '8', '9' )) = -1)
.Println;Здесь:• Искомый член a ряда состоит из пяти цифр:
і i.NumDigit = 5• Искомый член ряда a - палиндром:• Искомый член ряда состоит из цифр 0 и 1:i.IsPalindromevar c2 := c.Select(i -> new string(i.ToString.ToCharArray)).Where(s -> s.IndexOfAny(new char[]('2', '3', '4', '5', '6', '7', '8', '9' )) = -1)С помощью метода IndexOfAny мы легко определим, имеются ли в заданном
числе (точнее - в строке, полученной из этого числа) «запрещённые» цифры.ІНа самом деле многие проверки в данном случае излишни; тот же самый ответ
мы получили бы и с гораздо меньшими усилиями.Все условия соблюдены, запускаем программу - и находим решение подзадачи“О
* N ew Sci enti st #2597Enigma 1436: One more step35940134310101И последнюю подзадачу мы решаем без труда:for var i := 1 to sq.Count-2 do
beginif (sq.ElementAt(i+1) - sq.ElementAt(i) = 1000) then
beginPrintln(i, i + 1, sq.ElementAt(i), sq.ElementAt(i + 1));
end;
end;Если мы начнём с первого члена ряда (n=1), то нам нужно найти разность между
ним и следующим членом ряда, номер которого на единицу больше. Если эта раз¬
ность не равняется 1000, то переходим ко второму члену ряда и находим разность
между ним и третьим членом ряда. Продолжаем так до тех пор, пока не получим
заданную разность:■ New Scientist #2597 — □ XEnigma 1436: One more step35940134310101500 501 249501 250501V
Метод SelectManyМетод SelectMany возвращает последовательность, составленную из отдельных
подпоследовательностей, возвращаемых функцией selector:function SelectMany<Res>(selector: T->sequence of Res): sequence of Res;Пусть у нас имеется стихотворение Лермонтова Парус, представленное в виде
массива строк text:var text : array of string :=('Белеет парус одинокой','В тумане моря голубом.','- Что ищет он в стране далекой?','Что кинул он в краю родном?','Играют волны, ветер свищет,','И мачта гнется и скрыпит;','Увы! - он счастия не ищет','И не от счастия бежит! -','Под ним струя светлей лазури,','Над ним луч солнца золотой: -','А он, мятежный, просит бури,','Как будто в бурях есть покой!');Мы хотим получить последовательность всех слов в этом стихотворении.Метод SelectMany последовательно получает строки массива text- Функция selec¬
tor разбивает их на отдельные слова обычным способом - методом Split класса
String - и возвращает последовательность отдельных слов строки. Все эти после¬
довательности затем объединяются в единую последовательность слов, которую
и возвращает метод SelectMany:Ivar words := text.SelectMany(s -> s.Split().Select(s2 -> '<' + s2 + '>'));
■ Метод SelectManyМетод SelectManу<Белеет> <парус> <одинокой> <В> <тумане> <моря> <голубом.> <-> <Что> <
ищет> <он> <в> <стране> <далекой?> <Что> <кинул> <он> <в> <краю> <родн
ом?> <Играют> <волны,> <ветер> освищет,> <И> <мачта> <гнется> <скр
ыпит;> <Увы!> <он> <счастия> <не> <ищет> <И> <не> <от> <счастия> <
бежит!> <-> <Под> <ним> <струя> <светлей> глазури,> <Над> <ним> <луч>
<солнца> <золотой:> <А> <он,> <мятежный,> <просит> <бури,> <Как> <
будто> <в> <бурях> <есть> <покой!>ЛVМетод Select нужен только для выделения отдельных слов. Можно обойтись и
него:Ivar words := text.SelectMany(s -> s.Split());Метод SelectMany — □ XМетод SelectManуБелеет парус одинокой В тумане моря голубом. - Что ищет он в стра
не далекой? Что кинул он в краю родном? Играют волны, ветер свище
т, И мачта гнется и скрыпит; Увы! - он счастия не ищет И не от сч
астия бежит! - Под ним струя светлей лазури, Над ним луч солнца з
олотой: - А он, мятежный, просит бури, Как будто в бурях есть покВся программа целиком:uses System;begin//заголовок окна:Console.Title := 'Метод SelectMany';
Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Метод SelectMany');
Console.ForegroundColor := ConsoleColor.Green;
Println;
var text : array of stning :=('Белеет парус одинокой','В тумане моря голубом.','- Что ищет он в стране далекой?','Что кинул он в краю родном?','Играют волны, ветер свищет,','И мачта гнется и скрыпит;','Увы! - он счастия не ищет','И не от счастия бежит! -','Под ним струя светлей лазури,','Над ним луч солнца золотой: -','А он, мятежный, просит бури,','Как будто в бурях есть покой!');var wonds := text.SelectMany(s -> s.Split().Select(s2 -> '<' + s2 + '>'));wonds.Pnintln;Pnintln;Console.FonegnoundColon := ConsoleColon.Red;end.Во втором методе SelectMany функция selector дополнительно получает индекс
каждого элемента исходной последовательности:function SelectMany<Res>(selecton: (T,integen)->sequence of Res):sequence of Res;В третьем методе SelectMany дополнительно вызывается функция resultSelector
для каждого элемента выходной последовательности:function SelectMany<Coll,Res>(collSelecton:(T,integen)->sequence of Coll;
nesultSelecton: (T,Coll)->Res):
sequence of Res;
В четвёртом методе SelectMany функция collectionSelector дополнительно полу¬
чает индекс каждого элемента исходной последовательности:function SelectMany<Coll,Res>(collSelector: T->sequence of Coll;resultSelector: (T,Coll)->Res):
sequence of Res;Методы последовательностей хорошо заменяют обычные циклы, но вложенные
циклы даются им с трудом. В следующем проекте мы привлечём к этому метод
SelectMany.Трёхзначное число 2 (Range.SelectMany)Задача 585 из книги Математическая шкатулка [Нагибин88]:Если первую цифру трёхзначного числа увеличить на n, а вторую и третью цифру
уменьшить на n, то получится число в n раз больше исходного.Найдите n и исходное число.В первую очередь нам нужны функции для извлечения цифр из трёхзначного
числа:// первая цифра:var n1: integer -> integer := x -> x div 100;// вторая цифра:var n2: integer -> integer := x -> x div 10 mod 10;// третья цифра:var n3: integer -> integer := x -> x mod 10;Тут всё просто.Ещё проще получить последовательность трёхзначных чисел:”)
I Range(100,999)Трёхзначное - это первое из искомых чисел. Но есть ещё и второе. В задаче оно
обозначено буквой п. Оно однозначное.Вторую последовательность получить сложнее, чем первую. Здесь нам необхо¬
дим метод SelectMany. Он позволяет к каждому числу первой последовательно¬
сти добавить число из второй последовательности:і Range(1,9)В итоге мы получаем все пары чисел: трёхзначное число - однозначное число. Но
они нужны нам не сами по себе, а как аргументы в функциях п1, п2, п3. Мы могли
бы все вычисления провести и в методе SelectMany, но тогда нужные нам числа
не удалось бы распечатать. Поэтому метод SelectMany будет создавать последо¬
вательность из пар чисел. Проще всего сохранить пары чисел в кортеже:Range(100,999).SelectMany(y -> Range(1,9), (x,y) -> (x,y)).Println;Мы распечатали последовательность кортежей, и на рисунке хорошо видны все
пары чисел от (100,1) до (999,9) (рис. на следующей странице).Полдела сделано!В каждой паре первое (трёхзначное) число имеет индекс 0, а второе (однознач¬
ное) - индекс 1. Теперь в методе Where мы можем выбрать такую пару чисел (или
несколько пар), которые удовлетворяют условиям задачи:Where(n -> (n1(n[0]) + n[1]) * 100 +
(n2(n[0]) - n[1]) * 10 +
(n3(n[0]) - n[1]) = n[0] * n[1])
» Нагибин 5£5□X
Распечатав отфильтрованную последовательность, мы убеждаемся, что задача
имеет единственное решение. Поскольку нам нужно только оно, то мы берём
первый и единственный элемент последовательности:і ElementAt(0)Так как это кортеж, то при печати ответа мы извлекаем из него оба числа по их
индексам.Задача решена:usesSystem;// Нагибин 585begin// заголовок окна:Console.Title := 'Нагибин 585';Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Трёхзначное число 2');Console.ForegroundColor := ConsoleColor.Green;Console.WriteLine();// решаем задачу -->// первая цифра:var n1: integer -> integer := x -> x div 100;// вторая цифра:var n2: integer -> integer := x -> x div 10 mod 10;// третья цифра:var n3: integer -> integer := x -> x mod 10;var res:= Range(100,999).SelectMany(y -> Range(1,9), (x,y) -> (x,y))//.Println
.Where(n -> (n1(n[0]) + n[1]) * 100 +(n2(n[0]) - n[1]) * 10 +(n3(n[0]) - n[1]) = n[0] * n[1]).Println
.ElementAt(0);// печатаем ответ:Console.WriteLine();Console.WriteLine('Число = {0} n = {1}', res[0], res[1]);
Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.А вот и ответ на эту замысловатую задачу:■ Нагибин 5В5—□XТрёхзначное число 2л(178,2)Число = 178 п = 2VМетод ZipМетод Zip возвращает последовательность, объединяющую 2 заданные последо¬
вательности. К каждой паре элементов обеих последовательностей с одинако¬
выми индексами применяется функция resultSelector:function Zip<TSecond,Res>(second: sequence of TSecond;resultSelector: (T,TSecond)->Res):
sequence of Res;Если одна последовательность короче другой, то в результирующей последова¬
тельности будет столько же элементов, сколько их имеется в более короткой по¬
следовательности, так как для остальных элементов более длинной последова¬
тельности не найдётся пары в короткой последовательности.Создаём 2 последовательности по 10 натуральных чисел:
var sq1 := Range(1,10).Println(' ');var sq2 := Range(l1,20).Println;И в методе Zip попарно складываем их:
і sq1.Zip(sq2, (x,y) -> x + y).Println;ill Метод Zip — □ XМетодZip12В 4567891011121В 14151617IS1920121416 IS202224262330Плюс-минус (Range.Zip)Задача 7-2 из книги Математическая шка¬
тулка [Нагибин88], страница 15:ПЛЮС■ Как быстро вычислить: <^аВщкI 99 - 97 + 95 - 93 + 91 - 89 + ... + 7 - 5 + 3 - 1?Конечно, быстро вычислить можно на компьютере!По сравнению с обычной арифметической прогрессией (здесь она убывающая)
знак членов ряда чередуется: плюс-минус-плюс-минус...Последовательность легко получить от метода Range:
і Range(99, 1, -2)
Здесь мы учитываем, что абсолютная величина элементов последовательности
уменьшается на 2.Более сложно решить проблему с чередующимися знаками.Заметим, что все полученные числа последовательности нужно умножать попе¬
ременно на числа 1 и -1. Метод Range возвращает числа от 1 до 50. Нам нужно,
чтобы нечётные числа равнялись 1, а чётные - -1. В методе Select мы без труда
справляемся с этой задачей.| Range(1,50).Select(x -> x.IsOdd ? 1 : -1)Мы получили 2 последовательности:Ivar seq1 := Range(99, 1, -2);var seq2 := Range(1,50).Select(x -> x.IsOdd ? 1 : -1);В первой хранятся числа, а во второй - знаки. Чтобы получить заданную последо¬
вательность, нужно эти последовательности поэлементно перемножить. А это
умеет делать метод Zip:і seq1.Zip(seq2, (x,y)->x*y)Он получает по одному элементу из каждой последовательности и перемножает
их.Осталось найти сумму третьей последовательности - и задача решена:usesSystem;// Нагибин, с.15, Задача 7-2begin
// заголовок окна:Console.Title := 'Нагибин, с.15, Задача 7-2';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Плюс-минус');Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:var seq1 := Range(99, 1, -2).Println;var seq2 := Range(1,50).Select(x -> x.IsOdd ? 1 : -1).Println;
var summa := seq1.Zip(seq2, (x,y)->x*y).Println.Sum;// печатаем ответ:Console.WriteLine();Console.WriteLine('Сумма ряда равна {0}', summa);
Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Сумма ряда найдена:■ Нагибин, с.15. Задача 7-2 — □ XПлюс-минус99 97 9Б 9В 91 S9 S7 S5 S3 S1 79 77 7Б 73 71 69 67 6Б 63 61 Б9 Б7 ББ БЗ 51
49 47 45 43 41 39 37 35 33 31 29 27 25 23 21 19 17 15 13 11 9 7 Б 3 1
1-11-11-11-11-11-11-11-11-11-11-11-11-11-11-1
1-11-11-11-11-11-11-11-11-11-199 -97 95 -93 91 -S9 S7 -S5 S3 -S1 79 -77 75 -73 71 -69 67 -65 63 -61 59 -5
7 55 -53 51 -49 47 -45 43 -41 39 -37 35 -33 31 -29 27 -25 23 -21 19 -17 15
-13 11 -9 7-5 3 -1Сумма ряда равна 50
Минус-плюс (Range.Zip)Задача 482-8 из книги Математическая шкатулка [Наги-
бин88], страница 88:I Найдите простой приём вычисления:1002 - 992 + 982 - 972 + 962 - 952 + ... + 42 - 32 + 22 - 12Здесь мы видим практически тот же ряд, что и в предыдущей задаче, только все
числа возводятся в квадрат, что легко сделать в методе Select:і var seq1 := Range(100, 1, -1).Select(x -> x*x);Дальше задача решается без труда, но довольно интересно, что ответ на неё пол¬
ностью совпадает с тем ответом, который мы получили, решая задачу Гаусса:usesSystem;// Нагибин, с.88, Задача 482-8begin// заголовок окна:Console.Title := 'Нагибин, с.88, Задача 482-8';Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Минус-плюс');Console.ForegroundColor := ConsoleColor.Green;Console.WriteLine();// решаем задачу:var seq1 := Range(100, 1, -1).Select(x -> x*x);var seq2 := Range(1,100).Select(x -> x.IsOdd ? 1 : -1);var summa := seq1.Zip(seq2, (x,y)->x*y).Sum;// печатаем ответ:
Console.WriteLine('CyMMa ряда равна {0}', summa);
Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.■ Нагибин, с.ЗЗ, Задача 4S2-3 — □ XМетод DistinctМетод Distinct возвращает исходную последовательность, из которой удалены по¬
вторяющиеся элементы (то есть все оставшиеся элементы - уникальны):і function Distinct(): sequence of T;Создаём последовательность целых чисел, а затем напечатаем её без повторов
элементов:var sq:= SeqRandom(20,1,10).ToArray.Println;
sq.Distinct().Println;Можно дополнительно
отсортировать эле¬
менты:ЁІ Метод Distinct — □XЛМетод Distinct3 94125 10 51 10 34 10 71S39923 94125 10 7012345709 10VМинус-плюсСумма ряда равна 5050
sq.Distinct().Sorted.Println;Метод Distinct поможет нам найти все разнобуквицы русского языка:var lstStrAll := ReadAllLines('OSH-W97.txt');
var razn := lstStrAll.Where(s -> s.Length = s.Distinct().Count())
.OrderBy(s -> s.Length).ThenBy(s -> s).Select(s -> s + ' - ' + s.Length + NewLine)
.Println;В разнобуквицах буквы не
повторяются, поэтому
длина исходного слова
должна быть равна длине
слова, полученного после
удаления из него повторя¬
ющихся букв:ПОЗДРАВИТЕЛЬ - 12
ПРОТЁСЫВАНИЕ - 12
ПРОЧЁСЫВАНИЕ - 12
ПУЧЕГЛАЗОСТЬ - 12
ПЫЛЕВИДНОСТЬ - 12
ПЯТИРУБЛЁВКА - 12
РЕБЯЧЛИВОСТЬ - 12
РУКОДЕЛЬНИЦА - 12
СЛАДКОЗВУЧИЕ - 12
СТЕКЛОГРАФИЯ - 12
ТРЁХДЮЙМОВКА - 12
УПАДНИЧЕСТВО - 12
ФЕХТОВАЛЬЩИК - 12
ЦАРЕУБИЙСТВО - 12
ШКУРНИЧЕСТВО - 12
ЭПИКУРЕЙСТВО - 12
ЯЙЦЕВИДНОСТЬ - 12
БАРЫШНИЧЕСТВО - 13
ГРУШЕВИДНОСТЬ - 13
ДРУЖЕЛЮБНОСТЬ - 13
НЕВЫРАЗИМОСТЬ - 13
НЕУДАЧЛИВОСТЬ - 13
ОПРЫСКИВАТЕЛЬ - 13
ПРИВЫКАЕМОСТЬ - 13
УПРАВЛЯЕМОСТЬ - 13
ДЕСЯТИРУБЛЁВКА - 14
ЗВУКОСНИМАТЕЛЬ - 14
РАЗГИЛЬДЯЙСТВО - 14
ЧЕТЫРЁХУГОЛЬНИК - 1БV
Второй метод Distinct для сравнения элементов последовательности использует
заданный компаратор comparer типа IEqualityComparer<T>:і function Distinct(comparer: IEqualityComparer<T>): sequence of T;Метод UnionМетод Union объединяет две последовательности. При этом в выходной после¬
довательности остаются элементы без повторов. Для сравнения элементов ис¬
пользуется компаратор проверки на равенство по умолчанию:і function Union(second: sequence of T): sequence of T;Создаём две последовательности чисел, которые для удобства отсортируем, и
находим их объединение:var sq1:= SeqRandom(20,1,10).ToArray.OrderBy(n -> n).Println;
var sq2:= SeqRandom(20,6,16).ToArray.OrderBy(n -> n).Println;Println;var union := sq1.Union(sq2).Println;На рисунке пока¬
зана объединён¬
ная последова¬
тельность. Из неё
исчезли повторя¬
ющиеся элементы
последовательно¬
сти sql, после ко¬
торых следуют элементы последовательности sq2, не совпадающие с элементами
первой последовательности.■ Метод UnionX□1Метод UnionА111124455555566778 8 106 S 10 10 10 11 11 12 13 13 13 14
1 2 4 5 6 7 S 10 11 12 13 14 1514 14 14 1Б 15 1Б 15 15V
Второй метод Union также объединяет две последовательности, но для сравнения
элементов использует компаратор comparer:I function Union(second: sequence of T; comparer: IEqualityComparer<T>):
sequence of T;Метод IntersectМетод Intersect возвращает пересечение двух последовательностей, то есть по¬
следовательность, состоящую из элементов, имеющихся в обеих последователь¬
ностях. Для сравнения элементов используется компаратор проверки на равен¬
ство по умолчанию:і function Intersect(second: sequence of T): sequence of T;Найдём пересечение числовых последовательностей sq1 и sq2:var sq1:= SeqRandom(20,1,10).ToArray.OrderBy(n -> n).Println;
var sq2:= SeqRandom(20,6,16).ToArray.OrderBy(n -> n).Println;Println;var inter := sq1.Intersect(sq2).Println;В последовательности inter оказались элементы, которые содержатся в обеих по-
следовател ьностях:[і] Метод Intersect — □ XМетод Intersect134455566667778999 10 10
6 7 7 7 8 8 9 9 9 9 10 11 11 11 14 14 16 16 16 166 7 8 9 10V
Во втором методе Intersect для сравнения элементов используется заданный ком¬
паратор comparer:function Intersect(second: sequence of T;comparer: IEqualityComparer<T>): sequence of T;Метод ExceptМетод Except возвращает разность двух последовательностей, то есть последо¬
вательность, состоящую из элементов первой последовательности за исключе¬
нием тех элементов, которые содержатся и во второй последовательности. Для
сравнения элементов используется компаратор проверки на равенство по умол¬
чанию:function Except(second: sequence of T): sequence of T;Найдём разность числовых последовательностей sq1 и sq2:var sq1:= SeqRandom(20,1,10).ToArray.OrderBy(n -> n).Println;
var sq2:= SeqRandom(20,6,16).ToArray.OrderBy(n -> n).Println;Println;var exc := sq1.Except(sq2).Println;В последовательности* Метод Except- □ Xexc оказались элементыЛпервой последователь¬Метод Exceptности, которые не со¬12355566677889999 10 10 10держатся во второй по¬6777888910 10 11 11 12 12 12 13 14 15 15 16следовательности:12 3 5V
Во втором методе Except для сравнения элементов используется заданный ком¬
паратор comparer:function Except(second: sequence of T; comparer: IEqualityComparer<T>):
sequence of t;Метод ContainsМетод Contains возвращает true, если последовательность содержит элемент с за¬
данным значением value, используя компаратор проверки на равенство по умол¬
чанию.:function Contains(value: T): boolean;Создаём последовательность натуральных чисел 1..20:
і var sq := Range(1, 20).Println;И узнаём, есть ли среди них число 12:if sq.Contains(12) thenPrintln('Такой элемент есть')
elsePrintln('Такого элемента нет');Рисунок подтверждает,
что такое число содер¬
жится в последователь¬
ности:■ Метод ContainsX□1ЛМетод Contains1 2 В 4 Б 6 7 S 9 10 11 12 1314 1Б 16 17 18 19 20Такой элемент естьV
Изменим условие:if sq.Contains(22) thenРгі^1п('Такой элемент есть')
elsePrintln('Такого элемента нет');А числа 22 в последовательности нет:■ Метод ContainsX□1ЛМетод Contains123456789 1011 12 13 14 15 16 17 18 19 20Такого элемента нетVВторой метод Contains использует для сравнения элементов заданный компара¬
тор comparer:function Contains(va1ue: T; comparer: IEqua1ityComparer<T>): boolean;Методы Single и SingleOrDefaultМетод Single возвращает первый и единственный элемент последовательности:
function Single(): T;Если в исходной коллекции больше одного элемента или вообще нет элементов,
генерируется исключение.Создаём последовательность с единственным элементом:
var res := Range(1, 1)
.Single;И печатаем его:
I Println(res);* Методы Single и SingleOrDefault—□XМетоды Single и SingleOrDefaultAVДобавим в последовательность второй элемент:var res := Range(1, 2)
.Single;При запуске программы получаем сообщение:2122var res := Range(lj 2).Single;іисок ошибокСтрока Описание! 1 21 Оши&ка времени выполнения: Последовательность содержит Еюлее одного элементаЕсли последовательность не содержит элементов, то также возникает ошибка:var res := Range(1, -2)
.Single;
21I var Г0522.Single;іисок ошибокСтрока Описание1 21 Ошибка времени выполнения: Последовательность не содержат элементовВторой метод Single проверяет элементы исходной коллекции. Если единствен¬
ный элемент удовлетворяет условию, то он будет возвращён, в противном случае
возникает исключение:і function Single(predicate: T->boolean): T;Создаём последовательность из 10 натуральных чисел и проверим, имеется ли в
ней единственная семёрка:Ivar res := Range(1, 10).Single(n -> n = 7);Println(res);Всё верно:■ Методы Single и SingleOrDefault—□XМетоды Single и SingleOnDefaultЛVИзменяем условие:var res := Range(1, 10).Single(n -> n <= 7);
Println(res);
Оно вызывает ошибку:241 var res := Range(l,10)25.Single(n* n
IIVсЛ1<Список ошибокСтрока Описание>0 1 24 Ошибка времени выполнения: Последовательность содержит более одного соответствующего элементаЧтобы избежать ошибок в работе программы, используйте метод SingleOrDefault.
Он действует аналогично первому методу Single. Но если исходная коллекция пу¬
стая, то возвращается значение по умолчанию для типа T. Если последователь¬
ность содержит более одного элемента, генерируется исключение:і function SingleOrDefault(): T;Создаём пустую последовательность и печатаем её первый элемент:Ivar res := Range(1, -1).SingleOrDefault;Println(res);Так как последовательность не содержит элементов, то метод SingleOrDefault воз¬
вращает значение по умолчанию для типа integer, то есть 0:* Методы Single и SingleOrDefault—□XAМетоды Single и SingleOrDefaultVНо если последовательность содержит более одного элемента, возникает
ошибка:Ivar res := Range(1, 2).SingleOrDefault;Println(res);
231 var res := Range(l, 2)29.SingleOrDefault■<Список ошибокСтрока Описание1 28 Ошибка времени выполнения: Последовательность содержит более одного элементаВторой метод SingleOrDefault также возвращает единственный элемент или зна¬
чение по умолчанию, если исходная коллекция пуста или в ней не нашлось ни
одного элемента, удовлетворяющего условию:і function SingleOrDefault(predicate: T->boolean): T;var res := Range(1, 10).SingleOrDefault(n -> n > 10);Println(res);■ Методы Single и SingleOrDefault — □ XМетоды Single и SingleOrDefaultЕсли совпадение не единственное, возникает исключение:2329ЗѲ31323334var res := Range(lJ 2).SingleOrDefault;
PrintIn(res);}var res := Range(l, 10).SingleOrDefault(n -> n > 7);
Println(res);<Список ошибокl>*1Строка32ОписаниеОшибка времени выполнения: Последовательность содержит более одного соответствующего элемента
Метод AnyМетод Any возвращает true, если последовательность содержит элементы (не
стая):і function Any(): boolean;Создаём последовательность из 20 элементов и проверяем её методом Any:var sq := Range(1, 20).Println;if sq.Any() thenPrintln('Последовательность содержит элементы')
elsePrintln('Последовательность пустая');Создаём пустую последовательность:
і var sq := Range(1, -20).Println;Метод Any «узнал» её:Метод Any1 2 В 4 5 6 7 S 9 10 11 12 IB 14 15 16 17 IS 19 20
Последовательность содержит элементыМетод AnyПоследовательность пустая
Второй метод Any возвращает true, если последовательность содержит хотя бы
один элемент, удовлетворяющий условию predicate:і function Any(predicate: T->boolean): boolean;Проверяем, имеется ли в последовательности sq хотя бы 1 элемент, который
меньше 20:var sq := Range(1, 20).Println;if sq.Any(n -> n < 20) thenPrintln('Последовательность содержит такие элементы')
elsePrintln('Последовательность не содержит таких элементов');Метод Any утверждает
правильно:* Метод Any□XА больше 20?Метод Any1 2 В 4 5 6 7 S 9 10 11 12 1В 14 15 16 17 IS 19 20
Последовательность содержит такие элементыvar sq := Range(1, 20).Println;if sq.Any(n -> n > 20) thenPrintln('Последовательность содержит такие элементы')
elsePrintln('Последовательность не содержит таких элементов');А таких элементов нет:Метод Any1 2 3 4 5 6 7 S 9 10 11 12 13 14 15 16 17 IS 19 20
Последовательность не содержит таких элементов
Метод AllМетод All возвращает true, если все элементы последовательности удовлетво¬
ряют заданному условию predicate:і function All(predicate: T->boolean): boolean;Создаём последовательность натуральных чисел 1..20 и проверяем, все ли эле¬
менты не больше 20:var sq := Range(1, 20).Println;if sq.All(n -> n <= 20) thenPrintln('Bce элементы удовлетворяют условию')
elsePrintln('He все элементы удовлетворяют условию');Метод АН подтверждает,
что все элементы после¬■ 7 Метод АН - □ XЛдовательности удовле¬Метод Allтворяют заданному усло¬1 2 3 4 5 6 7 3 9 10 11 12 13 14 15 16 17 18 19 20вию:Все элементы удовлетворяют условиюVА все ли элементы последовательности не больше 10?if sq.All(n -> n <= 10) thenPrintln('Все элементы удовлетворяют условию')
elsePrintln('Не все элементы удовлетворяют условию');ІіЗ Метод АН - □ XРазумеется, не все:Метод All1 2 3 4 5 6 7 S 9 10 И 12 13 14 15 16 17 18 19 20
Не все элементы удовлетворяют условиюV
Метод DefaultlfEmptyМетод DefaultlfEmpty возвращает исходную коллекцию source, если она не пуста,
или одноэлементную последовательность со значением элемента по умолчанию
в противном случае:і function DefaultIfEmpty(): sequence of T;Создаём пустую последовательность и пропускаем её через метод DefaultlfEmpty:і var sq := Range(1, -20).Он возвращает последова¬
тельность из одного эле¬
мента, значение которого
равно нулю:Второй метод DefaultIfEmpty позволяет задать любое значение defaultValue по
умолчанию для пустой коллекции:і function DefaultIfEmpty(defaultValue: T): sequence of T;Если последовательность пустая, то метод DefaultlfEmpty вернёт последователь¬
ность из одного элемента, значение которого равно -1:і var sq := Range(1, -20).DefaultIfEmpty(-1).Println;■ Метод DefaultlfEmpty — □ XDefaultIfEmpty.Println;■ Метод DefaultlfEmpty—□XМетод DefaultlfEmptyЛVМетод DefaultlfEmpty
-1
Метод SequenceEqualМетод SequenceEqual возвращает true, если две последовательности совпадают
по числу элементов, по их значению и порядку. При сравнении элементов после¬
довательности используется компаратор по умолчанию для типа T:function SequenceEqual(second: sequence of T): boolean;Создаём 2 одинаковые последовательности:var sq1 := Range(1, 10).Println;
var sq2 := Range(1, 10).Println;if sq1.SequenceEqual(sq2) thenPrintln('Последовательности совпадают')
elsePrintln('Последовательности не совпадают');Метод SequenceEqual пока¬
зывает, что они совпа¬
дают:Добавим ко второй после¬
довательности ещё 1 эле¬
мент:і var sq2 := (0 + Range(l, 10)).Println;Теперь последовательно¬
сти не совпадают:■ Метод SequenceEqual—□XлМетод SequenceEqual123456789 100 123456789 10Последовательности не совпадаютV■ Метод SequenceEqual—□XМетод SequenceEqualЛ123456789 10
123456789 10
Последовательности совпадаютV
Второй метод SequenceEqual использует для сравнения элементов заданный ком¬
паратор comparer:function SequenceEqual(second: sequence of T;comparer: IEqualityComparer<T>): boolean;Метод OfTypeМетод OfType возвращает последовательность элементов заданного типа Res из
исходной последовательности:function OfType<Res>(): sequence of Res;Этот метод имеет смысл применять к коллекциям, которые могут содержать раз¬
нотипные элементы.Создаём массив элементов разных типов:і var arr := new object[](1,2,3,'один','два','три', 1.0, 2.0, 3.0);И выбираем из них строки:
і arr.OfType&<string>.Println;■ Метод OfType—□XМетод OfTypeАодин два триV
Метод CastМетод Cast действует аналогично методу OfType, но, если в коллекции встретятся
элементы другого типа, возникает исключение:function Cast<Res>(): sequence of Res;Создаём массив вещественных чисел:var arr := new object[](1.0, 2.0, 3.0);
arr.Cast&<real>.Println;■ Метод Cast—□XМетод CastЛ12 3VДобавляем к ним число типа integer:var arr := new object[](1.0, 2.0, 3.0, 1);
arr.Cast&<real>.Println;И получаем сообщение об ошибке:1314var arr := new object[](1.0, 2.0, 3.Q, 1);arr.Cast&< real>.PrintingСписок ошибокСтрока Описание14 Ошибка времени выполнения: Заданное приведение является недопустимым.
Метод ToArrayМетод ToArray возвращает массив элементов типа T, созданный из последова¬
тельности элементов типа T:function ToArray(): array of T;var arr := Range(1, 20)
.ToArray
.Println;ill Метод ToArray — □ XМетод ToAnnay1 2 В 4 5 6 7 S 9 10 11 12 IB 14 15 16 17 IS 19 20Метод ToListМетод ToList возвращает список элементов типа T, созданный из последователь¬
ности того же типа:function ToList(): List<T>;var lst := Range(1, 20)
.ToList
.Println;* Метод ToList□ XМетод ToList1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 IB 19 20
Метод ToDictionaryМетод ToDictionary возвращает словарь, созданный из последовательности. По¬
скольку словарь состоит из пар ключ-значение, а в последовательности имеются
только значения, то с помощью метода keySelector необходимо создать ключ типа
Key. Для сравнения ключей метод ToDictionary использует компаратор проверки
на равенство по умолчанию:і function ToDictionary<Key>(keySelector: T->Key): Dictionary<Key,T>;I Все ключи в словаре должны быть разными!Пусть у нас имеется таблица синусов от 0 до 90 градусов, сохранённая в после¬
довательности sq:// создаём последовательность синусов:
var sq := Range(0, 90).Select(i -> Sin(i * PI / 180.0)).Println;Метод ToDictionaryѲ Ѳ.0174524064372835 0.034899496702501 0.0523359562429438 0.0697564737441253 Ѳ
.0871557427476582 0.104528463267653 0.121869343405147 0.139173100960065 0.1564
34465040231 0.17364817766693 0.190808995376545 0.207911690817759 0.22495105434
3865 0.241921895599668 0.258819045102521 0.275637355816999 0.292371704722737 0
.309016994374947 Ѳ.325568154457157 0.342020143325669 Ѳ.3583679495453 0.3746065
B3415912 0.390731128489274 0.4067366430758 0.422618261740699 0.438371146789077
0.453990499739547 0.469471562785891 0.484809620246337 0.5 0.515038074910054 0
.529919264233205 0.544639Ѳ35Ѳ15Ѳ27 0.5591929Ѳ347Ѳ747 0.573576436351046 0.58778
5252292473 0.601815023152048 0.615661475325658 0.629320391049837 0.64278760968
6539 Ѳ.65605902899Ѳ507 0.669130606358858 0.681998360062498 0.694658370458997 0
.707106781186547 0.719339800338651 0.73135370161917 0.743144825477394 0.754709
580222772 0.766044443118978 0.777145961456971 0.788010753606722 0.798635510047
293 0.809016994374947 0.819152044288992 0.829037572555042 0.838670567945424 Ѳ.
848048096156426 0.857167300702112 0.8660254Ѳ3784439 0.874619707139396 0.882947
592858927 0.891006524188368 0.898794046299167 0.90630778703665 0.9135454576426
01 0.92050485345244 0.927183854566787 0.933580426497202 0.939692620785908 0.94
5518575599317 0.951056516295154 0.956304755963035 0.961261695938319 0.96592582
6289068 0.970295726275996 Ѳ.974370064785235 Ѳ.978147600733806 0.98162718344766
U 0.984807753012208 0.987688340595138 0.99026806874157 0.992546151641322 0.994
521895368273 0.996194698091746 0.997564050259824 0.998629534754574 0.999390827
019096 0.999847695156391 1£
Мы хотим создать словарь, в котором по значению ключа в градусах мы можем
сразу получить соответствующее значение синуса, без повторного его вычисле¬
ния. Вся сложность состоит в том, чтобы по значению синуса вычислить его угол.
Проблема решается «тригонометрически»:// создаём словарь:
var dic := sq.ToDictionary(i ->
Round(ArcSin(i) * 180.0 / PI)).Println;* Метод ToDictionary□X(0,0) (1,0.0174524064372835) (2,0.034899496702501) (3,0.0523359562429438) (4,0
.0697564737441253) (5,0.0871557427476532) (6,0.104523463267653) (7,0.121369343
405147) (3,0.139173100960065) (9,0.156434465040231) (10,0.17364317766693) (11,
0.190808995376545) (12,0.207911690817759) (13,0.224951054343865) (14,0.2419218
95599663) (15,0.253319045102521) (16,0.275637355316999) (17,0.292371704722737)
(13,0.309016994374947) (19,0.325563154457157) (20,0.342020143325669) (21,0.35
83679495453) (22,0.374606593415912) (23,0.390731128489274) (24,0.4067366430758
) (25,0.422613261740699) (26,0.433371146739077) (27,0.453990499739547) (23,0.4
69471562735891) (29,0.484809620246337) (30,0.5) (31,0.515038074910054) (32,0.5
29919264233205) (33,0.544639035015027) (34,0.559192903470747) (35,0.5735764363
51046) (36,0.537735252292473) (37,0.601315023152043) (33,0.615661475325653) (39.0.629320391049337) (40,0.642787609686539) (41,0.656059023990507) (42,0.66913
0606358858) (43,0.681998360062498) (44,0.694658370458997) (45,0.70710678118654
7) (46,0.719339300333651) (47,0.73135370161917) (43,0.743144325477394) (49,0.7
54709580222772) (50,0.766044443113978) (51,0.777145961456971) (52,0.7330107536
06722) (53,0.793635510047293) (54,0.809016994374947) (55,0.819152044233992) (56.0.329037572555042) (57,0.333670567945424) (53,0.343043096156426) (59,0.35716
7300702112) (60,0.866025403784439) (61,0.874619707139396) (62,0.88294759285892
7) (63,0.391006524188368) (64,0.893794046299167) (65,0.90630778703665) (66,0.9
13545457642601) (67,0.92050435345244) (63,0.927133354566737) (69,0.93353042649
7202) (70,0.939692620785908) (71,0.945518575599317) (72,0.951056516295154) (73
,0.956304755963035) (74,0.961261695938319) (75,0.965925826289063) (76,0.970295
726275996) (77,0.974370064735235) (73,0.973147600733306) (79,0.931627133447664
) (80,0.984807753012208) (81,0.987688340595138) (82,0.99026806874157) (83,0.99
2546151641322) (34,0.994521395363273) (35,0.996194693091746) (36,0.99756405025
9324) (37,0.993629534754574) (33,0.999390327019096) (39,0.999347695156391) (90Л)Пользуясь словарём, мы быстро найдём синус любого угла от 0 до 90 градусов:IPrintln;// печатаем таблицу:
for var i := 0 to 90 do
beginConsole.Write(' Угол = ' + i.ToString().PadLeft(2));
Console.WriteLine(' Синус = ' + dic.ElementAt(i).Value);end;Угол=64Синус=0,898794046299167Угол=65Синус=0,90630778703665Угол=66Синус=0,913545457642601Угол=67Синус=0,92050485345244Угол=68Синус=0,927183854566787Угол=69Синус=0,933580426497202Угол=70Синус=0,939692620785908Угол=71Синус=0,945518575599317Угол=72Синус=0,951056516295154Угол=7ВСинус=0,956304755963035Угол=74Синус=0,961261695938319Угол=75Синус=0,965925826289068Угол=76Синус=0,970295726275996Угол=77Синус=0,974370064785235Угол=78Синус=0,978147600733806Угол=79Синус=0,981627183447664Угол=80Синус=0,984807753012208Угол=81Синус=0,987688340595138Угол=82Синус=0,99026806874157Угол=83Синус=0,992546151641322Угол=84Синус=0,994521895368273Угол=85Синус=0,996194698091746Угол=86Синус=0,997564050259824Угол=87Синус=0,998629534754574Угол=88Синус=0,999390827019096Угол=89Синус=0,999847695156391Угол=90Синус=1ill Метод ToDictionary — □ XУгол=0Синус=0Угол=1Синус=00174524064372835Угол=2Синус=0034899496702501Угол=3Синус=00523359562429438Угол=4Синус=00697564737441253Угол=5Синус=00871557427476582Угол=6Синус=0104528463267653Угол=7Синус=0121869343405147Угол=8Синус=0139173100960065Угол=9Синус=0156434465040231Угол=10Синус=017364817766693Угол=11Синус=0190808995376545Угол=12Синус=0207911690817759Угол=13Синус=0224951054343865Угол=14Синус=0241921895599668Угол=15Синус=0258819045102521Угол=16Синус=0275637355816999Угол=17Синус=0292371704722737Угол=18Синус=0309016994374947Угол=19Синус=0325568154457157Угол=20Синус=0342020143325669Угол=21Синус=03583679495453Угол=22Синус=0374606593415912Угол=23Синус=0390731128489274Угол=24Синус=04067366430758Угол=25Синус=0422618261740699Угол=26Синус=0438371146789077IB паскале PascalABC.NET методы ToDictionary и ToLookup возвращают
последовательность типа KeyValuePair!Метод ToDictionary имеет ещё три, более сложные разновидности:function ToDictionary<Key>(keySelector: T->Key;comparer: IEqualityComparer<Key>):
Dictionary<Key,T>;
function ToDictionary<Key,Element>(keySelector: T->Key;elementSelector: T->Element):
Dictionary<Key,Element>;function ToDictionary<Key,Element>(keySelector: T->Key;elementSelector: T->Element;
comparer: IEqualityComparer<Key>):
Dictionary<Key,Element>;I—, w VВ первой из них для сравнения ключей использует заданный компаратор про¬
верки на равенство comparer.Во второй для вычисления значения элементов используется функция elementSel¬
ector.Третья перегрузка используется одновременно и comparer, и elementSelector.Если значение comparer равно null, то для сравнения ключей используется компа¬
ратор проверки на равенство по умолчанию.Метод ToLookupМетод ToLookup возвращает множественный словарь, созданный из последова¬
тельности с помощью метода keySelector:function ToLookup<Key>(keySelector: T->Key):System.Linq.ILookup<Key,T>;Загружаем словарь в строковый массив:var lstStrAll := ReadAllLines('OSH-W97.txt');
Создаём из него множественный словарь lookup, последовательно применяя ме¬
тоды:• Where - чтобы отбросить слова, которые длиннее 6 букв• OrderBy - чтобы отсортировать слова по длине• ToLookup - чтобы создать множественный словарь, в котором роль ключа иг¬
рает длина слов:var lookup := lstStrAll.Where(s -> s.Length < 6).OrderBy(s -> s.Length).ToLookup(s -> s.Length).Println;Таким образом, в словаре lookup для каждой длины слов создана последователь¬
ность строк. Получить эту последовательность можно по ключу. Например, если
мы хотим напечатать список слов из 5 букв, то сначала получаем коллекцию слов,
а затем печатаем её на экране (рис. на следующей странице):Ivar words := lookup.ElementAt(3);Println(words);Метод ToLookup имеет ещё три разновидности. Они в точности повторяют разно¬
видности метода ToDictionary:function ToLookup<Key>(keySelector: T->Key;comparer:IEqualityComparer<Key>):System.Linq.ILookup<Key,T>;function ToLookup<Key,Element>(keySelector: T->Key;elementSelector: T->Element):
System.Linq.ILookup<Key,Element>;^282 ^
function ToLookup<Key,Element>(keySelector: T->Key;elementSelector: T->Element;
comparer: IEqualityComparer<Key>):
System.Linq.ILookup<Key,Element>;• Метод To LookupМетод ToLookup□X[АД,АЗ,АР,AC,ГО,ЁЖ,ИЛ,OR,OP,ПА,РЭ,СУ,УЖ,УН,УС,ЩИ,ЮГ,ЮЗ,ЮР,ЮС,ЯД,ЯК,ЯЛ,ЯН,ЯР]
[АЗУ,АКР,АКТ,АУЛ,АУТ,АХИ,БАЙ,БАК,БАЛ,БАР,БАС,БЕГ,БЕЙ,БЕК,БЕС,БИТ,БИЧ,БОА,БО
Б,БОГ,БОЙ,БОК,БОН,БОР,БОТ,БРА,БУЙ,БУК,БУН,БУР,БУТ,БУФ,БЫК,БЫТ,ВАЛ,ВАР,ВЕК,BE
С,ВИД,ВОЗ,ВОЙ,ВОЛ,ВОР,ВУЗ,ВЫЯ,ВЯЗ,ГАД,ГАЗ,ГАН,ГЕН,ГИД,ГИК,ГОД,ГОЛ,ГОН,ГОТ,ГУ
к, ГУЖ, ГУЛ, ДАР, ДЕД, ДНО, ДОГ, ДОЖ, ДОК, ДОЛ, ДОН, ДОТ, ДУБ, ДУХ, ДУШ, ДЫН, ЕДА, ЕЛЬ, ЁРШ, ЖА
Р,ЖИД,ЖИН,ЖИР,ЖОН,ЖОР,ЖОХ,ЖУК,ЖЭК,ЗАВ,ЗАД,ЗАЛ,ЗАН,ЗЕВ,ЗЛО,ЗОБ,ЗОВ, ЗУБ, ЗУД, ЗЫ
К,ИВА,ИГО,ИКС,ИНЯ,ИНК,...] [АГАТ,АДЫ Г,АЖУР,АЗОТ,АИСТ,АЙВА,АКЫН,АЛОЭ,АЛЬТ,АНБ
к,АНИС,АНОД,АПАШ,АРАБ,АРАП,АРАТ,АРБА,АР ГО,АРИЯ,АРКА,АРФА,АРЫК,АТОН,АУРА,АШУ Г
,БАБА,БАЗА,БАКИ,БАЛЛ,БАЛТ,БАНК,БАНТ,БАНЯ,БАРД,БАРК,БАРС,БАСК,БАТЯ,БАУЛ,БАЯН,
БЕГА,БЕДА,БЕЗЕ,БЕЛИ,БЗИК,БИЛО,БИНТ,БИТА,БЛАТ,БЛЕФ,БЛИК,БЛИН,БЛИЦ,БЛОК,БЛУД,Б
ШЮЗ,БОБР,БОЕЦ,БОКС,БОЛТ,БОЛЬ,БОНЖ,БОНА,БОРТ,БОРЩ,БОСС,БРАК,БРАТ,БРЕ Г,БРЕД,БР
ИГ,БРИЗ,БРОД,БРОН,БРУС,БУЕР,БУЗА,БУКА,БУНТ,БУРТ,БУРЯ,БУСЫ,БУЧА,БУЯН,БЫЛЬ,БЬЕ
н>,БЮРО,БЮСТ,БЯЗЬ,БЯКА,ВАГА,ВАЗА,ВАТА,ВАТТ,ВВОД,ВВОЗ,ВДОХ,ВЕЕР,ВЕКО,ВЕНА, ...]
[АББАТ,АБЗАЦ,АБОРТ,АБРЕК,АБРИС,АБХАЗ,АВАНС,АВРАЛ,АВТОЛ,АВТОР,АГАВА,АГЕНТ,АГ
НЕЦ,АДЕПТ,АДРЕС,АЗАРТ,АЗИАТ,АЙНАК,АКТЁР,АКТИВ,АКУЛА,АКЦИЗ,АКЦИЯ,АЛЕУТ,АЛИБИ,
клКАШ,АЛЛАХ,АЛЛЕЯ,АЛЛЮР,АЛНАЗ,АЛТЫН,АЛЫЧА,АЛЬФА,АМБАР,АНВОН, АМЁБА, АНИНЬ, АНПЕ
Р,АНПИР,АНАША,АН ГАР,АН ГЕЛ,АНОНС,АНЧАР,АОРТА,АПАЧИ,АПОРТ,АРБУЗ,АР ГОН,АРЕАЛ,АР
ЕНА,АРЕСТ,АРИЕЦ,АРКАН,АРНИЯ,АРНЯК,АРХАР,АРШИН,АСКЕТ,АСПИД,АСТНА,АСТРА,АТАКА,
ктЛАС,АТЛЕТ,АТОЛЛ,АФЕРА,АФИША,АЦТЕК,БАБКА,БАГАЖ,БАГЕТ,БАГОР,БАДЬЯ,БАЗАР,БАЗИ
С,БАЙКА,БАЛДА,БАЛЕТ,БАЛКА,БАЛОК,БАЛЫК,БАНАН,БАНДА,БАНКА,БАРАК,БАРАН,БАРДА,БА
РЖА,БАРИЙ,БАРИН,БАРИЧ,БАРКА,БАРНЫ,БАРОН,БАРЫШ,БАСНА,БАСНЯ,БАСОК,БАТОГ,...][АББАТ,АБЗАЦ,АБОРТ,АБРЕК,АБРИС,АБХАЗ,АВАНС,АВРАЛ,АВТОЛ,АВТОР,АГАВА,АГЕНТ,АГН
ЕЦ,АДЕПТ,АДРЕС,АЗАРТ,АЗИАТ,АЙНАК,АКТЁР,АКТИВ,АКУЛА,АКЦИЗ,АКЦИЯ,АЛЕУТ,АЛИБИ,А
ШКАШ,АЛЛАХ,АЛЛЕЯ,АЛЛЮР,АЛНАЗ,АЛТЫН,АЛЫЧА,АЛЬФА,АНБАР,АНВОН,АНЁБА,АНИНЬ,АНПЕР
,АНПИР,АНАША,АН ГАР,АН ГЕЛ,АНОНС,АНЧАР,АОРТА,АПАЧИ,АПОРТ,АРБУЗ,АР ГОН,АРЕАЛ,АРЕ
НА,АРЕСТ,АРИЕЦ,АРКАН,АРНИЯ,АРНЯК,АРХАР,АРШИН,АСКЕТ,АСПИД,АСТНА,АСТРА,АТАКА,А
ТЛАС,АТЛЕТ,АТОЛЛ,АФЕРА,АФИША,АЦТЕК,БАБКА,БАГАЖ,БАГЕТ,БАГОР,БАДЬЯ,БАЗАР,БАЗИС
,БАЙКА,БАЛДА,БАЛЕТ,БАЛКА,БАЛОК,БАЛЫК,БАНАН,БАНДА,БАНКА,БАРАК,БАРАН,БАРДА,БАР
MCA,БАРИЙ,БАРИН,БАРИЧ,БАРКА,БАРНЫ,БАРОН,БАРЫШ,БАСНА,БАСНЯ,БАСОК,БАТОГ,...]Метод AsEnumerableМетод AsEnumerable возвращает последовательность того же типа, что и задан-
j function AsEnumerable(): sequence of T;Создаём список из 20 элементов типа integer:Ivar list := Lst(Range(1, 20)).Println;И конвертируем его в последовательность того же типа:Ivar sq:= list.AsEnumerable().Println;Элементы списка и последовательности, естественно, одинаковые:■ Метод АіЕпи тега ЬІе—□ XМетод ftsEnumerableА123456789 101112131415161718 19 20123456789 101112131415161718 19 20VМетод AggregateМетод Aggregate возвращает значение типа T, вычисленное аккумулирующей
функцией func, которая применяется ко всем элементам заданной последова¬
тельности:j function Aggregate(func: (T,T)->T): T;Все параметры функции func имеют один и тот же тип. Первый параметр - это
аккумулятор. Вначале он равен первому элементу заданной последовательности.
Второй параметр - это следующий элемент последовательности: второй, тре¬
тий,..., последний. Он добавляется к аккумулятору. Конечное значение аккумуля¬
тора возвращается методом Aggregate. Тип возвращаемого значения определя¬
ется типом функции.Найдём сумму первых 100 натуральных чисел в последовательности sqi, которую
напечатаем на экране:і var sqi := Range(1, 100).Println;Сумму элементов последовательности мы получим от метода Aggregate. Значе¬
ние переменной res - это промежуточная сумма элементов заданной последова¬
тельности. Она инициализируется первым элементом последовательности, а за¬
тем мы друг за другом добавляем к промежуточной сумме res все остальные
числа последовательности next, начиная со второго и заканчивая последним.
Окончательное значение переменной res присваивается переменной sum, кото¬
рую мы и печатаем на экране:Console.WriteLine('Сумма элементов последовательности sqi = {0}',sqi.Sum);var sum := sqi.Aggregate((res, next) -> res + next);Console.WriteLine('Сумма элементов последовательности sqi = {0}',sum);Для проверки печатаем сумму элементов, вычисленную методом расширения
Sum. Рисунок показывает, что метод Aggregate правильно нашёл сумму:■ Метод Aggregate — □ XМетод Aggregate1 2 В 4 5 6 7 S 9 1Э 11 12 1В 14 15 16 17 18 19 20 21 22 23 24 25 2
6 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 9
3 94 95 96 97 98 99 100Сумма элементов последовательности sqi = 5050
Сумма элементов последовательности sqi = 5050
Второй метод Aggregate позволяет задать начальное значение аккумулятора seed:function Aggregate<Accum>(seed: T; func: (Accum,T)->Accum): T;Создаём из строковой последовательности IstStr строку rev2, в которой слова рас¬
положены в обратном направлении.// второй пример:var lstStrAll := ReadAllLines('OSH-W97.txt').ToList();var lstStr := lstStrAll.Take(7).Println;var rev := lstStr.Aggregate('', (res, next) -> next + ' ' +res).Println;Println;Метод Aggregate 2АБАЖУР АБАЗИНЕЦ АБАЗИНКА АББАТ АББАТИСА АББАТСТВО АББРЕВИАТУРА
АББРЕВИАТУРА АББАТСТВО АББАТИСА АББАТ АБАЗИНКА АБАЗИНЕЦ АБАЖУРНайдём сумму чисел последовательности sqi, которые меньше 20:
var sqi := Range(1, 100).Println;var sum := sqi.Aggregate(0, (res, next) -> next < 20 ?res + next : res);
Console.WriteLine('Сумма элементов последовательности, ' +'которые меньше 20, = {0}', sum);■ Метод Aggregate 2- □XМетодAggregate 2Л12 34 5 6 7 S 91011121314151617131920212223 24252627 2S29 30 31 3233343536373S3940414243444546 47434950 5152 53 54 5556575S5960616263646566676369 70717273 7475 76 77 7S79S0S1S2S3S43536373339909192 93949596 979S 99 100Суммаэлементов последовательности,которые меньше20,=190V
Для больших целых чисел используйте тип decimal:// факториал:var sqi := Range(1, 20).Println;var fact := sqi.Aggregate(decima1(1), (res, next) -> res * next);
Conso1e.WriteLine('Факториал = {0}', fact);Третий метод Aggregate дополнен ещё одной функцией - resultSelector, которая
вычисляет возвращаемое значение:function Aggregate<Accum,Res>(seed: T; func: (Accum,T)->Accum;resultSelector: Accum->Res): T;// третий пример:var lstStrAll := ReadA11Lines('OSH-W97.txt').ToList();
var lstStr := 1stStrA11.Take(5).Print1n;Если строка длинная, то можно использовать экземпляр типа StringBuilder:var rev := 1stStr.Aggregate(new StringBui1der('Перевёрнутый список '),(sb, next) -> sb.Insert(0, (next + ' ')),
sb -> sb.ToString().ToLower()).Print1n;Метод Aggregate 41 2 3 4 5 6 7 8 9 10 11 12 13 14 1Б 16 17 IS 19 2Ѳ
Факториал = 2432902008176640000Здесь функция resultSelector конвертирует строку в нижний регистр:
■ Метод Aggregate 1Метод Aggregate 2АБАЖУР АБАЗИНЕЦ АБАЗИНКА АББАТ АББАТИСАаббатиса аббат абазинка абазинец абажур перевёрнутый списокСнова найдём сумму элементов последовательности sqi, которые меньше 20, но
вернём не сумму, а квадратный корень из неё, применив к найденному значе¬
нию res математическую операцию извлечения квадратного корня. Если резуль¬
тат - вещественное число, то начальное значение аккумулятора должно иметь тип
real:var sqi := Range(1, 100).Println;var sum := sqi.Aggregate(0.0, (res, next) -> next < 20 ?res + next : res, res -> Sqrt(res));Console.WriteLine('Корень квадратный из суммы элементовпоследовательности, ' +'которые меньше 20, = {0}', sum);* Метод Aggregate }—□XМетод Aggregate 3л123456789101112131415161718192Ѳ21222324252627 28 29 30 31 32333435363738394Ѳ41424344454647484950 51 52 53 54 55565758596061626364656667686970717273 74 75 76 77 787980тН00828384858687888990919293949596 97 98 99 100Корень квадратный из суммы
ше 20, = 13,7840487520902элементовпоследовательности,которые меньVМетод JoinМетод Join объединяет две последовательности типа Res (заданная, внешняя) и
Tinner (внутренняя) по ключам outerKeySelector и innerKeySelector. Для каждого
элемента выходной последовательности вызывается метод resultSelector:
function Join<TInner,Key,Res>(inner: sequence of TInner;outerKeySelector: T->Key;
innerKeySelector: TInner->TKey;
resultSelector: (T,TInner)->Res):
sequence of Res;Для сравнения ключей используется компаратор проверки на равенство по
умолчанию.Разберём действие метода Join на примере.Мы будем искать слова-палиндромоиды, то есть такие пары слов, что если одно
слово из пары написать задом наперёд, то они совпадут. Например:СКЕЛЕТ - ТЕЛЕКС
ОСЕЛОК - КОЛЕСО
ШРАМ - МАРШДля переворота слов используем строковый метод расширения:function Reverse(Self: String): string; extensionmethod;beginResult := new string(Self.ToCharArray().Reverse().ToArray());end;Загружаем список слов IstStrAll:I var lstStrAll := ReadAllLines('OSH-W97.txt');Просматриваем его и каждое слово переворачиваем с помощью метода Reverse.
Снова просматриваем список слов lstStrAll и ищем в нём слово, которое совпа-
дает с перевёрнутым. Если такое слово обнаружится, то пара слов-палиндромо-
идов найдена, и мы добавляем строку с обоими словами в выходную последова¬
тельность.Из этого «алгоритма» можно сделать такие выводы:• Внешняя последовательность - это список слов lstStrAll• Внутрення последовательность - это также список слов lstStrAll, поскольку все
слова содержатся в одной и той же последовательности. В других задачах это
может быть, конечно, другая последовательность - и даже не обязательно
строковая.• Ключ для слов первой последовательности - перевёрнутое слово. Его мы полу¬
чаем от метода outerKeySelector.• Ключ для слов второй последовательности - прямое слово. Его мы получаем от
метода innerKeySelector.• Если ключи (то есть слова - перевёрнутое и прямое) совпадут, то метод
resultSelector формирует результат операции, который будет добавлен в вы¬
ходную последовательность. Мы хотим получить строку, состоящую из слов-
палиндромоидов, разделённых пробелом, поэтому выходная последователь¬
ность имеет тип String.Для визуализации найденного списка палиндромоидов мы печатаем его в кон¬
сольном окне://палиндромоиды и палиндромы:var 1st := 1stStrA11.Join(1stStrA11,s1 -> s1.Reverse(),
s2 -> s2,(s1, s2) -> s1 + ' ' + s2).Println(NewLine);Пристальный взгляд на рисунок убеждает нас, что мы нашли не только собственно
палиндромоиды, но и палиндромы:^290 ^
■ Метод Join — □ X■ Метод Join — □ XАДЕД ДЕДлМетод JoinДОВОД ДОВОДДОГ годАПОРТ ТРОПАдок кодАРАП ПАРАдоход доходАРАТ ТАРАЖАР РАЖБАР РАБЗАКАЗ ЗАКАЗБАРК КРАБЗАЛ ЛАЗБОБ БОБЗОВ возБРЕГ ГЕРБКАБАК КАБАКБУК КУБКАЗАК КАЗАКВЕС СЕВКАЛ ЛАКВОЗ зовКАП ПАКвол ловКАПОР РОПАКВОР РОВКАРТ ТРАКВОРОН НОРОВКИТ ТИКГАИ МАГКЛЁШ ШЁЛКГЕРБ БРЕГклоп полкГОД догКОД докгол логкок кокГРОМ МОРГкокс скокVVОт палиндромов можно избавиться, сравнив дополнительно первое (прямое!)
слово со вторым: если они совпадут, значит, это палиндромы:Println;//палиндромоиды:var lst2 := lstStrAll.Join(lstStrAll,s1 -> s1.Reverse(),
s2 -> s2,(s1, s2) -> s1 = s2 ? nil : s1 + ' ' + s2)
.Where(s -> s <> nil).Println(NewLine);На рисунке вы уже не увидите палиндромов ДЕД, ДОВОД, ДОХОД, ЗАКАЗ, КАБАК,
КАЗАК и КОК:
* Метод Join□ X* Метод Join□ XГРОТ ТОРГ
ГУЛ ЛУГ
ДОГ ГОД
ДОК КОД
ЖАР РАЖ
ВАЛ ЛАЗ
ЗОВ ВОЗ
КАЛ ЛАК
КАП ПАК
КАПОР РОПАК
КАРТ ТРАК
КИТ ТИК
КЛЁШ ШЁЛК
КЛОП ПОЛК
КОД док
кокс скокКОЛЕСО ОСЕЛОКкот ток
кош шокКРАБ БАРК
КРАП ПАРК
КУБ БУК
КУС СУК
ЛАЗ ЗАЛ
ЛАК КАЛ
ЛОВ ВОЛАналогично мы можем получить только спи¬
сок палиндромов:БОБДЕДДоводдоходЗАКАЗКАБАККАЗАККОККОЛОККОНОКНАДАННИННАГАНркоюнопоппотопПУПРАДАРРОТАТОРРОТОРТАТТОПОТТУТШАБАШШАЛАШШИШPrintln;//палиндромы:var lst3 := lstStrAll.Join(lstStrAll,s1 -> s1.Reverse(),
s2 -> s2,(s1, s2) -> s1 <> s2 ? nil : s1)
.Where(s -> s <> nil)
.Println(NewLine);Второй метод Join для сравнения ключей используется заданный компаратор com¬
parer проверки на равенство:function Join<TInner,Key,Res>(inner: sequence of TInner;
outerKeySelector: T->Key;
innerKeySelector: TInner->TKey;
resultSelector: (T,TInner)->Res;
comparer:IEqualityComparer<Key>):
sequence of Res;Метод GroupJoinМетод GroupJoin действует так же, как метод Join, но возвращает группы последо¬
вательностей:function GroupJoin<TInner,Key,Res>(inner: sequence of TInner;outerKeySelector: T->Key;
innerKeySelector: TInner->TKey;
resultSelector: (T,sequence of TInner)->Res):
sequence of Res;Для сравнения ключей используется компаратор проверки на равенство по
умолчанию.Займёмся поиском анаграмм. Внимательный взор на список анаграмм обнаружит
слова, которые имеют не одну анаграмму, а больше - от двух до шести. Например,
слову ТОРС принадлежат анаграммы РОСТ и СОРТ, слову ТОРФ - ФОРТ и ФТОР,
слову ТРОПИК - ПОРТИК, ПРИТОК и ПОРТКИ.Лучше и правильнее объединить такие родственные анаграммы в одну группу, за¬
менив метод Join групповым методом объединения последовательностей
GroupJoin://анаграмммы:var groups := lstStrAll.GroupJoin(lstStrAll,s1 -> new string(s1.OrderBy(ch -> ch).ToArray),
s2 -> new string(s2.OrderBy(ch -> ch).ToArray),(s1, g) -> s1 = g.ElementAt(0) ?nil : String.Join(' ', g)).Where(s -> s <> nil)
.Println(NewLine);Второй параметр метода resultSelector имеет тип не Tinner, как раньше, а sequence
of Tinner, то есть последовательность всех слов второй последовательности, име¬
ющих ключ, который возвращает метод outerKeySelector. В нашем случае в группе
слов окажутся все слова, состоящие из тех же букв, что и отсортированное слово
s1, включая и само первое слово.Изъян этого способа состоит в том, что большие группы анаграмм будут повто¬
ряться в выходной последовательности (они располагаются не в алфавитном по¬
рядке):llJ Метод GroupJoin — □ XНЕОФИТКА ФОНЕТИКАлТОРФ ФОРТ ФТОРФАКТОР ФОРТКАТОРФ ФОРТ ФТОРТУФ ФУТМАХ ХАМСОХА ХАОСХАЛВА ХВАЛАЛИХОСТЬ ХИЛОСТЬХАН ХНАПРАХ ХРАПУХОД ХУДОАВСТРИЕЦ ЦАРСТВИЕКОЛЬЦО ЦОКОЛЬУМНИЦА ЦУНАМИКАЛАЧ ЧАЛКАСЧИТКА ЧАСТИК ЧИСТКАКЕДРАЧ ЧЕРДАКРЕЧНИЦА ЧЕРНИЦАЧЕПРАК ЧЕРПАКСЧИТКА ЧАСТИК ЧИСТКАЛЕНЧИК ЧЛЕНИКПОРОЧНОСТЬ ЧОПОРНОСТЬVДля ясности восприятия отсортируем группы по алфавиту:Println;var groups2 := groups.OrderBy(s -> s).Println(NewLine);
На рисунке вы видите, что группа РОСТ-
СОРТ-ТОРС-ТРОС присутствует в списке
трижды, а группа РУНА-УРАН-УРНА - два¬
жды:ёЗ Метод GroupJoin — □ XС помощью метода Distinct мы отсеем
одинаковые строки в группах:var groups2 := groups.Dis-
tinct().OrderBy(s -> s).Println(NewLine);РОСТ СОРТ ТОРС ТРОС
РОСТ СОРТ ТОРС ТРОС
РОСТ СОРТ ТОРС ТРОС
РУНА УРАН УРНА
РУНА УРАН УРНА
РУНО УРОН
РЯД ЯРД
СААМ САМА
САЙРА САРАЙ
САПУН СУММА
САНИТАР СТАРИНА
СВАЛКА СЛАВКА
СВИНТУС СВИСТУН
СДИРКА СКИРДА
СЕДЁЛКА СЕЛЁДКА
СЕКАЧ СЕЧКА
СЕЛЬПО СОПЕЛЬ
СЕНАТ СТЕНА
СКЕЛЕТ ТЕЛЕКС
СКЛОКА СКОЛКА
СКОТ СТОКТеперь все группы в последовательности groups2 содержатся однократно:VРОСИНКА СОРИНКА
РОСТ СОРТ ТОРС ТРОС
РУНА УРАН УРНА
РУНО УРОН
РЯД ЯРД
СААМ САМА
САЙРА САРАЙ
САМУМ СУММА
САНИТАР СТАРИНА
СВАЛКА СЛАВКА
СВИНТУС СВИСТУН
СДИРКА СКИРДА
СЕДЁЛКА СЕЛЁДКА
СЕКАЧ СЕЧКА
СЕЛЬПО СОПЕЛЬ
СЕНАТ СТЕНА
СКЕЛЕТ ТЕЛЕКС
СКЛОКА СКОЛКА
СКОТ СТОК
СОБРАТ ТОРБАС
СОРАТНИЦА СТАЦИОНАР
СОТКА ТОСКА
Второй метод GroupJoin для сравнения ключей использует заданный компаратор
comparer проверки на равенство:function GroupJoin<TInner,Key,Res>(inner: sequence of TInner;outerKeySelector: T->Key;
innerKeySelector: TInner->TKey;
resultSelector: (T,sequence of TInner)->Res;comparer: IEqualityComparer<Key>):
sequence of Res;Метод GroupByМетод GroupBy возвращает последовательность, в которой элементы разбиты на
группы по значению ключа. Ключ для каждого элемента заданной последователь¬
ности вычисляет метод keySelector:function GroupBy<Key>(keySelector: T->Key):IEnumerable<IGrouping<Key,T>>;Для сравнения ключей используется компаратор проверки на равенство по
умолчанию.В новом проекте мы разобьём последовательность чисел sq на 2 группы - чётные
числа и нечётные числа. Для чётных чисел остаток от деления на 2 равен нулю,
для нечётных - единице. Таким образом, ключом может служить остаток от деле¬
ния числа на двойку:var sq := Range(1, 20).Println;var groupi := sq.GroupBy(n -> n mod 2);
Метод keySelector получает число и находит для него ключ - 0 или 1. В зависимо¬
сти от значения ключа метод GroupBy помещает число в соответствующую группу- чётных или нечётных чисел. В итоге мы получаем результирующую последова¬
тельность, по структуре напоминающую словарь Dictionary и состоящую из не¬
скольких групп (возможно, ни из одной). Тип каждой группы - IGrouping<Key, T>, в
данном примере - IGrouping<integer, integer>.В переменной groupi мы получаем последовательность групп, которые можно пе¬
ребрать в цикле foreach. Каждая группа имеет одно и то же значение ключа:foreach var g in groupi do
beginConsole.WriteLine('Ключ = {0}:', g.Key);Print(g);Console.WriteLine();end;На рисунке вы видите, что числа правильно разбиты на группы, но порядок групп
в выходной последовательности зависит от значения первого элемента исходной
последовательности. На верхнем рисунке последовательность начинается с
группы нечётных чисел, а на нижнем - с группы чётных чисел:■ Метод GroupBy — □ XAМетод GroupBy1 2 В 4 Б 6 7 S 9 10 11 12 IB 14 1Б 16 17 IS 19 20Ключ = 1:[1,3,5,7,9,11,13,15,17,19]Ключ = 0:[2,4,6,8,10,12,14,16,IS,20]Vvar sq := (0 + Range(1, 20))
.Println;
■ Метод Group ByМетод GnoupBy0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 IS 19 20
Ключ = 0:[0,2,4,6,3,10,12,14,16,13,20]Ключ = 1:[1,3,5,7,9,11,13,15,17,19]Вполне разумно отсортировать группы в выходной последовательности по воз¬
растанию значения ключа:Ivar groupi := sq.GroupBy(n -> n mod 2).OrderBy(g -> g.Key);Теперь в выходной по¬
ел едовател ьности■ Метод Group&y — □ XАгруппа чётных чиселМетод GnoupByбудет предшествовать0 1 2 3 4 5 6 7 S 9 10 11 12 13 14 15 16 17 18 19 20группе нечётных чи¬Ключ = 0:сел - как на рисунке[0,2,4,6,8,10,12,14,16,18,20]выше. Это значит, чтоКлюч = 1:[1,3,5,7,9,11,13,15,17,19]вы можете гарантиро¬
ванно получить нуж¬Чётные числа:[0,2,4,6,8,10,12,14,16,18,20]ную группу чисел поНечётные числа:ключу и, например,Vнапечатать её:Println;Console.WriteLine('Чётные числа:');
var gg := groupi.ElementAt(0);
Print(gg);Console.WriteLine();
Console.WriteLine('Нечётные числа:');
gg := groupi.ElementAt(1);Print(gg);Println;
Во втором примере мы отыщем в словаре Ожегова все слова-мамы, то есть такие
слова, которые состоят из двух одинаковых частей.И здесь ключ может принимать два значения - true и false. Словам-мамам соот¬
ветствует ключ со значением true:var lstStrAll := ReadAllLines('OSH-W97.txt');var groups := lstStrAll.GroupBy(s -> (s.Length mod 2 = 0) and(s.Substring(0, s.Length div 2) =
s.Substring(s.Length div 2, s.Length div2)));foreach var g in groups do
beginif (g.Key) thenPrintln(g, NewLine);end;ill Метод Group By 2 — □ XМетод GnoupBy 2[БАБА,БЕРБЕР,ВАРВАР,ГАГА,ДЯДЯ,КАНКАН,МАМА,НЯНЯ,
ПАПА,ПУРПУР,ТАМТАМ,ТАРТАР,ТЯТЯ,ЦАЦА,ЦЕЦЕ]Все остальные слова (а их больше 20 тысяч!) попадут в группу со значением ключа
false. Поскольку нас эта группа не интересует, то с помощью метода Where мы
можем оставить только группу с ключом true:var groups := lstStrAll.GroupBy(s -> (s.Length mod 2 = 0) and(s.Substring(0, s.Length div 2) =
s.Substring(s.Length div 2, s.Length div 2)))
.Where(g -> g.Key);
Цикл foreach напечатает тот же список слов-мам, но уже без дополнительной про¬
верки ключа:foreach var g in groups do
begin//if (g.Key) thenPrintln(g, NewLine);end;Чтобы подсчитать число слов каждой длины, мы можем разбить их на группы по
длине, а затем отсортировать группы по числу элементов:var lstStrAll := ReadAllLines('OSH-W97.txt');var groups := lstStrAll.GroupBy(s -> s.Length).OrderByDescending(g -> g.Count());foreach var g in groups do
beginConsole.WriteLine(^^o слов = {1} - Длина слов = {0}',g.Key.ToString().PadLeft(3),g.Count().ToString().PadLeft(5));//Print(g);//Console.WriteLine();end;Рисунок неопровержимо свидетельствует, что в русском языке больше всего су¬
ществительных из 8 и 7 букв.I Чтобы напечатать все группы слов, раскомментируйте строчки кода!^300 ^
■ Метод Group By 3МетодGnoupBy3Числослов=3596-Длинаслов=0Числослов=35В5-Длинаслов=7Числослов=3212-Длинаслов=9Числослов=3024-Длинаслов=6Числослов=2746-Длинаслов=10Числослов=2432-Длинаслов=5Числослов=2177-Длинаслов=11Числослов=1676-Длинаслов=12Числослов=1277-Длинаслов=13Числослов=11S9-Длинаслов=4Числослов=933-Длинаслов=14Числослов=515-Длинаслов=15Числослов=352-Длинаслов=3Числослов=326-Длинаслов=16Числослов=162-Длинаслов=17Числослов=93-Длинаслов=13Числослов=47-Длинаслов=19Числослов=25-Длинаслов=2Числослов=21-Длинаслов=20Числослов=10-Длинаслов=21Числослов=2-Длинаслов=22Числослов=1-Длинаслов=23Числослов=1-Длинаслов=24Изменив условие в методе keySelector, мы узнаем, сколько существительных начи¬
нается на каждую букву:var lstStrAll := ReadAllLines('OSH-W97.txt');var groups := lstStrAll.GroupBy(s -> s.First()).OrderByDescending(g -> g.Count());foreach var g in groups do
beginConsole.WriteLine(^^o слов = {1} - Начальная буква = {0}',g.Key.ToString().PadLeft(3),g.Count().ToString().PadLeft(5));//Print(g);//Console.WriteLine();end;
Оказывается, русские слова предпочитают начинаться на букву П:■ Метод GroupBy 4 — □ XМетод GroupBy4Числослов=4102_Начальнаябуква=ПЧислослов=2814-Начальнаябуква=СЧислослов=2207-Начальнаябуква=КЧислослов=1618-Начальнаябуква=ОЧислослов=1465-Начальнаябуква=рЧислослов=1376-Начальнаябуква=БЧислослов=1345-Начальнаябуква=мЧислослов=1331-Начальнаябуква=вЧислослов=1262-Начальнаябуква=нЧислослов=1090-Начальнаябуква=тЧислослов=107S-Начальнаябуква=дЧислослов=910-Начальнаябуква=гЧислослов=900-Начальнаябуква=3Числослов=797-Начальнаябуква=АЧислослов=725-Начальнаябуква=ЛЧислослов=722-Начальнаябуква=УЧислослов=679-Начальнаябуква=иЧислослов=534-Начальнаябуква=фЧислослов=474-Начальнаябуква=шЧислослов=421-Начальнаябуква=чЧислослов=378-Начальнаябуква=XЧислослов=376-Начальнаябуква=вЧислослов=248-Начальнаябуква=жЧислослов=182-Начальнаябуква=цЧислослов=133-Начальнаябуква=яЧислослов=99-Начальнаябуква=ЕЧислослов=70-Начальнаябуква=щЧислослов=53-Начальнаябуква=юЧислослов=9-Начальнаябуква=ЁЧислослов=9-Начальнаябуква=ЙОстальные методы GroupBy:function GroupBy<Key>(keySelector: T->Key;comparer: System.Collections.Generic.IEqualityComparer<Key>):
IEnumerable<IGrouping<Key,T>>;
Как первый метод, но для сравнения ключей используется заданный компаратор
проверки на равенство comparer.function GroupBy<Key,E1ement>(keySe1ector: T->Key;elementSelector: T->E1ement):
IEnumerab1e<IGrouping<Key,T>>;Как первый метод, но к каждому исходному элементу применяется метод ele¬
mentSelector . Для сравнения ключей используется компаратор проверки на ра¬
венство по умолчанию.function GroupBy<Key,E1ement>(keySe1ector: T->Key;elementSelector: T->E1ement;
comparer: IEqua1ityComparer<Key>):
IEnumerab1e<IGrouping<Key,E1ement>>;Как предыдущий метод, но для сравнения ключей используется заданный компа¬
ратор проверки на равенство comparer.function GroupBy<Key,Res>(keySe1ector: T->Key;resultSelector: (Key,sequence of T)->Res):
sequence of Res;Как первый метод, но результирующее значение для каждой группы вычисляет
метод resultSelector. Для сравнения ключей используется компаратор проверки на
равенство по умолчанию.function GroupBy<Key,E1ement,Res>(keySe1ector: T->Key;elementSelector: T->E1ement;
resultSelector: (Key,sequence of E1ement)->Res):
sequence of Res;Как предыдущий метод, но для сравнения ключей используется заданный компа¬
ратор проверки на равенство comparer.
function GroupBy<Key,Res>(keySelector: T->Key;resultSelector: (Key,sequence of T)->Res;
comparer: IEqualityComparer<Key>):
sequence of Res;Как третий и пятый метод вместе взятые.function GroupBy<Key,Element,Res>(keySelector: T->Key;elementSelector: System.T->Element;
resultSelector: (Key,sequence of Element)->Res;comparer: IEqualityComparer<Key>):
sequence of Res;Как предыдущий метод, но для сравнения ключей используется заданный компа¬
ратор проверки на равенство comparer.Дополнительные методы расширения
для последовательностейКроме методов расширения для последовательностей, позаимствованных у плат¬
формы .NET, паскаль PascalABC.NETимеет немало новых, собственных.Методы Print и PrintlnРассмотрены нами ранее.Метод ReadLinesМетод ReadLines возвращает последовательность строк из заданного файла:function ReadLines(path: string): sequence of string;
Файл открывается и закрывается автоматически. Кодировка строк - Windows.var sqStrAll := ReadLines('OSH-W97.txt')
.Take(19).Println;Метод ReadLinesАБАЖУР АБАЗИНЕЦ АБАЗИНКА АББАТ АББАТИСА АББАТСТВО АББРЕВИАТУРА
АБЕРРАЦИЯ АБЗАЦ АБИССИНЕЦ АБИССИНКА АБИТУРИЕНТ АБИТУРИЕНТКА А
БОНЕМЕНТ АБОНЕНТ АБОНЕНТКА АБОРДАЖ АБОРИГЕН АБОРИГЕНКАВторой метод ReadLines действует так же, но дополнительно можно задать коди¬
ровку строк в файле:function ReadLines(path: string; en: Encoding): sequence of string;Метод WriteLinesМетод WriteLines выводит строковую последовательность в заданный файл:function WriteLines(Self: sequence of string; fname: string):sequence of string; extensionmethod;var sq := Seq('раз','два','три')
.Println;Println;sq.WriteLines('Метод WriteLines.txt');
* Метод WriteLines□X ^ Lister - [e:\_0... — □XФайл Правка Вид КодировкаСправка100%разЛдватриV<>Метод WniteLines
раз два триМетод ForEachМетод ForEach - это процедура, поэтому он не возвращает элементов последова¬
тельности, а только применяет к ним указанные действия:j procedure ForEach(action: T->());Например, мы можем напечатать все элементы последовательности:Ivar sq := Range(1,10).Println;
sq.ForEach(n -> Println(n));■ ММетод ForEach — □ XАМетод ForEach123456789 10VЧтобы «вернуть» значение, необходимо использовать внешнюю переменную:
Println;
var summa:= 0;sq.ForEach(procedure(n) -> summa := summa + n * n);
Println('Сумма квадратов = ' + summa);■ MМетод ForEach—□XМетод ForEachA12345678910Сумяв квадратов = 385VВторой метод ForEach применяет к элементам последовательности указанные
действия с учётом индекса:procedure ForEach<T>(Self: sequence of T; action: (T,integer) -> ());extensionmethod;Печатаем суммы элементов и их индексов в последовательности:var sq := Range(1,10).Println;
sq.ForEach((n, i) -> Print(i+n));■ MМетод ForEach—□XМетод ForEachл1234B67S9 10
1 3 Б 7 9 11 13 15 17 19V
Метод BatchМетод Batch разбивает последовательность на последовательности (серии) за¬
данной длины size:function Batch<T>(Self: sequence of T; size: integer):
sequence of sequence of T; extensionmethod;Если число элементов в последовательности не кратно size, то в последнюю се¬
рию помещаются оставшиеся элементы:Ivar sq := Range(1, 20).Println;sq.Batch(7).Println;■ Метод Batch- □ XМетод BatchЛ12 3 4 5 6 7 89 10 11 12 1314 15 16 17 18 19 20[1,2,3,4,5,6,7][8,9,10,11,12,13,14] [15,16,17,18,19,20]VВторой метод Batch разбивает последовательность на последовательности задан¬
ной длины size и применяет к каждой серии заданную функцию proj:function Batch<T,Res>(Self: sequence of T; size: integer;
proj: Func<IEnumerable<T>,Res>):
sequence of Res; extensionmethod;Так как каждая серия элементов - это последовательность, то к ней можно при¬
менить любой из рассмотренных выше методов. Например, отберём из каждой
серии только чётные числа:Println;
sq.Batch(5).Println;sq.Batch(5, n -> n.Where(n -> n mod 2 = 0)).Println;Мы получили 4 серии чётных чисел:■ Метод Batch- □ XМетод Batchл1 234567S9 10 11 12 13 14 15 16 17IS 19 20[1,2,3,4,5] [6,
[2,4] [6,S,10]7,S,9,10] [11,12,13,14,15]
[12,14] [16,IS,20][16,17,13,19,20]VМетод PairwiseМетод Pairwise возвращает последовательность кортежей, составленных изо всех
пар соседних элементов:function Pairwise<T>(Self: sequence of T): sequence of (T,T);extensionmethod;var sq := Range(1, 20)
.Println;Println;sq.Pairwise.Println;ММетод Pairwise1 2 В 4 Б 6 7 S 9 10 11 12 IB 14 1Б 16 17 IS 19 20(1,2) (2,3) (3,4) (4,Б) (Б, 6) (6,7) (7,S) (S,9) (9,10) (10,11) (11,12)
(12,13) (13,14) (14,15) (15,16) (16,17) (17,IS) (IS,19) (19,20)
Второй метод Pairwise возвращает последовательность кортежей, составленных
изо всех пар соседних элементов, и к каждому кортежу применяет заданную
функцию func:I function Pairwise<T,Res>(Self: sequence of T; func:(T,T)->Res):sequence of Res; extensionmethod;Найдём произведение пар чисел в каждом кортеже:
sq.Pairwise((x,y) -> x*y).Println;■ Метод PairwiseX□1ММетод PainwiseA1234567S9 10 11 12 13 1415 16 17 IS19 202 6 12 20 30 4256 72 90 110 132156 1S2 210240 272306 342 380VМетод PartitionМетод Partition возвращает кортеж из двух последовательностей. Функция cond
разбивает исходную последовательность на 2 части по заданному условию:I function Partition<T>(Self: sequence of T; cond: T->boolean):(sequence of T,sequence of T); extensionmethod;Разобьём числовую последовательность sq на 2 части так, чтобы в одной оказа¬
лись чётные числа, а в другой - нечётные:Ivar sq := Range(1, 20).Println;Println;var res := sq.Partition(n -> n mod 2 = 0);
Println(res.Item1);Println(res.Item2);■ Метод Partition- □ XМетод Partition1 2 3 4 5 6 7 8 9 10 11 12 1314 15 16 17 10 19 20[2,4,6,3,10,12,14,16,13,20]
[1,3,5,7,9,11,13,15,17,19]VВторой метод Partition возвращает кортеж из двух последовательностей. Функция
cond разбивает исходную последовательность на 2 части по заданному условию с
учётом индексов:function Partition<T>(Self: sequence of T;cond: (T,integer)->boolean):(sequence of T,sequence of T); extensionmethod;Пусть в первой последо¬
вательности будут чёт¬
ные числа, индексы ко¬
торых меньше 12:var sq := Range(1,20).Println;Println;var res := sq.Partition((n, i) -> (n mod 2 = 0) and (i < 12));Println(res.Item1);Println(res.Item2);■ Метод Partition- □ XМетод Partitionл1 2 3 4 5 6 7 S 9 10 11 12 13 14 15 1617 18 19 20[2,4,6,8,10,12][1,3,5,7,9,11,13,14,15,16,17,18,19,20]V
Метод SplitAtМетод SplitAt возвращает кортеж из двух последовательностей. Параметр ind -
это индекс элемента, начинающего вторую последовательность:function SplitAt<T>(Self: sequence of T; ind: integer):(sequence of T,sequence of T); extensionmethod;var sq := Range(1, 20)
.Println;Println;var res := sq.SplitAt(6);Println(res.Item1);Println(res.Item2);Метод CartesianМетод Cartesian возвращает декартово произведение двух последовательностей:function Cartesian<T,T1>(Self: sequence of T; b: sequence of T1):sequence of (T,T1); extensionmethod;var sq1 := Range(1, 4)
.Println;
var sq2 := Range(5, 7)Метод SplitAt1 2 3 4 Б 6 7 S 9 10 11 12 13 14 15 16 17 18 19 20
[1,2,3,4,5,6][7,8,9,10,11,12,13,14,15,16,17,13,19,20]
.Println;Println;var res := sq1.Cartesian(sq2).Println;Метод Cartesian12 3 45 6 7(1,5) (1,6) (1,7) (2,5) (2,6) (2,7) (3,5) (3,6) (3,7) (4,5) (4,6) (4,7)Второй метод Cartesian возвращает декартово произведение двух последователь¬
ностей. К каждой паре элементов в кортежах применяется функция func:function Cartesian<T,T1,T2>(Self: sequence of T; b: sequence of T1;func: (T,T1)->T2):sequence of T2; extensionmethod;Найдём произведение всех пар чисел в кортежах:var sq1 := Range(1, 4).Println;
var sq2 := Range(5, 7).Println;Println;sq1.Cartesian(sq2).Println;
var res := sq1.Cartesian(sq2, (x,y) -> x * y)
.Println;
■ Метод Cartesian — □ XМетодСаntesian12 345 6 7(1-5)(1-6) (1,7)(2,5)(2,6)(2,7) (3,5) (3,6) (3,7) (4,5) (4,6) (4,7)5 6 710 12 14 15IS2120242SДекартово произведение двух последовательностей и цикл foreach вполне могут
заменить традиционные вложенные циклы for.Сколько мне лет? (Range.Cartesian)Дядя Алёша вдвое старше меня, а цифры числа,
ющего мой возраст, равны сумме и разности
возраста.Сколько мне лет?Из условия задачи следует, что мне (автору задачи) не меньше 10 лет. Чтобы ре¬
шить задачу, достаточно перебирать цифры, составляющие мой возраст и све¬
ряться с текстом задачи:usesSystem;begin// заголовок окна:Console.Title := 'Сколько мне лет?';
Console.WniteLine('');Console.FonegnoundColon := ConsoleColor.Red;
Console.WniteLine('Сколько мне лет?');
Console.FonegnoundColon := ConsoleColor.Green;
Console.WriteLine();выража-
цифр его
// цифры:var digits := Range(0, 10);// решаем задачу:foreach var t in digits.Cartesian(digits) do
beginvar a := t.Item1;
var b := t.Item2;
var i := a * 10 + b;// мой возраст - двузначное число:
if (i < 10) then
continue;
var onkel := i * 2;
var dig1 := onkel div 10;
var dig2 := onkel mod 10;
if (((dig1 + dig2 = a) and
(Abs(dig1 - dig2) = b)) or
((dig1 + dig2 = b) and
(Abs(dig1 - dig2) = a))) then
beginWriteln('l^ возраст = ' + i);Writeln('Возраст дяди Алёши = ' + onkel);endend;Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Задача несложная, но для упражнения в программировании вполне годится* Сколько мне лет?—□XСколько мне лет?лкой возраст = 17
Возраст дяди Алёши = 34V
Великолепная семёрка (Range.Cartesian)В журнале Квантик №10 за 2015 год напечатана такая конкурсная задача:Заметим, что в числе 1000 семёрок нет, значит, нам достаточно проверить числа
от 1 до 999. Чтобы не выделять цифры из чисел, мы найдём декартово произве¬
дение всех цифр от 0 до 9:// цифры:var digits := Range(0, 9);var res := digits.Cartesian(digits);var cart3 := res.Cartesian(digits);Теперь мы можем перебирать все комбинации из трёх цифр в цикле foreach:I foreach var t in cart3 do
beginТекущие значения элементов кортежа дадут нам цифры очередного числа:
I var a := t.Item1.Item1;46. Хулиган Семён, любимое чис¬
ло которого - семь, забрался ньЛью
через окно в гостиницу ^Караван-Са-
рай» и с дверей всех номеров снял се¬
мёрки. Утром Семёна поймал поли¬
цейский Пронькин, который заявил,
что за каждую снятую цифру полага¬
ется платить штраф в размере одного
доллара. Сколько долларов придётся
заплатить Семёну, если в гостинице
1000 номеров и они нумеруются под¬
ряд, начиная с 1 ?\ t
var b := t.Item1.Item2;var c := t.Item2;Каждый кортеж состоит из двухэлементного кортежа и одиночного элемента:((0,0), 0)Вот так выглядит часть последовательности кортежей:Iteml - это двухэлементный кортеж. Первый элемент этого кортежа равен Iteml.
Iteml, а второй - Iteml. Item2.Item2 - одиночный элемент.Если какая-либо из цифр - семёрка, то мы увеличиваем на единицу счётчик семё¬
рок n7. И наконец, для сокращения кода мы используем 3 условных оператора ?:- по одному для каждого элемента кортежа:usesSystem;begin
// заголовок окна:Console.Title := 'Великолепная семёрка';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;
Console.WriteLine('Великолепная семёрка');
Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// цифры:var digits := Range(0, 9);var res := digits.Cartesian(digits);var cart3 := res.Cartesian(digits);//решаем задачу//число семёрок:
var n7 := 0;foreach var t in cart3 do
beginvar a :=t.Item1.Item1;var b :=t.Item1.Item2;var c :=t.Item2;var s :=0;s += a=7? 1 :0;s += b=7? 1 :0;s += c=7? 1 :0;n7 += s;end;Println('Всего семёрок: ' + n7);Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.Всё готово! Запускаем программу и тут же получаем точный и круглый ответ - в
числах от 1 до 1000 семёрка встречается ровно 300 раз:■ Великолепная семёрка—□XВеликолепная семёркаАВсего семёрок: 300V
Вьетнамские буйволы (Range.Cartesian)Вот очень старая вьетнамская за¬
дача:Для кормления 100 буйволов
заготовили 100 охапок сена.Стоящий молодой буйвол
съедает 5 охапок сена.Лежащий молодой буйвол
съедает 3 охапки сена.Старые буйволы втроём съе¬
дают 1 охапку сена.Сколько молодых буйволов стоят, сколько лежат и сколько буйволов старых?Решение задачи с буйволами не очень сильно отличается от решения предыду¬
щей.Буйволов каждого вида не меньше нуля и не больше (100 поделить на число съе¬
даемых охапок сена). Некоторую нервозность вносят старые буйволы, которых
следует считать тройками.Когда общее число буйволов и съедаемых охапок сена будет по 100, мы напеча¬
таем ответ:usesSystem;begin// заголовок окна:Console.Title := 'Вьетнамские буйволы';Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Вьетнамские буйволы');Console.ForegroundColor := ConsoleColor.Green;Ѳ
Console.WriteLine();// буйволы:var sqBuffaloS := Range(0, 100 div 5);var sqBuffaloSL := sqBuffaloS.Cartesian(Range(0, 100 div 3));
var cart3 := sqBuffaloSL.Cartesian(Range(0, 100 div 3));// решаем задачу:
foreach var t in cart3 do
beginvar buffaloS := t.Item1.Item1;
var buffaloL := t.Item1.Item2;
var bO := t.Item2;
var buffaloO := bO * 3;if ((buffaloS + buffaloL + buffaloO = 100) and
(buffaloS * 5 + buffaloL * 3 +
buffaloO div 3 = 100)) thenbeginPrintln('Стоящих молодых буйволов : '+ buffaloS);Println('Лежащих молодых буйволов : '+ buffaloL);Println('Старых буйволов : '+ buffaloO);Println;end
end;Console.Write-
Line();Console.Fore-
groundColor := Con¬
soleColor.Red;
end.Задача имеет 4 решения:■ Вьетнамские буйволы- □ XВьетнамские буйволылСтоящих молодых буйволов0Лежащих молодых буйволов25Старых буйволов75Стоящих молодых буйволов4Лежащих молодых буйволов18Старых буйволов78Стоящих молодых буйволов8Лежащих молодых буйволов11Старых буйволов81Стоящих молодых буйволов12Лежащих молодых буйволов4Старых буйволов84V
Метод IncrementalМетод Incremental возвращает последовательность, составленную изо всех пар
соседних элементов исходной последовательности, к которым применена функ¬
ция func:I function Incremental<T,T1>(Self: sequence of T; func: (T,T)->T1):sequence of T1; extensionmethod;Найдём произведения всех пар соседних элементов последовательности sq:var sq := Range(1, 20).Println;Println;var res := sq.Incremental((x,y) -> x*y).Println;■ Метод Incremental—□ XМетод IncrementalА1 2 В 4 Б 6 7 S 9 10 11 12 1В 1415 16 17 1S19 202 6 12 20 30 42 56 72 90 110 132156 1S2 210240 272 306342 3S0VВторой метод Incremental возвращает последовательность, составленную изо
всех пар соседних элементов исходной последовательности, к которым приме¬
нена функция func с учётом индекса пары (отсчёт начинается с 1):function Incremental<T,T1>(Self: sequence of T;func: (T,T,integer)->T1):
sequence of T1; extensionmethod;Найдём сумму всех соседних пар чисел и индекса пары:
var sq := Range(1, 20).Println;Println;var res := sq.Incremental((x,y,n) -> x + y + n)
.Println;Метод Incremental1 2 3 4 Б 6 7 S 9 10 11 12 13 14 15 16 17 IS 19 20
И 7 10 13 16 19 22 25 2S 31 34 37 40 43 46 49 52 55 5SМетод InterleaveМетод Interleave возвращает последовательность, составленную из чередую¬
щихся элементов двух заданных последовательностей:I function Interleave<T>(Self: sequence of T; a: sequence of T):sequence of T; extensionmethod;Результирующая последовательность заканчивается, когда в более короткой по¬
следовательности не останется элементов.var sq1 := Range(1, 10).Println;
var sq2 := Range(11, 20).Println;Println;sq1.Interleave(sq2).Println;
■ Метод InterleaveВторой метод Interleave возвращает последовательность, составленную из чере¬
дующихся элементов трёх заданных последовательностей:function Interleave<T>(Self: sequence of T; a,b: sequence of T):sequence of T; extensionmethod;var sq1 := Range(1, 6)
.Println;
var sq2 := Range(7, 14)
.Println;
var sq3 := Range(15, 20)
.Println;Println;sq1.Interleave(sq2, sq3)
.Println;Третий метод Interleave возвращает последовательность, составленную из чере¬
дующихся элементов четырёх заданных последовательностей:■ Метод Interleave 3□ X
function Interleave<T>(Self: sequence of T; a,b,c: sequence of T): se¬quence of T; extensionmethod;varsq1:= Range(1, 6).Println;varsq2:= Range(7, 14).Println;varsq3:= Range(15, 20).Println;varsq4:= Range(101, 116).Println;Println;sq1.Interleave(sq2, sq3,sq4 )
.Println;■ M егод 1 nterl ea ve 4X□1AМетод Interleave 41 2 3 4 Б 67 8 9 10 11 12 13 1415 16 17 18 19 20101 102 103 104 105 106187 108 109 110 111 112 113 114 115 1161 7 15 101 2 8 16 102 39 17 103 4 10 18 104 5 11 19 105 6 12 20 106VМетод JoinlntoStringМетод JoinlntoString без параметра возвращает строку, составленную из строко¬
вого представления элементов последовательности. Разделителем элементов
служит пробел.function JoinIntoString<T>(Self: sequence of T): string;extensionmethod;
var sq := Range(1, 10);sq.JoinlntoString.Println;■ Метод JoinlntoString—□XМетод JoinIntoStringA123456785 10VМетод JoinlntoString с параметром возвращает строку, составленную из строко¬
вого представления элементов последовательности. Разделителем элементов
служит строка delim.I function JoinIntoString<T>(Self: sequence of T; delim: string): string;extensionmethod;var sq := Range(1, 10);
sq.JoinIntoString(', ').Println;■ Метод JoinlntoString—□XМетод JoinlntoString1, 2, 3, 4, 5, 6, 7, 3, 9, 10V
Сотая цифра (Range. JoinlntoString)Задача 300 из книги Математическая шка¬
тулка [Нагибин88], страница 45:Все натуральные числа, начиная с 1, запи¬
саны в порядке их возрастания: 1 2 3 4 5 6 7
8 9 10 11...Какая цифра в этой записи стоит на сотом ме¬
сте?Проще всего решить задачу «строковым» способом.Мы не знаем, сколько чисел нам понадобится, поэтому в методе Range указываем
заведомо большее число элементов в последовательности:і Range(1,100)Так мы получим последовательность чисел от 1 до 100, а нам нужна строка. Метод
расширения JoinIntoString объединяет все элементы в одну строку, но по умолча¬
нию ставит между ними пробелы. Нам нужно записать все числа вплотную друг за
другом, поэтому в скобках указываем пустую строку:I JoinIntoString('')В итоге мы получим длинную строку из последовательных натуральных чисел:* Нагибин, с.45. Задача 300□XСотая цифра123456789101112131415161718192021222324252627282930313233343536373339404142434445464748495051525354555657585960616263646566676369707172737475767778793081323334353637338990919293949596979899100V
Выделяем из неё сотую цифру - и задача решена:usesSystem;// Нагибин, с.45, Задача 300begin// заголовок окна:Console.Title := 'Нагибин, с.45, Задача 300';
Console.WriteLine('');Console.ForegroundColor := ConsoleColor.Red;Console.WriteLine('Сотая цифра');Console.ForegroundColor := ConsoleColor.Green;
Console.WriteLine();// решаем задачу:var res:= Range(1,100).JoinIntoString('');// печатаем ответ:Console.WriteLine('На сотом месте стоит цифра: {0}', res[100])
Console.WriteLine();Console.ForegroundColor := ConsoleColor.Red;
end.На рисунке вы видите, что сотое место в строке занимает цифра 5:■ Нагибин, с.45. Задача 300—□XСотая цифраАНа сотом месте стоит цифра: 5VРешение «честным», числовым способом значительно сложнее!
Методы LastMaxBy и LastMinByМетод LastMaxBy возвращает последний элемент последовательности с макси¬
мальным значением ключа:I function LastMaxBy<T, TKey>(Self: sequence of T; selector: T -> TKey):
T; extensionmethod;var sq := SeqRandom(20, 1, 10)
.ToArray
.Println;Println;var res := sq.LastMaxBy(n -> n > 8);
Println(res);■ Методы LastMaxBy и LastMinBy — □XЛМетоды LastMaxBy и LastMinBy2533292332 10 10 93253755VМетод LastMinBy возвращает последний элемент последовательности с мини¬
мальным значением ключа:I function LastMinBy<T, TKey>(Self: sequence of T;selector: T -> TKey): T; extensionmethod;var sq := SeqRandom(20, 1, 10).ToArray.Println;Println;var res := sq.LastMinBy(n -> n mod 4);
Println(res);
[IJ Методы LastMaxBy и LastMinBy — □XМетоды LastMaxBy и LastMinBy7 10 87845 10 9521 10 48325 10 6VМетоды MaxBy и MinByМетод MaxBy возвращает первый элемент последовательности с максимальным
значением ключа:I function MaxBy<T, TKey>(Self: sequence of T;selector: T -> TKey): T; extensionmethod;var sq := SeqRandom(20, 1, 10)
.ToArray
.Println;Println;var res := sq.MaxBy(n -> n > 7);
Println(res);■ Методы MaxBy и MinBy — □XМетоды MaxBy и MinByЛ25784895816676334 10 32VМетод MinBy возвращает первый элемент последовательности с минимальным
значением ключа:I function MinBy<T, TKey>(Self: sequence of T;selector: T -> TKey): T; extensionmethod;
var sq := SeqRandom(20, 1, 10).ToArray.Println;Println;var res := sq.MinBy(n -> n mod 4);
Println(res);■ Методы МахВуи MinBy — □XМетоды МахВу иMinByA10 9 10 10 2 513261921439239SVМетод NumerateМетод Numerate возвращает последовательность кортежей. Первый элемент кор¬
тежа - номер элемента последовательности (счёт с 1), второй - сам элемент по-
следовател ьности:function Numerate<T>(Self: sequence of T): sequence of (integer,T);extensionmethod;var sq := SeqRandom(10, 1, 10)
.ToArray
.Println;Println;var res := sq.Numerate().Println;■ Метод Numerate- □ XМетод Numerate164Б613274(1,1) (2,6) (3,4) (4,Б)(Б,6) (6,1) (7,S) (S,2) (9,7) (10,4)V
Второй метод Numerate возвращает последовательность кортежей. Первый эле¬
мент кортежа - номер элемента последовательности (счёт с from), второй - сам
элемент последовательности:I function Numerate<T>(Self: sequence of T; from: integer):sequence of (integer,T); extensionmethod;var sq := SeqRandom(10, 1, 10)
.ToArray
.Println;Println;var res := sq.Numerate(0).Println;Метод Numerate
7 822216485(0,7) (1,8) (2,2) (3,2) (4,2) (5,1) (6,6) (7,4) (8,8) (9,5)Методы Print и PrintlnЭти методы мы уже рассмотрели раньше.Метод SkipLastЭтот метод мы уже рассмотрели раньше.
Метод SliceМетод Slice возвращает элементы последовательности, начиная с индекса from, с
шагом step:function Slice<T>(Self: sequence of T; from,step: integer):sequence of T; extensionmethod;var sq := Range(0, 20)
.Println;Println;sq.Slice(6, 2).Println;;■ Метод Slice- □ XМетод SliceA01234567S 9 1011 12 13 14 15 16 17 IS 19 206 S 10 12 14 16IS 20VI Шаг должен быть больше нуля!Второй метод Slice возвращает элементы последовательности, начиная с индекса
from, с шагом step, но не более count:function Slice<T>(Self: sequence of T; from,step,count: integer):sequence of T; extensionmethod;var sq := Range(0, 20)
.Println;Println;sq.Slice(6, 2, 4).Println;
■ Метод SliceМетод Slice0 1 2 3 4 Б 6 7 S 9 10 11 12 13 14 1Б 16 17 IS 19 206 S 10 12Методы Sorted и SortedDescendingМетод Sorted возвращает последовательность, отсортированную в лексикографи¬
ческом порядке (по возрастанию):function Sorted<T>(Self: sequence of T): sequence of T; extensionmethod;Отсортируем случайную последовательность. Чтобы «зафиксировать» значения,
преобразуем её в массив:Ivar sq := SeqRandom.ToArray.Println;
sq.Sorted.Println;■ Метод Sorted — □ XМетод Sorted34 87 6Б Б Б91389374Б375 13 34 37 374ББ96Б8789Метод SortedDescending возвращает последовательность, отсортированную в ан-
тилексикографическом порядке (по убыванию):function SortedDescending<T>(Self: sequence of T):sequence of T; extensionmethod;
var sq := SeqRandom.ToArray.Println;sq.SortedDescending.Println;■ Метод Sorted—□XМетод SortedБ2 39 86 23 33
90 33 36 Б2 433 90 11 43 21
39 23 21 11 3VМетод TabulateМетод Tabulate возвращает последовательность кортежей. Первый элемент кор¬
тежа - элемент последовательности, второй - значение заданной функции для
этого элемента:function Tabulate<T,T1>(Self: sequence of T; F: T->T1):sequence of (T,T1); extensionmethod;var sq := Range(0, 10).Println;Println;sq.Tabulate(x -> x * x).Println;■ Метод Tabulate- □ XМетод TabulateA0 12345678910(0,0) (1,1) (2,4) (3,9)(4,16) (Б,2Б) (6,36) (7,49) (3,64) (9,SI) (10,100)V
Метод TakeLastЭтот метод мы уже рассмотрели раньше.Метод ToHashSetМетод ToHashSet возвращает множество типа HashSet из элементов последова¬
тельности:function ToHashSet<T>(Self: sequence of T): HashSet<T>; extensionmethod;var sq := (Range(1, 19) + Seq(1,2))
.Println;Println;var hs:= sq.ToHashSet;Println(hs);Метод ToSortedSetМетод ToSortedSet возвращает множество типа SortedSet из элементов последо¬
вательности:function ToSortedSet<T>(Self: sequence of T): SortedSet<T>; extension¬
method;Метод ToHashSet1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 IB 19 1 2
{1,2,3,4-5,6,7,8,9,10,11,12,13,14,15,16,17,13-19}
var sq := Seq(2, 4, 7, 2, 67, 8, 1, 19, 4, 5, 4).Println;Println;var ss:= sq.ToSortedSet;Println(ss);■ MeroflToSortedSet—□XМетод ToSortedSet2 4 7 2 67 0 1 19 4 5 4{1,2,4,5,7,0,19,67}Метод ToLinkedListМетод ToLinkedList возвращает связный список типа LinkedList из элементов по-
следовател ьности:function ToLinkedList<T>(Self: sequence of T): LinkedList<T>;extensionmethod;var sq := Range(1, 20)
.Println;Println;var ll:= sq.ToLinkedList;
Println(ll);Метод ToLinkedList1 2 3 4 Б 6 7 S 9 10 11 12 13 14 15 16 17 IS 19 20[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]V
Метод ZipTupleМетод ZipTuple возвращает последовательность двухэлементных кортежей. Пер¬
вый элемент кортежа берётся из первой последовательности, а второй - из вто¬
рой - с тем же индексом.I function ZipTuple<T,T1>(Self: sequence of T; a: sequence of T1):sequence of (T,T1); extensionmethod;var sqi := Range(1, 10).Println;
var sq2 := Range(11, 18).Println;Println;sq1.ZipTuple(sq2).Println;В результирующей последовательности столько элементов, сколько их в более
короткой исходной последовательности:■ MeroflZipTupleX□1Метод ZipTupleA123456789 10
11 12 13 14 15 16 17IS(1,11) (2,12) (3,13)(4,14)(5j15) (6,16) (7,17) (8,18)VВторой метод ZipTuple возвращает последовательность трёхэлементных корте-и I I U м W Uжей. Первый элемент кортежа берётся из первой последовательности, второй -
из второй, а третий - из третьей. Все элементы имеют одинаковый индекс:function ZipTuple<T,T1,T2>(Self: sequence of T;a: sequence of T1; b: sequence of T2):
sequence of (T,T1,T2); extensionmethod;
var sq1 := Range(1, 6)
.Println;
var sq2 := Range(7, 14)
.Println;
var sq3 := Range(15, 20)
.Println;Println;sq1.ZipTuple(sq2, sq3)
.Println;■ Метод ZipTuple 3- □ XМетод ZipTuple 31 2 3 4 Б 67 8 9 10 11 12 13
1Б 16 17 IS 19 2014(1,7,15) (2,8,16)(3,9,17) (4,10,18) (5,11,19) (6,12,20)VТретий метод ZipTuple возвращает последовательность четырёхэлементных кор¬
тежей. Первый элемент кортежа берётся из первой последовательности, второй- из второй, третий - из третьей, а четвёртый - из четвёртой. Все элементы имеют
одинаковый индекс:function ZipTuple<T,T1,T2,T3>(Self: sequence of T;a: sequence of T1;
b: sequence of T2;
c: sequence of T3):
sequence of (T,T1,T2,T3);
extensionmethod;varsq1:= Range(1, 6).Println;varsq2:= Range(7, 14).Println;varsq3:= Range(15, 20).Println;varsq4:= Range(101, 116).Println;
Println;sq1.ZipTuple(sq2, sq3,sq4 ).Println;Метод ZipTuple 41 2 3 4 5 6
7 8 9 10 11 12 13 14
15 16 17 IS 19 20101 102 103 104 105 106 107 10S 109 110 111 112 113 114 115 116(1,7,15,101) (2,S,16,102) (3,9,17,103) (4,10,IS,104) (5,11,19,105) (6,12,20,106)Метод UnZipTupleМетод UnZipTuple возвращает две последовательности, созданные из последова¬
тельности двухэлементных кортежей. Первые элементы кортежей образуют
первую последовательность, а вторые - вторую:function UnZipTuple<T,T1>(Self: sequence of (T,T1)):(sequence of T, sequence of T1); extensionmethodvar sq1 := Range(1, 10)
.Println;
var sq2 := Range(11, 18)
.Println;Println;var zip:= sq1.ZipTuple(sq2)
.Println;Println;var unzip:= zip.UnZipTuple;
Println(unzip);
■ Метод UnZipTupleМетод UnZipTuple1234567S9 1011 12 IB 14 1Б 16 17 IS(1,11) (2,12) (ВДВ) (4,14) (5,15) (6,16) (7,17) (S,1S)
([1,2,3,4,5,6,7,S],[11,12,13,14,15,16,17,IS])VВторой метод UnZipTuple действует так же, но для трёхэлементных кортежей:function UnZipTuple<T,T1,T2>(Self: sequence of (T,T1,T2)):(sequence of T,sequence of T1,
sequence of T2); extensionmethod;var sq1 := Range(1, 6).Pnintln;
var sq2 := Range(7, 14).Pnintln;
var sq3 := Range(15, 20)
.Pnintln;Pnintln;var zip:= sq1.ZipTuple(sq2, sq3)
.Pnintln;Pnintln;var unzip:= zip.UnZipTuple;
Pnintln(unzip);Метод UnZipTuple 31 2 3 4 Б 6
7 S 9 10 11 12 13 1415 16 17 IS 19 20(1,7,15) (2,S,16) (3,9,17) (4,10,IS) (5,11,19) (6,12,20)([1,2,3,4,5,6],[7,S,9,10,11,12],[15,16,17,IS,19,20])V
Третий метод UnZipTuple действует так же, но для четырёхэлементных кортежей:function UnZipTuple<T,T1,T2,T3>(Self: sequence of (T,T1,T2,T3)):(sequence of T,sequence of T1,
sequence of T2,sequence of T3);
extensionmethod;varsq1:= Range(1, 6).Println;varsq2:= Range(7, 14).Println;varsq3:= Range(15, 20).Println;varsq4:= Range(101, 116).Println;Println;var zip:= sq1.ZipTuple(sq2, sq3,sq4 )
.Println;Println;var unzip:= zip.UnZipTuple;
Println(unzip);■ Метод UnZipTuple4AVМетод UnZipTuple 41 2 В 4 Б 67 S 9 10 11 12 IB 14
1Б 16 17 IS 19 20101 102 103 104 10Б 106 107 10S 109 110 111 112 113 114 11Б 116(1,7,15,101) (2,S,16,102) (3,9,17,103) (4,10,18,104) (5,11,19,105) (6,12,20,106)([1,2,3,4,5,6],[7,8,9,10,11,12],[15,16,17,18,19,20],[101,102,103,104,105,106])
Задания для самостоятельного решенияИз книги «Решение задач на языке паскаль»Египетские коровы
Римские адвокаты
Персидские яблоки
Кахунский папирус
Берлинский папирус
Русские яблоки
Турецкие долгожители
Чешские сливы
Французский покупатель *
Болгарские сливы *
Болгарский парикмахер
Немецкий вопрос
Русские гуси
Насос Эдисона
Китайская арифметика
Задача Этьена Безу
Кому сколько лет?Ноги и головы
Немецкая копилка
Ошибки
КоровыПовторяющиеся цифры *
Гимнастический зал
Грузовые машины
КувшинЕщё один кувшин
Собственные методы расширения для
последовательностейВстроенных и дополнительных методов расширения очень много, но для реше¬
ния конкретных задач вам могут потребоваться и такие, которых не в стандартном
наборе. Тогда вы можете написать собственные.Метод RandomElementМетод RandomElement возвращает случайный элемент последовательности:// СЛУЧАЙНЫЙ ЭЛЕМЕНТfunction RandomElement<T>(Self: sequence of T): T; extensionmethod;
beginvar n := Self.Count();var id := PABCSystem.Random(n);Result := Self.ElementAt(id);
end;Если метод что-то возвращает, значит, это функция.Так как это метод расширения, то параметр должен называться Self. Это и есть та
последовательность, для которой мы пишем расширение. Она может содержать
элементы любого типа Т.Тип возвращаемого значения - это тип элементов последовательности, то есть Т.Заканчивается заголовок функции ключевым словом extensionmethod.В теле функции переменная Self - это последовательность, для которой мы пишем
расширение.В остальном методы расширения ничем не отличаются от обычных функций.
Метод готов. Пора создавать последовательность.Пусть это будет ряд натуральных чисел:// последовательность:
var sq:= Range(1,100)
.Println;В цикле for вызываем 10 раз наш метод для этой последовательности:
Println;// случайный элемент:
for var i:= 1 to 10 do
beginvar re:= sq.RandomElement();Println(re);end;10 случайных элементов последовательности получены:■ Собственные методы расширения для последовательностей — □ X1 234Б 678 910111213141Б1617181920212223242Б 262728293031323334ЗБ3637383940414243444Б46474849Б0 515253Р4БББ6Б7Б8Б960616263646Б66676869707172737475 7677787980818283848Б8687888990919293949596979899100837389627 82118ИЗН2Н8
Метод SubSequenceМетод SubSequence возвращает часть последовательности длиной length, начи¬
ная с элемента с индексом start:// ВЫРЕЗКАfunction SubSequence<T>(Self: sequence of T;start, length: integer): sequence of T; extensionmethod;beginResult := Self.Skip(start).Take(length);end;Опять применяем новый метод 10 раз:
Println;// подпоследовательность:
for var i:= 1 to 10 do
beginvar start:= PABCSystem.Random(20);
var subsq:= sq.SubSequence(start, 8);
Println(subsq);
end;■ Собственные методы расширения для последовательностей — □ X1 234 567 091011 121314151617101920212223242526272029В031323334353637 3039404142434445464740495051525354555657505960616263 64656667606970717273747576777079000102S3048506S70089 90919293949596979099100[16,17,10,19,20,21,22,23]I1'2,3,4,5,6,7,S][12ДВ,14,15,16,17,10,19][20, 21,22,23,24,25,26,27][10,19,20,21,22,23,24,25][17,18,19,20,21,22,23,24][20, 21,22,23,24,25,26,27][13,14,15,16,17,10,19,20][20, 21,22,23,24,25,26,27][16,17,10,19,20,21,22,23]
Метод RandomSubSequenceМетод RandomSubSequence объединяет два предыдущих метода - он возвращает
случайную подпоследовательность заданной длины length:// СЛУЧАЙНАЯ ПОДПОСЛЕДОВАТЕЛЬНОСТЬfunction RandomSubSequence<T>(Self: sequence of T;length: integer): sequence of T; extensionmethod;beginvar start := PABCSystem.Random(Self.Count-length+1);Result := Self.Skip(start).Take(length);end;Традиционная проверка нового метода в главном блоке:
Println;// случайная подпоследовательность:for var i:= 1 to 10 dobeginvar rsubsq:= sq.RandomSubSequence(10);
Println(rsubsq);
end;■ Собственные методы расширения для последовательностей — □ X1 23ил67 8910111213 1415161718192021222324252627282930313233343536373839 4041424344454647484950515253545556575859606162636465 6667686970717273747576777879808182838485868788899091 9293949596979899100U1m, 36,37,38,39,40,41,42,43,44][8,9,10,11,12,13,14,15,16,17][31, 32,33,34,35,36,37,38,39,40][65,66,67,68,69,70,71,72,73,74][59,60,61,62,63,64,65,66,67,68][i,2,3,4,5,6,7,8,9,10:[86,87,88,89,90,91,92,93,94,95][28,29,30,31,32,33,34,35,36,37][90,91,92,93,94,95,96,97,98,99][58,59,60,61,62,63,64,65,66,67]
Метод ShuffleОчень часто необходимо перемешать элементы отсортированной последова¬
тельности. Наш метод расширения Shuffle решает эту задачу:// ПЕРЕМЕШИВАЕМ ПОДПОСЛЕДОВАТЕЛЬНОСТЬfunction Shuffle<T>(Self: sequence of T): sequence of T;extensionmethod;beginvar sq:= Self.ToList;Result := sq.OrderBy(x -> PABCSystem.Random(sq.Count));
end;10 замесов - и все случайные:Println;// перемешанная последовательность:for var i:= 1 to 10 dobeginvar sqsh:= sq.Shuffle().Println;
Println;
end;» с обствемные методы расширения для последовательностей — □ X1 2 3 4 5 б 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 3031 32 33 34 35 36 37 38 39 4Ѳ 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 5
7 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 10034 18 48 8 53 55 21 22 56 6 9 33 24 64 73 58 76 49 59 63 2 16 31 79 1 14 95 20 5
7 92 69 87 3 11 54 51 65 68 77 13 32 86 15 61 91 93 98 75 37 83 99 85 17 25 70 82 27 97 10 5 7 80 71 43 19 38 28 41 52 90 40 88 29 67 26 39 4 36 60 96 72 45 84
50 62 81 42 47 23 35 46 78 66 89 100 74 30 94 12 4457 22 67 49 45 75 40 44 58 77 56 8 25 1 46 83 61 64 85 28 5 86 84 100 42 52 76 6
5 27 34 17 18 33 82 26 55 12 72 62 78 91 36 47 50 63 59 90 96 23 2 51 6 74 93 31
70 79 14 4 21 35 68 24 19 7 54 3 73 29 30 39 88 11 99 98 97 60 9 66 16 71 94 1032 41 95 13 92 15 20 81 87 43 53 69 38 80 89 37 4889 64 11 48 3 79 10 40 88 90 36 50 98 1 25 43 6 20 77 93 51 18 68 57 17 61 32 34
55 22 37 80 85 47 76 59 99 8 45 14 24 71 87 42 84 4 31 65 41 13 58 81 52 30 5 9
75 12 23 54 29 73 38 39 72 69 60 7 33 78 96 97 100 66 92 46 53 28 56 70 95 16 63 82 83 2 91 35 27 62 15 21 44 74 26 67 86 49 94 1941 9 17 43 14 44 15 32 64 29 2 48 79 4 78 61 68 59 65 23 21 33 47 82 85 46 18 67
86 62 34 72 50 3 52 73 27 16 36 53 88 81 37 45 35 55 93 13 66 90 91 75 12 20 95
24 6 69 5 11 42 76 92 28 51 87 19 38 70 80 31 96 26 84 63 40 83 58 89 1 54 77 9
7 99 8 49 22 39 57 60 10 74 30 100 7 25 56 98 71 94
Новые методы расширения появляются в подсказке, так пользоваться ими очень
удобно:■АPrirtа [расширение sequence of T)function Shuffle<T>[): sequence of T; |СоPrirtln
. RandGmElemert:= ConsoleColor.Red;Со+v RandomSubSequence^ Reverseo
^ Selecto
^ SelectManyO
^ SequenceEqualo^ Singl&A5>Аналогично вы можете написать и другие методы расширения для любых коллек¬
ций. Например, для массивов и списков.Методы расширения можно писать непосредственно в файле программы или в
отдельном модуле, чтобы пользоваться ими многократно.Модуль OlympUnitПри решении олимпиадных и многих других задач, очень часто нужны функции
для вычисления факториалов, чисел Фибоначчи, для проверки чисел на палин-
дромность, простоту, и так далее. Вполне разумно поместить их в отдельный мо¬
дуль, чтобы все нужные функции всегда были под рукой.OlympUnitДля отладки модуля необходима тестовая программа.Создайте новый консольный проект OlympUnitTest, а также модуль OlympUnit. Со¬
храните их на диске и добавьте файл модуля к тестовому проекту:5
Структура проекта¥ X1/" OlympUГаШвжІ Проект OlympUni™. 'Собрать
Собрать все
Выполнитьuses 5\
typeintFI-.-чіі Подключенаі OlympUnitTes1 OlympUnit.pa:ІйткДобавить ►І_]) Новый файлДобавить сборкуСуществующий файлСвойстваФормуТеперь все файлы удобно разместились в одном проекте:Структура проектаB-lJp Проект OlympUnitTest
Ё|"Ш| Подключенные сборки! ^ OlympUnitTest, раз1 OlympUnit.pasМетод ReverseNumОбычно в задачах используют числа типа integer. Мы напишем для них метод рас¬
ширения, который возвращает исходное число, записанное в обратном порядке:// ПЕРЕВОРАЧИВАЕМ ЧИСЛОfunction ReverseNum(Self: integer): integer; extensionmethod;
beginvar rev := 0;
var num := self;
while (num > 0) do
beginrev := rev * 10 + num mod 10;
num := num div 10;end;Result := rev;end;Для печати целых чисел нам пригодится метод расширения Println:I // ПЕЧАТАЕМ ЧИСЛО ТИПА integer
function Println(Self: integer): integer; extensionmethod;
beginPrintln(Self);Result := Self;
end;Методы простые и понятные, поэтому сразу перейдём к их проверке.А проверка тоже очень простая:Ivar i:= 1234567890.Println;
i.ReverseNum.Println;Сначала мы печатаем заданное число i, затем переворачиваем его и снова печа¬
таем.Рисунок показывает, что новый метод работает верно:■ Методы расширения—□X1234567S909S7654321Иногда в задачах встречаются и очень большие числа, которые имеют тип
Biglnteger. Для них также имеется метод расширения ReverseNum:function ReverseNum(Self: Biglnteger): Biglnteger; extensionme¬
thod;
beginvar rev: Biglnteger := 0;
var num := self;
while (num > 0) do
beginrev := rev * 10 + num mod 10;
num := num div 10;end;Result := rev;end;
И метод печати:// ПЕЧАТАЕМ ЧИСЛО ТИПА Biglntegerfunction Println(Self: Biglnteger): Biglnteger; extensionmethod;
beginPrintln(Self);Result := Self;
end;Проверяем работу методов расширения для больших чисел:I Println;var bi:= BigInteger.Parse('123456789012345678901234567890').Println;
bi.ReverseNum.Println;И это испытание наши методы прошли успешно:■ Методы расширения—□X1234Б678901234Б67890123456789093765432109376543210937654321Метод NumDigitЧисто олимпиадная задача - подсчитать число цифр в заданном числе (длину) -
решается очень просто:// НАХОДИМ ЧИСЛО ЦИФР В ЗАДАННОМ ЧИСЛЕfunction NumDigit(Self: integer): integer; extensionmethod;
beginvar num := Abs(self);
var nd := 0;
while (num > 0) do
beginnd += 1;num := num div 10;
end;
Result := Max(1, nd);
end;function NumDigit(Self: int64): integer; extensionmethod;
beginvar num := Abs(self);
var nd := 0;
while (num > 0) do
beginnd += 1;num := num div 10;
end;Result := Max(1, nd);
end;Берём 10- и 1-значное числа и применяем к ним наш метод расширения:I Println;var nd:= 1234567890.Println.NumDigit.Println;
0.Println.NumDigit.Println;И этот метод считать умеет:■ Методы расширения—□X123456789010АVМетод IsQuadratИ опять олимпиадная задача: проверить, является ли заданное число полным
квадратом.Извлекаем из числа квадратный корень, оставляем целую часть и возводим её в
квадрат. Если получилось исходное число, значит, оно и есть полный квадрат:
function IsQuadrat(Self: integer): boolean; extensionmethod
beginvar r := Trunc(Sqrt(self));Result := r * r = self;
end; 111 Методы расширения —Проверяем метод на первой тысяче чисел.Классическим способом:Println;for var a := 1 to 1000 do
if a.IsQuadrat thenPrintln(a, ' --> полный квад¬
рат');Или функциональным:Println;Range(1,1000).Where(a -> a.IsQuadrat)
.Select(a -> NewLine + a + ' -->
полный квадрат').Println;Результаты в обоих случаях мы получили
одинаковые и правильные:1 --> полный квадрат
4 --> полный квадрат
9 --> полный квадрат16 --> полный квадрат
25 --> полный квадрат
36 --> полный квадрат
49 --> полный квадрат
64 --> полный квадрат
S1 --> полный квадрат100-->ПОЛНЫЙквадрат121- ->полныйквадрат144- ->полныйквадрат169- ->полныйквадрат196- ->полныйквадрат225- ->полныйквадрат256- ->полныйквадрат289- ->полныйквадрат324- ->полныйквадрат361- ->полныйквадрат400- ->полныйквадрат441- ->полныйквадрат4 S4- ->полныйквадрат529- ->полныйквадрат576- ->полныйквадрат625- ->полныйквадрат676- ->полныйквадрат729- ->полныйквадрат7S4- ->полныйквадрат041- ->полныйквадрат900- ->полныйквадрат961- - >полныйквадратМетод IsCubeПодобная задача для кубов.В этом случае извлечь кубический корень сложнее, но не гораздо:
function IsCube(Self: integer): boolean; extensionmethod;
beginvar r := Trunc(Round(Power(self, 1.0 / 3.0)));Result := r * r * r = self;
end;Снова проверяем первую тысячу чисел:Println;Range(1,1000).Where(i -> i.IsCube).Select(a -> NewLine + a + ' --> полный куб')
.Println;И опять наш метод на высоте:ёЗ Методы расширения — □ X1 --> полный куб8 --> полный куб27 -- >полныйкуб64 -- >полныйкуб125полныйкуб216полныйкуб34 Вполныйкуб512полныйкуб729полныйкуб1000- -> полный кубМетод IsPalindromeИ уже совсем олимпиадная задача - проверить, является ли заданное число па
линдромом.Эта задача - самая сложная:I function IsPalindrome(Self: integer): boolean; extensionmethod;
begin// избавляемся от чисел, заканчивающихся на нуль:
if ((self <> 0) and (self mod 10 = 0)) then
beginResult := false;
exit;
end;var rev:= 0;
var num := self;
while (num >= rev) do
beginrev := 10 * rev + num mod 10;
if ( num = rev) then
beginResult := true;
exit;
end;num := num div 10;
if ( num = rev) then
beginResult := true;
exit;
end;
end;Result := false;
end;Проверяем две сотни чисел:Println;Range(1,199).Where(a -> a.IsPalind-
rome).Select(a -> NewLine + a
+ ' --> палиндром')
.Println;И здесь всё сходится.■ Методы расширения — □ X1 палиндромЛ2 палиндром3 --> палиндром4 палиндром5 --> палиндром6 палиндром7 палиндром8 палиндром9 палиндром11 палиндром22 палиндром33 палиндром44 палиндромББ палиндром66 палиндром77 палиндром33 палиндром99 --> палиндром101 палиндром111 --> палиндром121 палиндром131 палиндром141 палиндром1Б1 палиндром161 палиндром171 палиндром1S1 палиндром191 палиндромV
При запуске тестовой программы в папке создаётся файл OlympUnit.pcu. Это ском¬
пилированный модуль, который нужно скопировать в папку с программой, кото¬
рая его использует. А в начале самой программы добавьте модуль в раздел uses:і uses OlympUnit;Здесь мы рассмотрели только те методы расширения, которые использовали при
решении задач в этой книге. Но вы можете добавить в олимпиадный модуль и
собственные методы. Тогда решение даже сложных задач станет более интерес¬
ным и простым.