en
Feedback
PascalABC.NET официальный канал

PascalABC.NET официальный канал

Open in Telegram

Официальный канал языка и системы программирования PascalABC.NET

Show more
1 860
Subscribers
-124 hours
-117 days
-2630 days
Posts Archive
📚 Анализ больших текстов в PascalABC.NET, Использование словарей 📚 Сегодня расскажем, как PascalABC.NET позволяет легко ана
📚 Анализ больших текстов в PascalABC.NET, Использование словарей 📚 Сегодня расскажем, как PascalABC.NET позволяет легко анализировать большой текст и получать ценную статистику! Взяв для примера текст "Властелин колец", мы сможем узнать, какие слова разной длины встречаются наиболее часто. Рассмотрим следующий код:
begin
  var a := ReadAllText('LordOfTheRings.txt').ToLower.ToWords(AllDelimiters)
    .Where(w -> w[1] in 'а'..'я');
  for var i:=2 to 10 do
    a.Where(w -> w.Length = i)
    .EachCount
    .OrderByDescending(kv -> kv.Value).Take(5).Println;
end.
👀 Что делает этот код? 🔹 Загружает текст из файла, приводит его к нижнему регистру и разбивает на слова, используя константу AllDelimiters как разделители. 🔹 Оставляет только те слова, которые начинаются с русских букв. 🔹 С помощью метода EachCount получает словарь частот слов заданной длины. 🔹 Сортирует слова по их длине (от 2 до 10 символов) и подсчитывает частоту каждого слова. 🔹 Выводит самые популярные слова для каждой длины, помогая понять, какие слова наиболее часто встречаются. ✨ Почему это удобно? Работа с текстом в PascalABC.NET позволяет: 🔹 Легко анализировать большие текстовые данные, 🔹 Использовать встроенные методы Where, EachCount, OrderByDescending, Println и многие другие для фильтрации и сортировки данных, 🔹 Проводить быстрый и наглядный анализ, который можно адаптировать под любой текст. Попробуйте адаптировать этот пример под свои тексты и узнайте, как часто и какие слова встречаются!

🔹 Лямбда-выражения в PascalABC.NET: преобразования, условия и проекции Лямбда-выражения в PascalABC.NET дают возможность упростить и улучшить код. Важно понимать, что лямбда-выражения делятся на три основные группы: лямбда-преобразования, лямбда-условия и лямбда-проекции. Давайте разберем каждый вид с примерами. 1. Лямбда-преобразования Лямбда-преобразование используется для преобразования данных. Оно может оставлять тип данных без изменений или переводить элементы в другой тип. Рассмотрим оба варианта: Преобразование к тому же типу. В этом примере каждый элемент массива возводится в квадрат, оставаясь числом.
var a := Arr(1, 2, 3, 4, 5);
var squares := a.Select(x -> x * x); // Числа преобразуются в их квадраты
a := squares.ToArray; // Результат можно присвоить тому же массиву
Println(squares); // Вывод: [1, 4, 9, 16, 25]
Преобразование к другому типу. Следующий пример показывает перевод чисел в строки, представляя каждое число текстом:
var a := Arr(1, 2, 3, 4, 5);
var strValues := a.Select(x -> x.ToString); // Числа преобразуются в строки
Println(strValues); // Вывод: ['1', '2', '3', '4', '5']
В обоих случаях метод Select позволяет эффективно преобразовывать данные: в первом случае, чтобы получить квадраты чисел, во втором — текстовое представление чисел. 2. Лямбда-условия Лямбда-условия применяются для фильтрации элементов по критерию. В примере ниже отбираются только четные числа:
var a := Arr(1, 2, 3, 4, 5, 6);
var evenNumbers := a.Where(x -> x mod 2 = 0); // Отбираются только чётные числа
Println(evenNumbers); // Вывод: [2, 4, 6]
Метод Where использует условие x -> x mod 2 = 0, чтобы выбрать только числа, делящиеся на 2 без остатка. 3. Лямбда-проекции Лямбда-проекция применяется, когда нужно извлечь часть составного объекта. В примере ниже из массива кортежей извлекаются только названия фруктов:
var items := Arr(('apple', 1.5), ('banana', 1.2), ('cherry', 0.9));
var fruitNames := items.Select(x -> x[0]); // Извлекается только название фрукта
Println(fruitNames); // Вывод: ['apple', 'banana', 'cherry']
Метод Select создает проекцию x -> x[0], возвращая первую часть кортежа x. Подводим итоги: ✦ Лямбда-преобразования помогают изменить значение элемента, сохраняя или меняя его тип. ✦ Лямбда-условия отбирают данные по критерию. ✦ Лямбда-проекции полезны для извлечения частей составных объектов. Пользуйтесь этими возможностями, чтобы сделать ваш код лаконичнее и понятнее! 🚀

Работа с форматом JSON Дорогие подписчики! Сегодня мы расскажем о том, как легко и удобно работать с форматом JSON в PascalAB
Работа с форматом JSON Дорогие подписчики! Сегодня мы расскажем о том, как легко и удобно работать с форматом JSON в PascalABC.NET с использованием библиотеки Newtonsoft.Json. JSON (JavaScript Object Notation) — это текстовый формат для представления структурированных данных на основе синтаксиса JavaScript. Он стал стандартом де-факто для обмена данными в веб-приложениях и широко используется благодаря своей простоте и удобству. 💻 Предлагаем вашему вниманию небольшой пример кода, который демонстрирует разбор сложной JSON-структуры и вывод ее содержимого в консоль с русскими названиями полей. Обратите внимание на легкость доступа к вложенным объектам и массивам:
uses Newtonsoft.Json.Linq;

begin
  // Считываем содержимое JSON из файла
  var jsonString := ReadAllText('data.json');
  
  // Разбор JSON-строки в объект JObject
  var jsonObject := JObject.Parse(jsonString);
  
  // Доступ к верхнему уровню объекта "человек"
  var person := jsonObject['человек'];
  
  // Извлечение простых значений
  var name := person['имя'].ToString;
  var age := person['возраст'].ToObject&<integer>;
  var isStudent := person['являетсяСтудентом'].ToObject&<boolean>;
  
  // Доступ к вложенному объекту "контакты"
  var contacts := person['контакты'];
  var email := contacts['электроннаяПочта'].ToString;
  var phone := contacts['телефон'].ToString;
  
  // Доступ к массиву "хобби"
  var hobbies := person['хобби'];
  var hobbyList := hobbies.ToObject&<array of string>;

  // Вывод извлеченных данных с ручным выравниванием
  Println($'Имя:                {name}');
  Println($'Возраст:            {age}');
  Println($'Является студентом: {isStudent}');
  Println($'Электронная почта:  {email}');
  Println($'Телефон:            {phone}');
  
  // Объединение элементов массива хобби в строку и вывод
  Println($'Хобби:              {hobbyList.JoinToString('', '')}');
end.
В этом примере форматированный вывод поможет вам легко читать данные, а использование русских названий для полей делает код более понятным. 🌟 Файл data.json имеет следующее содержимое:
{
  "человек": {
    "имя": "Иван",
    "возраст": 30,
    "являетсяСтудентом": false,
    "контакты": {
      "электроннаяПочта": "ivan@example.com",
      "телефон": "123-456-7890"
    },
    "хобби": ["чтение", "путешествия", "плавание"]
  }
}
🚀 Пробуйте работать с JSON в своих проектах на PascalABC.NET и делитесь своими впечатлениями в комментариях! Мы всегда рады услышать ваше мнение и поддержать новые идеи. 💬 #PascalABCNET #JSON #DataParsing

Присваивание и сравнение динамических массивов Работа с массивами в PascalABC.NET имеет свои особенности, особенно если речь
Присваивание и сравнение динамических массивов Работа с массивами в PascalABC.NET имеет свои особенности, особенно если речь идёт о динамических массивах. В этой новости разберём, как правильно присваивать, копировать и сравнивать такие массивы. 🔹 Присваивание динамических массивов Когда вы присваиваете один динамический массив другому через оператор :=, происходит ссылочное присваивание. Это означает, что обе переменные ссылаются на один и тот же массив в памяти. Любые изменения в одном массиве автоматически отразятся на другом. Пример:
var a := new integer[5]; // Создаём массив
var b := a; // b ссылается на тот же массив,что и a
b[0] := 10; // Теперь и a[0], и b[0] равны 10
Чтобы создать независимую копию массива, следует использовать встроенную функцию Copy(a). Пример:
var a := new integer[5]; // Создаём массив
var b := Copy(a);        // b — это копия массива a
b[0] := 10;  // Изменение b не затрагивает a
🔹 Сравнение массивов ✦ Прямое сравнение массивов через оператор = проверяет ссылки, а не содержимое массивов. Даже если два массива содержат одинаковые элементы, они будут считаться неравными, если ссылаются на разные объекты в памяти. Пример
var a, b: array of integer;
a := new integer[5];
b := new integer[5];
if a = b then // это сравнение вернёт False
  Println('Массивы равны')
else
  Println('Массивы не равны');
✦ Для корректного сравнения содержимого массивов в PascalABC.NET нужно использовать метод ArrEqual, который проверяет, равны ли все элементы двух массивов. Пример:
var a := new integer[5];
var b := new integer[5];
if a.ArrEqual(b) then  // Проверяет равенство всех элементов массива
  Println('Массивы равны')
else
  Println('Массивы не равны');
🔹 Итоги: ✦ Для копирования массива используйте функцию Copy(a), чтобы избежать ссылочного присваивания. ✦ Для сравнения содержимого массивов используйте метод ArrEqual. Это поможет вам избежать ошибок при работе с динамическими массивами и сделает ваш код более предсказуемым и надёжным! 🚀

Операции с массивами Операции + и * с массивами позволяют быстро и эффективно конструировать массивы, заполняя их значениями.
Операции с массивами Операции + и * с массивами позволяют быстро и эффективно конструировать массивы, заполняя их значениями. Для непрерывного диапазона элементов помогает также конструкция Arr(1..3). Задание - из реального урока в компьютерной школе, под контролем системы невидимой автоматической проверки.

Сортировка выбором - минимизация обменов Алгоритм сортировки выбором является одним из самых простых для понимания, но его мо
Сортировка выбором - минимизация обменов Алгоритм сортировки выбором является одним из самых простых для понимания, но его можно улучшить! В стандартной реализации количество обменов может быть избыточным, так как обмен выполняется всегда, даже если текущий элемент уже на своём месте. В улучшенной версии сортировки выбором проверяется, найдено ли новое минимальное значение, и обмен выполняется только в случае необходимости. Это позволяет сократить число обменов, что особенно полезно для массивов, которые уже частично отсортированы. Новый алгоритм выглядит так:
procedure SortByChoice(a: array of integer);
begin
  var n := a.Length;
  for var i := 0 to a.Length - 2 do
  begin
    var imin := i;

    for var j := i + 1 to n - 1 do
      if a[j] < a[imin] then
        imin := j;

    if imin <> i then
      Swap(a[imin], a[i]);
  end;
end;
🔥 Теперь алгоритм работает эффективнее за счёт уменьшения количества обменов! 🔥 Пробуйте и внедряйте это улучшение в свои программы! 🚀

Copilot - задача о поиске двух первых минимумов в массиве Два запроса - два отличных ответа! begin var arr := ArrRandomIntege
Copilot - задача о поиске двух первых минимумов в массиве Два запроса - два отличных ответа!
begin
  var arr := ArrRandomInteger(10);
  
  var min1, min2 := (MaxInt, MaxInt);
  
  foreach var x in arr do
    if x < min1 then
      (min1, min2) := (x, min1)
    else if x < min2 then
      min2 := x;
  
  Println('Сгенерированный массив: ', arr);
  Println('Первый минимум: ', min1);
  Println('Второй минимум: ', min2);
end.
и
begin
  var arr := ArrRandomInteger(10);
  
  var min1 := arr.Min();
  var min2 := arr.Where(x -> x <> min1).Min();
  
  Println('Сгенерированный массив: ', arr);
  Println('Первый минимум: ', min1);
  Println('Второй минимум: ', min2);
end.

Логирование в PascalABC.NET ✦ Устанавливаем пакет NLog командой nuget install NLog. ✦ Копируем dll в папку проекта ✦ Создаем
Логирование в PascalABC.NET ✦ Устанавливаем пакет NLog командой nuget install NLog. ✦ Копируем dll в папку проекта ✦ Создаем конфигурационный файл nlog.config:
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <targets>
    <target xsi:type="File" name="logfile" fileName="log.txt" />
  </targets>
  <rules>
    <logger name="*" minlevel="Info" writeTo="logfile" />
  </rules>
</nlog>
✦ Просим Copilot написать программу для логирования:
{$reference 'NLog.dll'}

uses NLog;

begin
  // Инициализация логгера
  LogManager.LoadConfiguration('nlog.config');
  var log := LogManager.GetCurrentClassLogger;
  
  // Запись логов
  log.Info('Программа запущена');
  
  try
    var a := 10;
    var b := 0;
    var c := a div b; // Это вызовет исключение
  except
    on e: Exception do
      log.Error('Ошибка: ' + e.Message);
  end;
  
  log.Info('Программа завершена');
end.
Вуаля!

Вычисление средних оценок студентов Дан словарь оценок студентов. Вычислить для каждого студента среднюю оценку. Результат за
Вычисление средних оценок студентов Дан словарь оценок студентов. Вычислить для каждого студента среднюю оценку. Результат записать в новый словарь.

Трансформация части элементов массива На скриншоте показана программа, в которой преобразуется часть элементов массива, удовл
Трансформация части элементов массива На скриншоте показана программа, в которой преобразуется часть элементов массива, удовлетворяющая некоторому условию. Для задания условия используется лямбда с условной операцией.

Используй принцип локальности при описании переменных! Сегодня напомним об одном важном принципе, который делает ваш код боле
Используй принцип локальности при описании переменных! Сегодня напомним об одном важном принципе, который делает ваш код более чистым, понятным и безопасным — принципе локальности при описании переменных. Принцип локальности подразумевает, что переменные должны объявляться как можно ближе к месту их использования, а не в самом начале программы или функции. Это помогает избежать путаницы и ошибок при работе с переменными, особенно в больших и сложных проектах. 🔹 Почему это важно? ✦ Читаемость: Когда переменные объявляются рядом с кодом, который их использует, легче понять, для чего они нужны. ✦ Минимизация ошибок: Переменные, объявленные локально, реже влияют на другие части программы, что уменьшает шанс на случайные ошибки. ✦ Упрощение отладки: Когда переменная используется только в одном небольшом блоке кода, легче отследить, как она изменяется и откуда берутся значения. ✦ Экономия памяти: Локальные переменные занимают память только в пределах своего блока, что делает код более эффективным.

Использование атрибутов Combinatorial, Values и RangeAttribute при Unit-тестировании Атрибуты Combinatorial, Values и RangeAt
Использование атрибутов Combinatorial, Values и RangeAttribute при Unit-тестировании Атрибуты Combinatorial, Values и RangeAttribute позволяют легко задавать разные наборы данных для тестов. 🔹 Combinatorial Этот атрибут помогает комбинировать несколько наборов данных, применяемых к параметрам теста. 🔹 Values С помощью атрибута Values можно указать несколько значений для одного параметра теста. Когда тест запускается, он выполняется с каждым из заданных значений, что позволяет проверить работу функции сразу на множестве входных данных. 🔹 RangeAttribute Атрибут RangeAttribute позволяет указать диапазон значений, которые будут использоваться для параметра теста. Использование этих атрибутов особенно полезно для тестирования методов с числовыми параметрами, чтобы покрыть различные диапазоны входных данных.

🧪 Unit-тестирование в PascalABC.NET Один из лучших способов убедиться, что ваш код работает корректно, — это Unit-тестирован
🧪 Unit-тестирование в PascalABC.NET Один из лучших способов убедиться, что ваш код работает корректно, — это Unit-тестирование. В PascalABC.NET вы можете использовать популярный инструмент для тестирования — NUnit, который интегрирован в модуль NUnitABC. Изображенную на скрине программу надо запустить, используя Ctrl-Shift-T. Тестирование с помощью NUnit — это не просто проверка кода, а способ сделать ваш проект стабильным, качественным и лёгким в поддержке. Внедряйте Unit-тестирование в ваш рабочий процесс, и ваши приложения станут надёжнее! 🚀

😈 Вредные советы для программистов на PascalABC.NET 😈 🎃 Совет 1: Никогда не проверяй код на ошибки. Если программа откомпилировалась — значит, всё работает идеально! А баги? Это фантазии пользователей. 🎃 Совет 2: Прояви немного уважения к программистам прошлого — объявляй все переменные до beginа основной программы. Зачем их размещать в блоке begin-end? Пусть поиск места первого присваивания такой переменной станет увлекательным квестом! 🎃 Совет 3: Используй глобальные переменные как можно больше! Глобальные переменные очень удобны, т. к. к ним можно обращаться отовсюду! И чем больше глобальных переменных — тем веселее искать причину ошибок! 🎃 Совет 4: Никогда не пиши комментарии. Пусть будущее поколение программистов с удивлением гадает, зачем этот код вообще существует. 🎃 Совет 5: Лишние скобки? Не заморачивайся, всё равно PascalABC.NET их поймёт! А ещё лучше — перемешивай их везде, где можно. Чем больше скобок, тем надёжнее программа! 🎃 Совет 6: Пиши всё в одну строку! Пробелы и отступы — для слабаков. Пусть коллеги попробуют разобрать этот шедевр без форматирования! И так код будет быстрее компилироваться! 🎃 Совет 7: Обязательно используй названия переменных вида a, b, c. Чем короче имя — тем быстрее работаешь. А тот, кто разберётся, что такое x9, будет настоящим героем! 🎃 Совет 8: Никогда не обновляй PascalABC.NET! Работает старая версия 2010 года? Зачем тогда что-то менять? Современные фишки — это излишество. 🎃 Совет 9: Писать тесты? Забудь! Тесты — для слабаков. Настоящие программисты сразу выкатывают код в продакшен! 🎃 Совет 10: Используй магические числа вместо констант. Пусть твой код выглядит загадочно и мистически! Никто не должен догадаться, зачем ты использовал 42 в каждом цикле. 🎃 Совет 11: Если что-то не работает, то, скорее всего, глючит компилятор. Попробуйте поменять местами некоторые переменные и строки кода. 🎃 Совет 12: Не пользуйся стандартной библиотекой языка! Что может быть интереснее, чем написать собственный алгоритм сортировки или собственное бинарное дерево? В своём коде уж точно не будет ошибок, а вот в стандартных функциях - ещё неизвестно! 🎃 Совет 13: Смело сравнивай числа с плавающей точкой с помощью операции =. Раз есть такая операция, значит ей нужно пользоваться. 🎃 Совет 14: Зачем инициализировать переменные, если там и так нули? Я вот недавно не инициализировал, и там ноль был. Всё работало. 🎃 Совет 15: Нет времени думать, копируй код многократно! Повторение — мать учения! 🎃 Совет 16: Выравнивание и единый стиль не дают раскрыться вашей индивидуальности и креативности. Это притеснение свободы личности и самовыражения. Каждый должен оформлять код так, как ему нравится. 🎃 Совет 17: Экономь память. Используй одну переменную для разных целей! 🎃 Совет 18: Статический массив — лучшее решение. И вообще, выделение памяти — зло. array[1..256] хватит всем, а если не хватит, то потом поменяем на 512. В крайнем случае – на 1024. 🎃 Совет 19: Если что-то не работает — просто добавь больше if-else. Логика обязательно сложится, а если нет, добавь ещё несколько уровней условий! 🎃 Совет 20: Настоящие программисты программируют только на PascalABC.NET. В крайнем случае на C++. И обязательно ругают Python! 😎 Эти советы точно помогут вам стать супер-программистом и на голову превзойти всех конкурентов! Научившись применять эти рекомендации на PascalABC.NET, используйте их затем и для других языков — они универсальны!

Снежинка Коха Сегодня поговорим о создании одного из самых известных фракталов — снежинки Коха. uses Turtle; procedure Koch(s
Снежинка Коха Сегодня поговорим о создании одного из самых известных фракталов — снежинки Коха.
uses Turtle;

procedure Koch(sz: real; n: integer);
begin
  if n = 0 then
    Forw(sz)
  else begin
    Koch(sz/3,n-1); Turn(-60);
    Koch(sz/3,n-1); Turn(120);
    Koch(sz/3,n-1); Turn(-60);
    Koch(sz/3,n-1);
  end;
end;

begin
  Window.Title := 'Снежинка Коха';
  Turn(90);
  Forw(-10);
  Down;
  Koch(20,5);
end.
Программа построения снежинки Коха использует модуль Turtle. Алгоритм заключается в следующем: Базовый случай: Если глубина рекурсии n=0, то рисуем отрезок длиной sz. Рекурсивный случай: Если n>0: ✦ Разбиваем отрезок на три части. ✦ Рисуем первую треть отрезка и поворачиваем на 60° влево. ✦ Рисуем первую часть писка и поворачиваем на 120° вправо. ✦ Рисуем вторую часть пика и поворачиваем на 60° влево ✦ Рисуем последнюю треть отрезка. Процесс повторяется для каждой стороны, добавляя всё больше деталей на каждом шаге. #рекурсия #графика

Библиотеки в PascalABC.NET Вот слайд с сегодняшней лекции по библиотекам. Библиотеки отличаются от модулей по нескольким пара
Библиотеки в PascalABC.NET Вот слайд с сегодняшней лекции по библиотекам. Библиотеки отличаются от модулей по нескольким параметрам.

🔍 Метод Ньютона: Быстрый способ нахождения корней уравнений Метод Ньютона — популярный численный метод нахождения корней ура
🔍 Метод Ньютона: Быстрый способ нахождения корней уравнений Метод Ньютона — популярный численный метод нахождения корней уравнений с квадратичной сходимостью. Метод Ньютона используется для нахождения корня функции, исходя из начального приближения, и работает за счет последовательных касательных к графику функции. На каждой итерации приближение улучшается, что позволяет с большой точностью находить корни сложных уравнений. Метод Ньютона работает гораздо быстрее метода половинного деления. Метод Ньютона особенно эффективен для задач, где производная функции не равна нулю в окрестности корня.
function f(x: real) := exp(x) - 4; 
function df(x: real) := exp(x);    // Производная f'(x) = exp(x)

function NewtonMethod(x, eps: real): real;
begin
  while abs(f(x)) > eps do
    x := x - f(x) / df(x); 
  Result := x;
end;

begin
  var x := 0.5;       // Начальное приближение
  var eps := 0.00001; 

  var root := NewtonMethod(x, eps);
  
  Println('Корень уравнения: ', root);
end.

📝 Метод половинного деления: простой и эффективный способ нахождения корней уравнений Один из классических численных методов
📝 Метод половинного деления: простой и эффективный способ нахождения корней уравнений Один из классических численных методов — метод половинного деления. Этот метод позволяет находить корни нелинейных уравнений с высокой точностью. Его особенность заключается в том, что он работает на отрезке, где функция непрерывна и меняет знак, и последовательно делит этот отрезок пополам, приближаясь к решению. Метод прост в реализации и гарантированно сойдется, если на выбранном интервале есть корень.
function f(x: real) := exp(x) - 4; 

function BisectionMethod(a, b, eps: real): real;
begin
  var fa := f(a); // Вычисляем f(a) один раз в начале
  while Abs(b - a) > eps do
  begin
    var c := (a + b) / 2;
    var fc := f(c); 
    if fc = 0 then
      break; 
    if fa * fc < 0 then
      b := c
    else
      (a, fa) := (c, fc); 
  end;
  Result := (a + b) / 2; 
end;

begin
  var (a, b) := (0.0, 3.0); 
  var eps := 0.000001;
  
  var root := BisectionMethod(a, b, eps);
  
  Println('Корень уравнения:', root);
end.

Реализация класса SortedMultiset Перед вами - реализация класса мультимножества, реализованная ChatGPT. Идея использовать для реализации класс SortedDictionary с подсчетом количества дублей также принадлежит ChatGPT. Отличное решение! Операции добавления, удаления, поиска и получения минимального/максимального элемента выполняются за O(log(n)) благодаря использованию сбалансированного дерева поиска, которое реализовано в SortedDictionary.
type
  SortedMultiset<T> = class(IEnumerable<T>)
  private
    data: SortedDictionary<T, integer>; // Храним элемент и количество его вхождений

  public
    // Конструктор
    constructor Create;
    begin
      data := new SortedDictionary<T, integer>();
    end;

    // Метод добавления элемента
    procedure Add(x: T);
    begin
      if data.ContainsKey(x) then
        data[x] := data[x] + 1 // Увеличиваем количество вхождений
      else
        data.Add(x, 1); // Добавляем новый элемент с количеством 1
    end;

    // Метод удаления элемента
    function Remove(x: T): boolean;
    begin
      if data.ContainsKey(x) then
      begin
        if data[x] > 1 then
          data[x] := data[x] - 1 // Уменьшаем количество вхождений
        else
          data.Remove(x); // Удаляем элемент, если это последнее вхождение
        Result := true;
      end
      else
        Result := false;
    end;

    // Метод получения количества вхождений элемента
    function Count(x: T): integer;
    begin
      if data.ContainsKey(x) then
        Result := data[x]
      else
        Result := 0;
    end;

    // Метод для получения минимального элемента
    function Min: T;
    begin
      if data.Count = 0 then
        raise new System.InvalidOperationException('Мультимножество пусто.');
      Result := data.Keys.First(); // Первый элемент в отсортированном множестве
    end;

    // Метод для получения максимального элемента
    function Max: T;
    begin
      if data.Count = 0 then
        raise new System.InvalidOperationException('Мультимножество пусто.');
      Result := data.Keys.Last(); // Последний элемент в отсортированном множестве
    end;

    // Метод для получения всех элементов множества
    function GetElements: sequence of T;
    begin
      foreach var key in data.Keys do
        for var i := 1 to data[key] do
          yield key;
    end;

    // Реализация интерфейса IEnumerable<T>
    function GetEnumerator: IEnumerator<T>;
    begin
      Result := GetElements().GetEnumerator();
    end;

    function System.Collections.IEnumerable.GetEnumerator: System.Collections.IEnumerator;
    begin
      Result := GetElements().GetEnumerator();
    end;
    
    // Метод для вывода множества
    procedure Print;
    begin
      foreach var e in self do
        Write(e, ' ');
      Writeln;
    end;
  end;

// Пример использования
begin
  var multiset := new SortedMultiset<integer>();
  multiset.Add(5);
  multiset.Add(3);
  multiset.Add(5);
  multiset.Add(2);
  
  multiset.Print(); // Вывод: 2 3 5 5
  
  multiset.Remove(5);
  multiset.Print(); // Вывод: 2 3 5
  
  Writeln('Min: ', multiset.Min); // Вывод: Min: 2
  Writeln('Max: ', multiset.Max); // Вывод: Max: 5
  Writeln('Количество 5: ', multiset.Count(5)); // Вывод: Количество 5: 1

  multiset.Println;
end.

Алгоритм выбора первых k максимумов В данном алгоритме, частично сгенерированным ChatGpt, приведен алгоритм получения первых
Алгоритм выбора первых k максимумов В данном алгоритме, частично сгенерированным ChatGpt, приведен алгоритм получения первых k максимумов в массиве. Используется минимальная куча, моделируемая SortedSet. Поскольку SortedSet удаляет дубли, то в конце приходится снова проходиться по массиву и отбирать все элементы, совпадающие с элементами кучи. Асимптотическая сложность при этом остаётся равной O(n log(k)). Алгоритм привлекает своей простотой
function GetTopKMaximums(arr: array of integer; k: integer): array of integer;
begin
  var minHeap := new SortedSet<integer>(arr.Take(k));

    for var i := k to arr.Length - 1 do
    if arr[i] > minHeap.Min then
    begin
      minHeap.Remove(minHeap.Min); 
      minHeap.Add(arr[i]); 
    end;

  // Преобразуем кучу в массив
  Result := arr.Where(x -> x in minHeap).OrderDescending.ToArray
end;

begin
  var arr := |3, 1, 4, 2, 5, 9, 7, 7, 6|;
  var k := 4; 

  var topKMaxs := GetTopKMaximums(arr, k);
  Print($'Первые {k} максимума(ов):', topKMaxs);
end.
#алгоритмы