<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>@pascalabcnet</title><generator>teletype.in</generator><description><![CDATA[@pascalabcnet]]></description><link>https://teletype.in/@pascalabcnet?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/pascalabcnet?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/pascalabcnet?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Sat, 18 Apr 2026 15:29:08 GMT</pubDate><lastBuildDate>Sat, 18 Apr 2026 15:29:08 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@pascalabcnet/NWise</guid><link>https://teletype.in/@pascalabcnet/NWise?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet</link><comments>https://teletype.in/@pascalabcnet/NWise?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet#comments</comments><dc:creator>pascalabcnet</dc:creator><title>Метод NWise в PascalABC.NET</title><pubDate>Tue, 26 Nov 2024 16:11:17 GMT</pubDate><description><![CDATA[Метод NWise в PascalABC.NET помогает разбивать последовательности на подгруппы фиксированной длины. Он возвращает последовательность массивов, где каждый массив содержит соседние элементы исходной последовательности. Это удобно для задач анализа временных данных, обработки текстов или любых последовательностей.]]></description><content:encoded><![CDATA[
  <p id="rciL">Метод NWise в PascalABC.NET помогает разбивать последовательности на подгруппы фиксированной длины. Он возвращает последовательность массивов, где каждый массив содержит соседние элементы исходной последовательности. Это удобно для задач анализа временных данных, обработки текстов или любых последовательностей.</p>
  <h3 id="od0Y">Пример работы:</h3>
  <pre id="5MBA" data-lang="pascal">var a := Arr(1, 2, 3, 4, 5, 6);
foreach var group in a.NWise(3) do
  Println(group); 
// Результат: [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]</pre>
  <h3 id="08Fy">📝 Примеры задач для анализа временных рядов:</h3>
  <p id="r9U1">1️⃣ <strong>Скользящее среднее температуры</strong><br />Вычислим средние значения температуры за последние 3 дня:</p>
  <pre id="OdOx" data-lang="pascal">var temperatures := Arr(22.1, 23.4, 21.8, 24.2, 22.9, 23.7);
foreach var window in temperatures.NWise(3) do
  Println(window.Average.Round(4)); 
// Результат: 22.4333, 23.1333, 22.9667, 23.6</pre>
  <p id="ycYF">2️⃣ <strong>Обнаружение резких изменений</strong><br />Найдем периоды, где изменения потребления электроэнергии превышают 50 кВт⋅ч:</p>
  <pre id="Fj2m" data-lang="pascal">var consumption := Arr(120, 180, 150, 200, 260, 220);
foreach var window in consumption.NWise(2) do
  if Abs(window[1] - window[0]) &gt; 50 then
    Println(window);
// Результат: [120, 180], [200, 260]</pre>
  <p id="Tb2H">3️⃣ <strong>Максимальная скорость ветра</strong><br />Определим максимальную скорость ветра за каждые 3 часа:</p>
  <pre id="mfqU" data-lang="pascal">var windSpeeds := Arr(5, 8, 6, 10, 12, 9, 7);
foreach var window in windSpeeds.NWise(3) do
  Println(window.Max); 
// Результат: 8, 10, 12, 12, 12</pre>
  <p id="veIl">4️⃣ <strong>Суммирование данных в окнах</strong><br />Подсчитаем общее количество запросов к серверу за каждые 4 часа:</p>
  <pre id="R9mU" data-lang="pascal">var requests := Arr(50, 60, 70, 80, 90, 100, 110);
foreach var window in requests.NWise(4) do
  Println(window.Sum);
// Результат: 260, 300, 340, 380</pre>
  <p id="FmLP">5️⃣ <strong>Выявление стабильных периодов</strong><br />Найдем периоды, где изменения курса валют минимальны (разница между максимумом и минимумом в окне ≤ 1):</p>
  <pre id="8XRP" data-lang="pascal">var exchangeRates := Arr(74.5, 74.6, 74.8, 74.7, 74.5, 74.4, 74.6);
foreach var window in exchangeRates.NWise(3) do
  if window.Max - window.Min &lt;= 1 then
    Println(window);
// Результат: [74.6, 74.8, 74.7], [74.5, 74.4, 74.6]</pre>
  <p id="rXo5">6️⃣ <strong>Выявление трендов</strong><br />Определим окна, где значения постоянно растут:</p>
  <pre id="nXc1" data-lang="pascal">var sales := Arr(100, 120, 140, 130, 150, 160, 170);
foreach var window in sales.NWise(3) do
  if (window[0] &lt; window[1]) and (window[1] &lt; window[2]) then
    Println(window);
// Результат: [100, 120, 140], [130, 150, 160], [150, 160, 170]</pre>
  <h3 id="pvIS">Вывод</h3>
  <p id="MXjT">В настоящей статье представлены различные примеры использования метода NWise в прикладных задачах.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@pascalabcnet/NLog</guid><link>https://teletype.in/@pascalabcnet/NLog?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet</link><comments>https://teletype.in/@pascalabcnet/NLog?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet#comments</comments><dc:creator>pascalabcnet</dc:creator><title>Логирование в PascalABC.NET</title><pubDate>Wed, 23 Oct 2024 07:43:07 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/1f/8e/1f8ee929-1df0-4a3d-a4d7-89bc6079de10.png"></media:content><category>PascalABC.NET</category><description><![CDATA[<img src="https://img4.teletype.in/files/f2/3e/f23ec7f2-0a06-44ef-81ef-e09714500762.png"></img>✦ Устанавливаем пакет NLog командой nuget install NLog. 
✦ Копируем dll в папку проекта
✦ Создаем конфигурационный файл nlog.config:]]></description><content:encoded><![CDATA[
  <p id="MTTu">✦ Устанавливаем пакет NLog командой nuget install NLog. <br />✦ Копируем dll в папку проекта<br />✦ Создаем конфигурационный файл nlog.config:</p>
  <pre id="e95g">&lt;nlog xmlns=&quot;http://www.nlog-project.org/schemas/NLog.xsd&quot;
      xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;&gt;
  &lt;targets&gt;
    &lt;target xsi:type=&quot;File&quot; name=&quot;logfile&quot; fileName=&quot;log.txt&quot; /&gt;
  &lt;/targets&gt;
  &lt;rules&gt;
    &lt;logger name=&quot;*&quot; minlevel=&quot;Info&quot; writeTo=&quot;logfile&quot; /&gt;
  &lt;/rules&gt;
&lt;/nlog&gt;</pre>
  <p id="4pSX">✦ Пишем программу для логирования:</p>
  <pre id="Wcch" data-lang="pascal">{$reference &#x27;NLog.dll&#x27;}
uses NLog;
begin
  // Инициализация логгера
  LogManager.LoadConfiguration(&#x27;nlog.config&#x27;);
  var log := LogManager.GetCurrentClassLogger;
  
  // Запись логов
  log.Info(&#x27;Программа запущена&#x27;);
  
  try
    var a := 10;
    var b := 0;
    var c := a div b; // Это вызовет исключение
  except
    on e: Exception do
      log.Error(&#x27;Ошибка: &#x27; + e.Message);
  end;
  
  log.Info(&#x27;Программа завершена&#x27;);
end.</pre>
  <p id="rSUz">Готово!</p>
  <figure id="Y7IB" class="m_column">
    <img src="https://img4.teletype.in/files/f2/3e/f23ec7f2-0a06-44ef-81ef-e09714500762.png" width="1280" />
  </figure>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@pascalabcnet/AssCompDynArr</guid><link>https://teletype.in/@pascalabcnet/AssCompDynArr?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet</link><comments>https://teletype.in/@pascalabcnet/AssCompDynArr?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet#comments</comments><dc:creator>pascalabcnet</dc:creator><title>Присваивание и сравнение динамических массивов</title><pubDate>Wed, 23 Oct 2024 07:39:11 GMT</pubDate><media:content medium="image" url="https://img1.teletype.in/files/81/1d/811d69a5-2c34-4d98-bb96-cb29b86ee8bd.png"></media:content><description><![CDATA[<img src="https://img1.teletype.in/files/8d/06/8d06fae1-8452-40b2-addf-5ffd3e56777b.png"></img>Работа с массивами в PascalABC.NET имеет свои особенности, особенно если речь идёт о динамических массивах. В этой новости разберём, как правильно присваивать, копировать и сравнивать такие массивы.]]></description><content:encoded><![CDATA[
  <p id="9II1">Работа с массивами в PascalABC.NET имеет свои особенности, особенно если речь идёт о динамических массивах. В этой новости разберём, как правильно присваивать, копировать и сравнивать такие массивы.</p>
  <p id="SikX">🔹 Присваивание динамических массивов</p>
  <p id="Qe1z">Когда вы присваиваете один динамический массив другому через оператор :=, происходит ссылочное присваивание. Это означает, что обе переменные ссылаются на один и тот же массив в памяти. Любые изменения в одном массиве автоматически отразятся на другом.</p>
  <pre id="cWNh" data-lang="pascal">Пример:
var a := new integer[5]; // Создаём массив
var b := a; // b ссылается на тот же массив,что и a
b[0] := 10; // Теперь и a[0], и b[0] равны 10</pre>
  <p id="oeWi">Чтобы создать независимую копию массива, следует использовать встроенную функцию Copy(a).</p>
  <pre id="LIvB" data-lang="pascal">Пример:
var a := new integer[5]; // Создаём массив
var b := Copy(a);        // b — это копия массива a
b[0] := 10;  // Изменение b не затрагивает a</pre>
  <p id="lvAT">🔹 Сравнение массивов</p>
  <p id="FDH9">✦ Прямое сравнение массивов через оператор = проверяет ссылки, а не содержимое массивов. Даже если два массива содержат одинаковые элементы, они будут считаться неравными, если ссылаются на разные объекты в памяти.</p>
  <pre id="RlE2" data-lang="pascal">Пример
var a := new integer[5];
var b := new integer[5];
if a = b then // это сравнение вернёт False
  Println(&#x27;Массивы равны&#x27;)
else Println(&#x27;Массивы не равны&#x27;);</pre>
  <p id="0YSj">✦ Для корректного сравнения содержимого массивов в PascalABC.NET нужно использовать метод ArrEqual, который проверяет, равны ли все элементы двух массивов.</p>
  <pre id="pjrE">Пример:
var a := new integer[5];
var b := new integer[5];
if a.ArrEqual(b) then  // Проверяет равенство всех элементов массива
  Println(&#x27;Массивы равны&#x27;)
else Println(&#x27;Массивы не равны&#x27;);</pre>
  <p id="9ZC5">🔹 Итоги:</p>
  <p id="p2pU">✦ Для копирования массива используйте функцию Copy(a), чтобы избежать ссылочного присваивания.<br />✦ Для сравнения содержимого массивов используйте метод ArrEqual.</p>
  <figure id="ZbUq" class="m_column">
    <img src="https://img1.teletype.in/files/8d/06/8d06fae1-8452-40b2-addf-5ffd3e56777b.png" width="1280" />
  </figure>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@pascalabcnet/aq7Z7khdTtH</guid><link>https://teletype.in/@pascalabcnet/aq7Z7khdTtH?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet</link><comments>https://teletype.in/@pascalabcnet/aq7Z7khdTtH?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet#comments</comments><dc:creator>pascalabcnet</dc:creator><title>Разработка заданий для модуля LightPT</title><pubDate>Sat, 12 Oct 2024 12:23:27 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/77/84/77844f13-0419-4298-b7dd-1fd3c186d232.png"></media:content><description><![CDATA[<img src="https://img4.teletype.in/files/7e/a9/7ea90009-330c-48a2-9596-bbd880ebe134.png"></img>Чтобы задания проверялись автоматически, необходимо создать Урок с заданиями. Урок представляет собой папку, в которой находятся .pas-файлы - заготовки с формулировками заданий, а также специальный файл lightpt.dat. Этот файл может быть пустым или содержать название урока. Именно этот файл является маркером того, что все задания в этой папки будут проходить через невидимую проверяющую систем]]></description><content:encoded><![CDATA[
  <h2 id="flnW">Начальные действия</h2>
  <p id="b6to">Чтобы задания проверялись автоматически, необходимо создать <strong>Урок </strong>с заданиями. Урок представляет собой папку, в которой находятся .pas-файлы - заготовки с формулировками заданий, модуль проверки Tasks.pas, который будет проверять все выполненные задания в уроке, а также специальный файл lightpt.dat. Этот файл может быть пустым или содержать название урока (в кодировке <em>Windows 1251</em>). Название урока не должно содержать пробелов. </p>
  <p id="tCau">Именно файл lightpt.dat является маркером того, что все задания в этой папке будут проходить через невидимую проверяющую систему.</p>
  <p id="29bj">Названия .pas-файлов с заданиями могут содержать в начале имени числа с последующим знаком подчёркивания, которые игнорируются. Например, 01_Пример1.pas и Пример1.pas это одинаковые имена для системы проверки.</p>
  <p id="44hO">Итак, создадим папку Lesson1 и в ней файл lightpt.dat, в котором напишем одну строчку в кодировке Windows 1251: <strong>Урок-1. Задания на массивы</strong></p>
  <figure id="fqvM" class="m_column">
    <img src="https://img4.teletype.in/files/7e/a9/7ea90009-330c-48a2-9596-bbd880ebe134.png" width="1625" />
  </figure>
  <h2 id="Nk8b">Создание задания</h2>
  <p id="XrX8">Теперь создадим в этой папке первый файл с заданием ArrTask1.pas со следующим содержимым:</p>
  <figure id="oDg3" class="m_column">
    <img src="https://img1.teletype.in/files/09/8d/098df882-3f4d-497a-92bf-80f37ea3a2c6.png" width="1464" />
  </figure>
  <p id="lW7R">Заметим, что в заготовке задания уже массив заполнен и выведен. Позже рассмотрим ситуацию решения с нуля.</p>
  <h2 id="Rp3S">Создание модуля проверки заданий</h2>
  <p id="anSg">Теперь создадим модуль, который будет проверять все выполненные задания в уроке (в олимпиадном программировании такие подпрограммы называются <strong>чекерами</strong>). Для этого в той же папке создадим файл Tasks.pas со следующим содержимым (важно использовать именно это имя файла):</p>
  <figure id="ifrQ" class="m_column">
    <img src="https://img1.teletype.in/files/0c/ce/0ccebac1-80c2-4304-9c3a-04add8a4f05d.png" width="1459" />
  </figure>
  <p id="cmQa">Вот полный текст проверяющего модуля Tasks:</p>
  <pre id="Ks3f" data-lang="pascal">unit Tasks;

uses LightPT;

procedure CheckTaskT(name: string);
begin
  ClearOutputListFromSpaces; 
  case name of
    &#x27;ArrTask1&#x27;: begin

    end;
  end;
end;

initialization
  CheckTask := CheckTaskT;
end.</pre>
  <p id="hgv5">Здесь ClearOutputListFromSpaces надо писать всегда - оно удаляет пробелы из вывода, которые добавляет при выводе Print, a.Print и ему подобные.</p>
  <p id="LzrD">Теперь в ветви &#x27;ArrTask1&#x27; оператора case напишем чекер, проверяющий выполнение задания ArrTask1.</p>
  <h2 id="eJRv">Написание чекера</h2>
  <p id="YA2z">Нужно понимать, что чекер состоит из следующих основных компонент:</p>
  <ul id="bpCa">
    <li id="Pb7V">Проверка типов, введенных пользователем (процедура CheckData)</li>
    <li id="2RN6">Считывание ввода пользователя в локальные переменные</li>
    <li id="xiqr">Решение задания в чекере и получение ответа</li>
    <li id="sxmI">Проверка соответствия ответа тому, который вывел пользователь (процедура CheckOutput)</li>
  </ul>
  <p id="tVkL">Рассмотрим каждый из этих пунктов по-отдельности</p>
  <h3 id="ogDS">Проверка типов, введенных пользователем</h3>
  <p id="Mgul">Проверка типов, введенных пользователем, осуществляется с помощью процедуры CheckData. Простейшая форма вызова CheckData имеет вид:</p>
  <pre id="3ZyO">var n := 10; 
CheckData(Input := [cInt] * n);</pre>
  <p id="ocVg">или</p>
  <pre id="TqEx">var n := 10; 
CheckData([cInt] * n);</pre>
  <p id="gaaO">Это говорит о том, что мы проверяем, что пользователь ввёл 10 целых чисел (каждое целое число задаётся здесь маркером типа cInt).</p>
  <p id="Y3US">Какие ещё варианты задания входных данных могут быть? Их всего пять: </p>
  <pre id="eRbP">cInt, cRe, cStr, cBool, cChar</pre>
  <p id="ANYD">Они должны быть объединены в массив типов тем или иным способом, например, с помощью операций сложения массивов и умножения массива на число. Например, выражение </p>
  <pre id="QzTH">[cInt] * n</pre>
  <p id="w7By">задаёт массив из n целых типов, а выражение</p>
  <pre id="BtJ3">[cStr] + [cInt] * n + [cRe] * 2</pre>
  <p id="zqSg">задаёт массив из одного строкового типа, после которого идёт n целых типов и 2 вещественных.</p>
  <p id="ZqCK">Если маркер типа умножается на число, его необязательно окаймлять в ||, например:</p>
  <pre id="Mhum">[cStr] + cInt * n + cRe * 2</pre>
  <h3 id="icVK">Считывание ввода пользователя</h3>
  <p id="lMX7">Итак, пусть мы написали </p>
  <pre id="zYjz">var n := 10; 
CheckData(cInt * n);</pre>
  <p id="8CDA">Это значит, что для решения задания ученик вводит n целых чисел тем или иным способом. </p>
  <p id="lR0R">Теперь чтобы проверить задание, мы должны те n значений, которые ввёл ученик, считать во внутренние переменные. Мы это сделаем таким способом:</p>
  <pre id="uZ23">var a := IntArr(n);</pre>
  <p id="mlfK">Функция IntArr(n) считывает следующие n данных, которые ввёл пользователь, в массив, рассматривая их как целые. Таким образом, она возвращает массив из n целых.</p>
  <p id="jU9d">Какие еще функции можно использовать для считывания ввода пользователя?</p>
  <p id="orTC">Это простейшие:</p>
  <pre id="uwSJ">Int
Re
Boo
Chr
Str</pre>
  <p id="C6Lb">которые считывают один элемент данных и продвигаются вперёд, пары</p>
  <pre id="WXtk">Int2
Re2</pre>
  <p id="oM9w">которые возвращают кортежи (пар другого типа нет поскольку это - редкость), а также массивы одного типа из n элементов:</p>
  <pre id="SnfW">IntArr(n)
ReArr(n)
BooArr(n)
ChrArr(n)
StrArr(n)</pre>
  <h3 id="80Dp">Решение задания в чекере и получение ответа</h3>
  <p id="VRtE">Теперь когда мы считали все данные, которые ввёл пользователь, в переменные, решим задачу так, как должен решать ученик. В данном случае это просто:</p>
  <pre id="bgUA">var cnt := a.Count(x -&gt; x mod 2 = 0);</pre>
  <h3 id="KXhJ">Проверка ответа</h3>
  <p id="uNUY">Теперь необходимо понять, что в заготовке задания мы уже вывели сгенерированный случайный массив и нам лишь надо вывести найденное значение cnt. Для этого воспользуемся процедурой CheckOutput:</p>
  <pre id="62xh">CheckOutput(a,cnt);</pre>
  <p id="dIUu">Всё, текст проверки полностью готов:</p>
  <pre id="biHq">procedure CheckTaskT(name: string);
begin
  ClearOutputListFromSpaces;
  case name of
    &#x27;ArrTask1&#x27;: begin
      var n := 10;
      CheckData(Input := cInt * n); // проверка типов
      var a := IntArr(n);                   // считывание ввода
      var cnt := a.Count(x -&gt; x mod 2 = 0); // решение задания
      CheckOutput(a,cnt);                   // проверка ответа
    end;
  end;
end;</pre>
  <p id="aKaD">Как видим, он совсем простой - состоит из 5 строк. </p>
  <h2 id="TwpR">Как это выглядит со стороны ученика</h2>
  <p id="xDgD">Ученик, решая ArrTask1, вначале просто запустит заготовку и увидит</p>
  <figure id="SWkl" class="m_column">
    <img src="https://img1.teletype.in/files/c8/26/c826aff1-6647-4966-8850-c23bb5b78af3.png" width="1607" />
  </figure>
  <p id="nDnN">Далее он попытается решить неправильно:</p>
  <figure id="VaDx" class="m_column">
    <img src="https://img3.teletype.in/files/a1/8b/a18b237b-a567-45a1-9ca7-6763ddac9d43.png" width="1459" />
  </figure>
  <p id="kw5f">И наконец</p>
  <p id="LBvj"> он решит задание правильно.</p>
  <figure id="Yb4P" class="m_column">
    <img src="https://img3.teletype.in/files/a2/52/a2521a4b-6ebe-4179-82b6-f6b679fc5cb3.png" width="1459" />
  </figure>
  <h2 id="S82D">Добавление тестов (не дописано)</h2>
  <p id="fx8R">К этому может добавляться вызов <strong>процедуры генерации тестов</strong>. Дело в том, что пользователь может попытаться обмануть систему проверки LightPT и ввести данные, на которых задача решается просто. </p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@pascalabcnet/FLq12dPr2bW</guid><link>https://teletype.in/@pascalabcnet/FLq12dPr2bW?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet</link><comments>https://teletype.in/@pascalabcnet/FLq12dPr2bW?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet#comments</comments><dc:creator>pascalabcnet</dc:creator><title>Алгоритм K-means кластеризации</title><pubDate>Wed, 25 Sep 2024 10:35:33 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/9f/12/9f12d8da-90e2-4fbe-af6a-f1a0c285d887.png"></media:content><description><![CDATA[<img src="https://img2.teletype.in/files/91/8c/918c7e41-816a-49bd-9f76-bafaf5489057.png"></img>Данный алгоритм был построен в диалоге с нейросетью ChatGPT. Неудачные примеры кода, предлагаемые нейросетью, отвергались - и ей предлагалось уточнить решение. После этого некоторые куски кода содержали незначительные ошибки, которые правились руками.]]></description><content:encoded><![CDATA[
  <p id="q63U">Данный алгоритм был построен в диалоге с нейросетью ChatGPT. Неудачные примеры кода, предлагаемые нейросетью, отвергались - и ей предлагалось уточнить решение. После этого некоторые куски кода содержали незначительные ошибки, которые правились руками.</p>
  <p id="ADcs">Итог - очень впечатляющая работа нейросети. Например, новый метод IndexMinBy из соседнего диалога был сразу задействован нейросетью в текущем диалоге. </p>
  <p id="DCjr">Нейросети предлагались несколько функций из стандартной библиотеки, которые делали то же, но эффективнее - она мгновенно учитывала советы.</p>
  <p id="h8a4">Несколько вопросов было по переработке алгоритма. Например, нейросеть инициализировала центроиды кластеров уже имеющимися точками, что могло приводить к ошибке общего алгоритма. Ей было предложено изменить алгоритм генерации центроидов на случайные точки в прямоугольнике - она сразу поняла, что прямоугольник, о котором идет речь, - это минимумы-максимумы координат точек. И предложила алгоритм GenerateRandomCentroids.</p>
  <p id="ICBM">Руками правились части кода, где надо было генерировать точки с помощью нового модуля Coords, а также с помощью библиотеки Mathnet.Numerics.dll - нейросеть конечно этого не знала.</p>
  <p id="IzFM">Но основная канва кода в крупных командах близка к идеальной.</p>
  <p id="Btuz">Итак, перед нами алгоритм KMeans выделения кластеров. Задаётся набор точек, количество кластеров, максимальное число итераций и минимальное расстояние между первоначальными центроидами.</p>
  <p id="NK1o">Код:</p>
  <pre id="rLYY" data-lang="pascal">{$reference Mathnet.Numerics.dll}
uses Mathnet.Numerics;
uses Coords;

function GenerateCluster(X, Y, deviation: real; count: integer): array of Point;
begin
  var xx := Generate.Normal(count, X, deviation);
  var yy := Generate.Normal(count, Y, deviation);
  Result := ArrGen(count, i -&gt; Pnt(xx[i],yy[i]));
end;

function Distance(p1, p2: Point): real := Sqrt(Sqr(p1.x - p2.x) + Sqr(p1.y - p2.y));

function GenerateRandomCentroids(k: integer; points: array of Point; minDistance: real): array of Point;
begin
  var minX := points.Min(p -&gt; p.x);
  var maxX := points.Max(p -&gt; p.x);
  var minY := points.Min(p -&gt; p.y);
  var maxY := points.Max(p -&gt; p.y);
  var centroids := new List&lt;Point&gt;();
  while centroids.Count &lt; k do
  begin
    // Случайная точка в пределах прямоугольника
    var candidate := new Point(
      RandomReal(minX, maxX),
      RandomReal(minY, maxY)
    );
    // Проверяем, что расстояние до всех ранее выбранных центроидов больше minDistance
    var isFarEnough := centroids.All(c -&gt; Distance(c,candidate) &gt;= minDistance);
    if isFarEnough then
      centroids.Add(candidate);
  end;
  Result := centroids.ToArray();
end;

function KMeans(points: array of Point; k: integer; maxIterations: integer; minDistance: real): (array of List&lt;Point&gt;, array of Point);
begin
  var n := points.Length;
  if k &gt; n then
    raise new System.ArgumentException(&#x27;Число кластеров не может быть больше числа точек&#x27;);
  // Генерируем начальные центроиды случайно в пределах прямоугольника
  var centroids := GenerateRandomCentroids(k, points, minDistance);
  // Инициализация кластеров как массив списков с помощью ArrGen
  var clusters := ArrGen(k, i -&gt; new List&lt;Point&gt;);
  for var iteration := 1 to maxIterations do
  begin
    // Шаг 1: Назначаем каждую точку ближайшему центроиду
    foreach var cluster in clusters do
      cluster.Clear(); // Очищаем списки на каждой итерации
    foreach var point in points do
    begin
      var nearestCentroidIndex := centroids.IndexMinBy(c -&gt; Distance(point,c));
      clusters[nearestCentroidIndex].Add(point); // Добавляем точку в ближайший кластер
    end;
    // Шаг 2: Обновляем центроиды как средние точки своих кластеров
    var newCentroids := Copy(centroids); // Копируем центроиды
    for var i := 0 to k - 1 do
      if clusters[i].Count &gt; 0 then
        newCentroids[i] := new Point(
          clusters[i].Average(p -&gt; p.x),
          clusters[i].Average(p -&gt; p.y)
        );
    // Проверяем, если центроиды не изменились, то завершаем
    if newCentroids.SequenceEqual(centroids) then
      break;
    centroids := newCentroids;
  end;
  Result := (clusters, centroids);
end;

begin
  // Сгенерируем исходные кластеры точек
  var cluster1 := GenerateCluster(5, 3, 2.5, 50);
  var cluster2 := GenerateCluster(7, -6, 1.5, 25);
  var cluster3 := GenerateCluster(-7, 2, 1, 44);
  
  var allPoints := cluster1 + cluster2 + cluster3;
  
  // Применяем алгоритм K-means для кластеризации
  var (clusters, centroids) := KMeans(allPoints, 3, 100, 2);
  
  DrawPoints(clusters[0].ToArray,3);
  DrawPoints(clusters[1].ToArray,3);
  DrawPoints(clusters[2].ToArray,PointRadius := 3);
  
  DrawPoint(centroids[0].x,centroids[0].y,PointRadius := 5);
  DrawPoint(centroids[1].x,centroids[1].y,PointRadius := 5);
  DrawPoint(centroids[2].x,centroids[2].y,PointRadius := 5);
end.</pre>
  <p id="GLLi">Вывод в графическом окне:</p>
  <figure id="UsZr" class="m_retina">
    <img src="https://img2.teletype.in/files/91/8c/918c7e41-816a-49bd-9f76-bafaf5489057.png" width="800" />
  </figure>
  <p id="LlbY">Немного не нравится необходимость преобразовывать списки к массивам с помощью ToArray. В Distance можно брать квадрат расстояния для ускорения, но оставил как есть.</p>
  <p id="G1kP">Решение о моделировании кластеров пополняемыми списками нейросеть принимала сама. А вычисление nearestCentroidIndex с помощью indexMinBy просто красиво.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@pascalabcnet/sH6iujQmEtL</guid><link>https://teletype.in/@pascalabcnet/sH6iujQmEtL?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet</link><comments>https://teletype.in/@pascalabcnet/sH6iujQmEtL?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet#comments</comments><dc:creator>pascalabcnet</dc:creator><title>GhatGPT создаёт новый язык программирования</title><pubDate>Fri, 30 Aug 2024 19:18:05 GMT</pubDate><description><![CDATA[О великий могучий ChatGPT! Предложи синтаксис нового современного языка программирования, который сразу понравится всем :)]]></description><content:encoded><![CDATA[
  <h1 id="Elju">Вот реальная переписка</h1>
  <h3 id="gLhn">Вопрос 1:</h3>
  <p id="ixtF">О великий могучий ChatGPT! Предложи синтаксис нового современного языка программирования, который сразу понравится всем :)</p>
  <h6 id="PETa">ChatGPT:</h6>
  <p id="KvVe">Создание синтаксиса нового языка программирования, который сразу понравится всем, — это сложная задача, так как предпочтения разработчиков сильно разнятся. Однако можно предложить нечто, что могло бы объединить лучшие черты популярных языков и учесть современные тенденции. Ниже я предложу синтаксис и концепции для такого гипотетического языка.</p>
  <h3 id="8Q7L">Имя языка: <strong>&quot;Fluent&quot;</strong></h3>
  <h4 id="hxML">1. <strong>Простота и Лаконичность</strong></h4>
  <ul id="7B5K">
    <li id="dOat">Минимальное количество ключевых слов.</li>
    <li id="tqyA">Краткость и выразительность как в Python.</li>
    <li id="epaV">Чистота и строгая типизация как в TypeScript.</li>
  </ul>
  <h4 id="ljQR">2. <strong>Консистентная и Универсальная Система Типов</strong></h4>
  <ul id="QDzy">
    <li id="D4wD">Типизация, подобная Rust, с возможностью автоматического выведения типов.</li>
    <li id="o95Q">Безопасность типов с возможностью применения как статической, так и динамической типизации.</li>
  </ul>
  <h4 id="xa2Q">3. <strong>Функциональность и Модульность</strong></h4>
  <ul id="z9JF">
    <li id="BxNB">Поддержка чистых функций и функционального программирования.</li>
    <li id="jGCD">Явная поддержка модульной архитектуры.</li>
  </ul>
  <h4 id="MJsN">4. <strong>Асинхронность и Параллелизм</strong></h4>
  <ul id="BgeD">
    <li id="Lzyy">Встроенная поддержка асинхронности с простым синтаксисом, как в JavaScript (<code>async/await</code>).</li>
    <li id="Z4PN">Возможность простого создания параллельных вычислений без необходимости управления потоками вручную.</li>
  </ul>
  <h4 id="aMRD">5. <strong>Единый Стиль Форматирования и Поддержка</strong></h4>
  <ul id="6AeE">
    <li id="nMN6">Автоматический форматтер в стиле Go, но с возможностью кастомизации.</li>
    <li id="2mT8">Встроенный линтер с гибкой настройкой.</li>
  </ul>
  <h3 id="RlVs">Пример синтаксиса:</h3>
  <pre id="jVZc">// Комментарии поддерживаются в стиле C++

// Модульное объявление
module MyApp.Utils

// Импорт других модулей
import MyApp.Core.Math as Math

// Определение функции
fn square(x: Number) -&gt; Number {
    return x * x
}

// Функции могут быть асинхронными
async fn fetchData(url: String) -&gt; Response {
    let response = await http.get(url)
    return response
}

// Структуры и типы данных
struct User {
    name: String
    age: Option&lt;Number&gt;
}

// Работа с коллекциями и выражениями
fn getUserNames(users: List&lt;User&gt;) -&gt; List&lt;String&gt; {
    return users.filter(user -&gt; user.age &gt; 18)
                .map(user -&gt; user.name)
}

// Использование опциональных типов с безопасной распаковкой
let age: Number = user.age.unwrap_or(0)

// Параллельные вычисления
fn processData(data: List&lt;Number&gt;) -&gt; List&lt;Number&gt; {
    return data.parallel_map(x -&gt; square(x))
}

// Обработка ошибок через Result тип
fn divide(a: Number, b: Number) -&gt; Result&lt;Number, String&gt; {
    if b == 0 {
        return Err(&quot;Division by zero&quot;)
    }
    return Ok(a / b)
}</pre>
  <h3 id="s6Ik">Ключевые концепции:</h3>
  <ul id="vhxo">
    <li id="mTcs"><strong>Модульность и Простота</strong>: Основное внимание уделяется простоте и читаемости кода. Отсутствие сложных конструкций, которые легко ошибочно интерпретировать.</li>
    <li id="vsnb"><strong>Безопасность типов</strong>: Строгая типизация предотвращает ошибки на этапе компиляции, при этом поддерживается удобная система типов с возможностью опциональных значений и типами <code>Result</code>.</li>
    <li id="36oc"><strong>Асинхронность и Параллелизм</strong>: Интуитивно понятная асинхронность и встроенные возможности параллелизма делают язык мощным и современным.</li>
  </ul>
  <p id="tnXG">Такой синтаксис может быть привлекательным для разработчиков, которые ценят лаконичность, безопасность и современный подход к программированию.</p>
  <p id="r3DF"></p>
  <h5 id="JB6K">Вопрос 2:</h5>
  <p id="68Sp">Предложи другой синтаксис языка программирования с теми же параметрами.</p>
  <h6 id="7IWB">ChatGPT сказал:</h6>
  <p id="efm9">Конечно! Вот еще один вариант синтаксиса гипотетического языка программирования, который соответствует тем же принципам: простота, типизация, модульность, асинхронность и параллелизм.</p>
  <h3 id="f5xj">Имя языка: <strong>&quot;Streamline&quot;</strong></h3>
  <h4 id="FOeN">Основные особенности:</h4>
  <ul id="SS0J">
    <li id="IKTK">Сильная типизация с автоматическим выводом типов.</li>
    <li id="kszS">Краткий и понятный синтаксис.</li>
    <li id="rhOf">Интуитивное функциональное программирование.</li>
    <li id="95Ev">Мощная система модулей и асинхронного кода.</li>
  </ul>
  <h3 id="mIp1">Пример синтаксиса:</h3>
  <pre id="sxLS">// Определение модуля
module App.Utilities

// Импортирование модулей
import App.Network.Http as Http

// Функции с кратким определением типов
func square(x: Number) -&gt; Number = x * x

// Асинхронные функции с простым синтаксисом
async func fetchData(url: String): Response {
    let response = await Http.get(url)
    return response
}

// Структуры и типы данных
type User {
    name: String
    age?: Number  // Опциональный тип с вопросительным знаком
}

// Работа с коллекциями и функциональными операциями
func adultNames(users: List&lt;User&gt;): List&lt;String&gt; =
    users.filter(|user| user.age &gt; 18)
         .map(|user| user.name)

// Опциональные типы с безопасным доступом через &#x60;?&#x60;
let age: Number = user.age ? 0  // Если &#x60;age&#x60; пусто, то вернется 0

// Параллельные вычисления с встроенной поддержкой
func processData(data: List&lt;Number&gt;): List&lt;Number&gt; =
    data.parallel_map(|x| square(x))

// Обработка ошибок через &#x60;Result&#x60; и &#x60;try-catch&#x60; блоки
func divide(a: Number, b: Number): Result&lt;Number, String&gt; =
    if b == 0 {
        return Err(&quot;Division by zero&quot;)
    } else {
        return Ok(a / b)
    }

// Пример использования обработки ошибок
try {
    let result = divide(10, 0)
    print(result)
} catch (error: String) {
    print(&quot;Error: $error&quot;)
}</pre>
  <h3 id="uYKD">Ключевые концепции:</h3>
  <ul id="YnrU">
    <li id="gMhK"><strong>Простота и Минимализм</strong>: Максимально лаконичный и читабельный код. Простые конструкции, которые легко понимаются и поддерживаются.</li>
    <li id="nOAY"><strong>Мощная Типизация</strong>: Вывод типов, строгая типизация с поддержкой опциональных значений и возможностей для обработки ошибок.</li>
    <li id="B5HV"><strong>Асинхронность и Параллелизм</strong>: Интеграция асинхронного программирования и параллельных вычислений с минимальными усилиями со стороны разработчика.</li>
    <li id="dDW3"><strong>Чистота и Структурированность</strong>: Чистый и компактный синтаксис, который легко структурировать и масштабировать.</li>
  </ul>
  <p id="zwBT">Этот синтаксис предлагает еще более минималистичный подход с акцентом на читаемость и выразительность кода.</p>
  <p id="w3Z1"></p>
  <h5 id="A91O">Вопрос 3:</h5>
  <p id="Ughw">Отлично! И предложи третий вариант языка, ещё более лаконичный и привлекательный</p>
  <h6 id="oHvy">ChatGPT сказал:</h6>
  <p id="lJUM">Рад, что вам понравилось! Вот третий вариант языка программирования, который будет еще более лаконичным и интуитивным.</p>
  <h3 id="TPlD">Имя языка: <strong>&quot;Swiftly&quot;</strong></h3>
  <h4 id="0YJM">Основные особенности:</h4>
  <ul id="pcz9">
    <li id="5SgF">Экстремальная лаконичность и выразительность.</li>
    <li id="1kZt">Интуитивный синтаксис с минимальным количеством операторов.</li>
    <li id="2mbY">Мощная система типов с простым выводом.</li>
    <li id="0rhd">Фокус на чистоту кода и функциональный стиль.</li>
  </ul>
  <h3 id="zNlc">Пример синтаксиса:</h3>
  <pre id="BsxR">// Модули и импорты
module Utils
import Net.Http

// Определение функции с неявным выводом типа
fn square(x) = x * x

// Асинхронные функции с неявным возвратом
async fn fetch(url) {
    response = await Http.get(url)
    response
}

// Структуры и типы данных
type User {
    name: String
    age?: Int  // Опциональный тип через &#x60;?&#x60;
}

// Функциональные коллекции
fn adult_names(users) = 
    users.filter(|u| u.age &gt; 18).map(|u| u.name)

// Опциональные типы с безопасным доступом через &#x60;?&#x60;
age = user.age ? 0  // Если &#x60;age&#x60; пусто, возвращаем 0

// Параллельные вычисления, встроенные в функциональные методы
fn process(data) = 
    data.parallel_map(square)

// Простая обработка ошибок через &#x60;?&#x60; и &#x60;catch&#x60;
fn divide(a, b) = 
    b == 0 ? &quot;Error: Division by zero&quot; : a / b

// Пример использования
result = divide(10, 0) 
print(result)  // &quot;Error: Division by zero&quot;</pre>
  <h3 id="FalT">Ключевые концепции:</h3>
  <ul id="Hg0A">
    <li id="q0hj"><strong>Минимум Синтаксиса</strong>: Минимум символов и конструкций, простой и понятный код. Вывод типов автоматически, где это возможно.</li>
    <li id="7Q7u"><strong>Лаконичность</strong>: Синтаксис оптимизирован для краткости без потери выразительности. Операторы и ключевые слова сведены к минимуму.</li>
    <li id="1gfb"><strong>Простая Асинхронность и Параллелизм</strong>: Асинхронные и параллельные операции интегрированы прямо в язык, используя простые и лаконичные конструкции.</li>
    <li id="BDnA"><strong>Функциональная Чистота</strong>: Лямбда-выражения и функции высшего порядка легко читаются и пишутся, что поощряет функциональный подход.</li>
  </ul>
  <p id="QqIW">Этот вариант языка делает акцент на максимальной лаконичности и простоте, позволяя разработчикам писать чистый и понятный код с минимальными усилиями.</p>
  <p id="VJqg"></p>
  <p id="NFuR"><strong>Вопрос 4.</strong> К чему ты больше склоняешься для языка для обучения - к статической типизации с мощным автовыведением типов или к динамической типизации?</p>
  <h6 id="5WeA">ChatGPT:</h6>
  <p id="N0FF">Я склоняюсь к <strong>статической типизации с мощным автовыведением типов</strong> для образовательного языка программирования. Вот почему:</p>
  <h3 id="uYjW">Основные Аргументы:</h3>
  <ol id="ge4s">
    <li id="zuWL"><strong>Раннее Обнаружение Ошибок</strong>:</li>
    <ul id="H03t">
      <li id="lDGh">Статическая типизация позволяет выявлять ошибки типов на этапе компиляции, что особенно важно для начинающих. Это помогает ученикам избегать распространенных ошибок и учит их лучше понимать, как типы данных влияют на выполнение программы.</li>
    </ul>
    <li id="pLCB"><strong>Автовыведение Типов</strong>:</li>
    <ul id="E7BS">
      <li id="NVlN">Автовыведение типов упрощает процесс написания кода, поскольку ученикам не нужно явно указывать типы данных везде. Это снижает когнитивную нагрузку и позволяет сосредоточиться на логике программирования. Например, ученику не нужно думать о том, какой тип у переменной <code>x</code>, если компилятор может это сделать за него.</li>
    </ul>
    <li id="UXjs"><strong>Гибкость и Чистота Кода</strong>:</li>
    <ul id="IBWS">
      <li id="Uugu">Код остаётся лаконичным и чистым, что важно для обучения. Ученики могут писать простые функции и выражения, не беспокоясь о типах данных, при этом получая все преимущества статической типизации, такие как проверка корректности на этапе компиляции.</li>
    </ul>
    <li id="DxcS"><strong>Переход к Более Сложным Концепциям</strong>:</li>
    <ul id="MXfn">
      <li id="u77p">Такой подход обеспечивает плавный переход к более сложным аспектам программирования. Начинающие программисты со временем начнут понимать, как работают типы, и смогут начать использовать явные аннотации типов для более сложных задач.</li>
    </ul>
    <li id="9jFO"><strong>Снижение Риска Ошибок во Время Выполнения</strong>:</li>
    <ul id="QEVN">
      <li id="Tbkd">Динамическая типизация может привести к ошибкам, которые возникают только во время выполнения программы, что усложняет отладку. Статическая типизация предотвращает такие проблемы, давая ученикам уверенность в правильности их кода ещё до запуска.</li>
    </ul>
  </ol>
  <h3 id="xUoX">Заключение:</h3>
  <p id="hwMC"><strong>Статическая типизация с мощным автовыведением типов</strong> обеспечивает лучшее сочетание безопасности, удобства и обучаемости. Она помогает начинающим программистам избежать типичных ошибок, облегчает понимание кода и даёт возможность постепенно углубляться в более сложные концепции, не теряя при этом гибкости и простоты, необходимых для эффективного обучения.</p>
  <p id="Jt2i"></p>
  <h5 id="PJst">Вопрос 5:</h5>
  <p id="AWK0">Хорошо. Спасибо! А теперь приведи синтаксис такого языка с достаточно полным набором конструкций, охватывая присваивания, разные циклы, стандартные типы, составные типы, богатый набор операций, функции, модули и некоторые современные вещи, такие как кортежи, срезы массивов, лямбда-выражения и цепочки методов</p>
  <h6 id="jVtz">ChatGPT сказал:</h6>
  <p id="vvHh">Конечно! Вот пример синтаксиса языка программирования, основанного на статической типизации с автовыведением типов, который охватывает широкий спектр конструкций и возможностей.</p>
  <h3 id="d73W">Имя языка: <strong>&quot;EduFlex&quot;</strong></h3>
  <h3 id="xG9j">Основные конструкции</h3>
  <h4 id="Vb1e">1. <strong>Присваивания и стандартные типы</strong></h4>
  <pre id="qJHB"># Переменные и типы
num = 42               # Тип &#x27;Int&#x27; выводится автоматически
pi = 3.14              # Тип &#x27;Float&#x27;
name = &quot;Alice&quot;         # Тип &#x27;String&#x27;
is_valid = true        # Тип &#x27;Bool&#x27;
colors = [&quot;red&quot;, &quot;blue&quot;, &quot;green&quot;]  # Тип &#x27;List&lt;String&gt;&#x27;

# Кортежи
person = (&quot;John&quot;, 30, true)  # Тип &#x27;(String, Int, Bool)&#x27;

# Словари (ассоциативные массивы)
scores = {&quot;Alice&quot;: 95, &quot;Bob&quot;: 88}  # Тип &#x27;Dict&lt;String, Int&gt;&#x27;

# Срезы массивов
sub_colors = colors[1:3]  # Срез списка: [&quot;blue&quot;, &quot;green&quot;]

# Множественное присваивание через кортежи
x, y = 10, 20
name, age, is_student = person
</pre>
  <h4 id="hcuX">2. <strong>Условные конструкции</strong></h4>
  <pre id="knVq"># Условные конструкции
if num &gt; 50:
    print(&quot;Число больше 50&quot;)
elif num == 42:
    print(&quot;Число равно 42&quot;)
else:
    print(&quot;Число меньше 50&quot;)
</pre>
  <h4 id="zoi3">3. <strong>Циклы</strong></h4>
  <pre id="KyDI"># Цикл for по списку
for color in colors:
    print(color)

# Цикл for с диапазоном
for i in 1..5:  # Диапазон от 1 до 5 включительно
    print(&quot;Итерация&quot;, i)

# Цикл while
counter = 0
while counter &lt; 5:
    print(&quot;Счётчик =&quot;, counter)
    counter += 1
</pre>
  <h4 id="BpNd">4. <strong>Функции</strong></h4>
  <pre id="cVTP"># Функции с автоматическим выводом типов
fn greet(name):
    return &quot;Привет, &quot; + name

message = greet(&quot;Alice&quot;)  # &quot;Привет, Alice&quot;

# Функции с аннотациями типов (по желанию)
fn add(a: Int, b: Int) -&gt; Int:
    return a + b

result = add(10, 20)  # 30

# Лямбда-выражения
squared_numbers = [1, 2, 3, 4].map(|x| x * 2)  # [2, 4, 6, 8]

# Функции высшего порядка
fn apply_twice(fn, x):
    return fn(fn(x))

result = apply_twice(|n| n + 2, 5)  # 9
</pre>
  <h4 id="hUNd">5. <strong>Составные типы</strong></h4>
  <pre id="hQRb"># Структуры
type User {
    name: String
    age: Int
    is_active: Bool
}

# Создание объекта структуры
user = User(&quot;Alice&quot;, 25, true)

# Доступ к полям структуры
print(user.name)  # &quot;Alice&quot;
user.age += 1

# Деструктуризация структуры
name, age, is_active = user

# Параметры по умолчанию в функциях
fn create_user(name: String, age: Int = 18, is_active: Bool = true) -&gt; User:
    return User(name, age, is_active)

user2 = create_user(&quot;Bob&quot;)</pre>
  <h4 id="DxOp">6. <strong>Модули и импорт</strong></h4>
  <pre id="3zvj"># Объявление модуля
module MathUtils

fn square(x: Int) -&gt; Int:
    return x * x

fn cube(x: Int) -&gt; Int:
    return x * x * x

# Импорт и использование функций модуля
import MathUtils

result = MathUtils.square(4)  # 16</pre>
  <h4 id="g2WD">7. <strong>Современные возможности</strong></h4>
  <pre id="C1ZT"># Кортежи и их использование в функциях
fn swap(a, b) -&gt; (Int, Int):
    return (b, a)

x, y = swap(10, 20)  # x = 20, y = 10

# Срезы списков и строк
slice = &quot;Hello, World&quot;[0:5]  # &quot;Hello&quot;

# Цепочки методов
numbers = [5, 3, 8, 6]
result = numbers.filter(|x| x &gt; 5).map(|x| x * 2).sort()  # [12, 16]</pre>
  <h4 id="pKCY">8. <strong>Обработка ошибок</strong></h4>
  <pre id="eHoJ"># Простейшая обработка ошибок с использованием типа Result
fn safe_divide(a: Int, b: Int) -&gt; Result&lt;Int, String&gt;:
    if b == 0:
        return Err(&quot;Деление на ноль&quot;)
    return Ok(a / b)

# Пример использования
result = safe_divide(10, 0)
if result.is_err():
    print(&quot;Ошибка:&quot;, result.unwrap_err())
else:
    print(&quot;Результат:&quot;, result.unwrap())
</pre>
  <h3 id="amMv">Заключение</h3>
  <p id="EVLp">Этот синтаксис покрывает множество ключевых концепций, необходимых для обучения программированию. Он включает как простые конструкции, такие как присваивания и циклы, так и более сложные, такие как функции высшего порядка, кортежи и цепочки методов. Статическая типизация с автовыведением типов упрощает код, делая его доступным для новичков, и обеспечивает раннее выявление ошибок, что способствует более быстрому и эффективному обучению.</p>
  <p id="VzxY"></p>
  <h3 id="S9n1">Вопрос 6: </h3>
  <p id="gMmI">Здорово! А как по-твоему, нужен ли в таком языке отдельный цикл &quot;повторить n раз&quot; или достаточно цикла for, который ты предложил?</p>
  <h3 id="dIjr">ChatGPT сказал:</h3>
  <p id="IRnG">Отдельный цикл &quot;повторить n раз&quot; (например, <code>repeat n times</code>) может быть полезен в образовательном языке, особенно для начинающих программистов. Хотя цикл <code>for</code> вполне может выполнять эту задачу, добавление специального цикла &quot;повторить n раз&quot; делает код более интуитивным и понятным, особенно на ранних этапах обучения.</p>
  <h3 id="DI4F">Преимущества отдельного цикла &quot;повторить n раз&quot;:</h3>
  <ol id="2LNV">
    <li id="W6b0"><strong>Простота и Ясность</strong>:</li>
    <ul id="qnDg">
      <li id="m0x4">Команда <code>repeat n times</code> более интуитивно отражает намерение программиста, особенно для начинающих, которые могут ещё не быть знакомы с концепцией диапазонов в циклах <code>for</code>.</li>
    </ul>
    <li id="8vQj"><strong>Читабельность</strong>:</li>
    <ul id="hupt">
      <li id="BjLT">Такой цикл делает код более самодокументированным. Читая <code>repeat 5 times</code>, сразу понятно, что операция повторяется 5 раз, без необходимости вникать в детали диапазона или начального значения счётчика.</li>
    </ul>
    <li id="oVoL"><strong>Фокус на Логике</strong>:</li>
    <ul id="vGtd">
      <li id="Jzhq">Учащиеся могут сосредоточиться на логике программы, не отвлекаясь на настройку диапазонов и счётчиков. Это упрощает обучение и позволяет быстрее перейти к более интересным и сложным задачам.</li>
    </ul>
  </ol>
  <h3 id="UUzY">Пример использования:</h3>
  <pre id="OLNS"># Цикл &quot;повторить n раз&quot;
repeat 5 times:
    print(&quot;Это сообщение будет напечатано 5 раз&quot;)</pre>
  <h3 id="eWOJ">Вариант с циклом <code>for</code>:</h3>
  <pre id="9NlA"># Цикл &quot;повторить n раз&quot;
repeat 5 times:
    print(&quot;Это сообщение будет напечатано 5 раз&quot;)</pre>
  <h3 id="n8XW">Итог:</h3>
  <p id="khwM">Хотя цикл <code>for</code> технически может заменить цикл &quot;повторить n раз&quot;, отдельная конструкция <code>repeat n times</code> добавляет простоту и читабельность, что делает язык более доступным для начинающих программистов. В образовательном контексте это может быть важным фактором, способствующим лучшему пониманию и более плавному погружению в программирование. Поэтому такой цикл имеет смысл включить в язык, ориентированный на обучение.</p>
  <p id="J844"></p>
  <h3 id="HJBG">Вопрос 7: </h3>
  <p id="kW33">А цикл типа &quot;loop 5 do&quot; подойдёт для этих целей?</p>
  <h3 id="eqsX">ChatGPT сказал:</h3>
  <p id="x5P8">Да, цикл вида <code>loop 5 do</code> отлично подойдёт для этих целей. Он является лаконичным и интуитивным, что особенно важно для начинающих программистов. Такой синтаксис легко читается и сразу даёт понять, что код внутри блока будет выполнен 5 раз.</p>
  <h3 id="AdWM">Преимущества синтаксиса <code>loop n do</code>:</h3>
  <ol id="PpEL">
    <li id="nJHB"><strong>Лаконичность</strong>:</li>
    <ul id="4Xjo">
      <li id="eNwd">Синтаксис короткий и понятный, что уменьшает количество кода, который нужно писать и читать. Это особенно полезно в образовательных целях, где важно минимизировать отвлекающие детали.</li>
    </ul>
    <li id="IVWH"><strong>Ясность</strong>:</li>
    <ul id="pnBR">
      <li id="fhcb">Команда <code>loop 5 do</code> ясно указывает намерение — повторить действия внутри цикла 5 раз. Это делает код доступным и понятным даже для тех, кто только начинает изучать программирование.</li>
    </ul>
  </ol>
  <h3 id="TCxZ">Пример использования:</h3>
  <pre id="IEvS"># Цикл &quot;loop n do&quot;
loop 5 do:
    print(&quot;Сообщение будет выведено 5 раз&quot;)</pre>
  <h3 id="QVCY">Варианты циклов:</h3>
  <ul id="lfYf">
    <li id="rH7W">Если нужно просто повторить действие несколько раз, <code>loop n do</code> выглядит как естественный выбор.</li>
    <li id="yKyu">Для более сложных сценариев, где требуется счётчик или диапазон значений, можно использовать цикл <code>for</code>.</li>
  </ul>
  <h3 id="6SRv">Итог:</h3>
  <p id="LiUK">Синтаксис <code>loop 5 do</code> является отличным выбором для реализации простого цикла с повторением фиксированного количества раз. Он сочетает в себе лаконичность и простоту, что идеально подходит для языка, ориентированного на обучение. Такой цикл сделает код более читаемым и интуитивно понятным для школьников и других начинающих программистов.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@pascalabcnet/kr7IoIkQnCc</guid><link>https://teletype.in/@pascalabcnet/kr7IoIkQnCc?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet</link><comments>https://teletype.in/@pascalabcnet/kr7IoIkQnCc?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet#comments</comments><dc:creator>pascalabcnet</dc:creator><title>Семантические ошибки в Python на этапе выполнения</title><pubDate>Thu, 22 Aug 2024 07:43:01 GMT</pubDate><description><![CDATA[Данный вид ошибок связан как правило с двумя проблемами – отсутствующим именем и несоответствием типов. Важно то, что в Python такие ошибки проявляются на этапе выполнения, то есть, слишком поздно – когда программой уже активно пользуются.]]></description><content:encoded><![CDATA[
  <p id="CCta">Данный вид ошибок связан как правило с двумя проблемами – <em>отсутствующим именем</em> и <em>несоответствием типов</em>. Важно то, что в Python такие ошибки проявляются <em>на этапе выполнения</em>, то есть, слишком поздно – когда программой уже активно пользуются.</p>
  <p id="qhNV"><strong>Ошибка 1.</strong> Имя переменной не определено к моменту использования.</p>
  <p id="y5aE">Если мы пытаемся использовать какую-то переменную в вычислениях, но еще не присваивали ей значение, то возникает данная ошибка. Например, в программе</p>
  <pre id="ANJM" data-lang="python">a = int(input())
print(b + 1)</pre>
  <p id="XK8u">мы вначале введем значение переменной a, и только потом при выполнении второй строки будет выведено «NameError: name &#x27;b&#x27; is not defined». Таким образом, данная проблема будет диагностирована слишком поздно – уже когда программа выполнялась достаточно долго. При этом программа будет прервана на этом месте и все данные будут потеряны.</p>
  <p id="uh8U">Сравним это поведение с аналогичной программой на PascalABC.NET:</p>
  <pre id="7BQP" data-lang="pascal">begin
  var a := ReadInteger;
  Print(b);
end.</pre>
  <p id="HbKE">При запуске такой программы мы получаем ошибку компиляции</p>
  <figure id="oCMd" class="m_original">
    <img src="https://img1.teletype.in/files/00/30/0030fe14-6eab-4472-80f6-6dd341a3c814.png" width="613" />
  </figure>
  <p id="IMVJ"><strong>Вывод. </strong>Компилятор PascalABC.NET контролирует все семантические ошибки и не даст запустить программу, которая содержит хотя бы одну такую ошибку. Это – <em>огромное преимущество компиляторов</em>, которе отсутствует у интерпретаторов.</p>
  <p id="PayF"><strong>Ошибка 2.</strong> Имя функции записано неправильно.</p>
  <pre id="yeXa" data-lang="python">a = int(input())
Print(a)</pre>
  <p id="KstT">В этой программе имя функции Print неправильно записано с большой буквы. Программа по-прежнему после запуска выполнит первую строку – ввод данных, и только после этого выведет ошибку: «NameError: name &#x27;Print&#x27; is not defined».</p>
  <p id="X1Un"><strong>Ошибка 3.</strong> Несоответствие типов при вызове функции.</p>
  <p id="4h4H">Бывает так, что мы хотим выполнить операцию над типом, который для этого не предназначен. При запуске следующей программы</p>
  <pre id="k9t3" data-lang="python">from math import *
print(sqrt(&#x27;Python&#x27;))</pre>
  <p id="NPze">возникнет ошибка «TypeError: must be real number, not str». Это означает, что функция sqrt не может быть применена к строкам – только к вещественным числам.</p>
  <p id="R0xW"><strong>Ошибка 4.</strong> Несоответствие типов при выполнении операции.</p>
  <p id="2k1O">Бывают ситуации, когда мы пытаемся делать какие-то операции над типами, которые для этого не предназначены. При запуске следующей программы</p>
  <pre id="3eK4" data-lang="python">s = input()
print(s + 1)</pre>
  <p id="tXfx">мы вначале введём строку в переменную s, а при выполнении второй строки получим ошибку «TypeError: can only concatenate str (not &quot;int&quot;) to str», что означает, что строку нельзя складывать с числом.</p>
  <p id="Rz7r"><strong>Вывод.</strong> Семантические ошибки на Python диагностируются поздно, что приводит к программам, которые заведомо содержат ошибку, но могут быть запущены и успешно проработают какое-то время. Такие программы более опасны чем те, которые сразу не запускаются из-за ошибок синтаксиса, потому что, если программа запускается, это создаёт <em>ложное ощущение её безопасности</em>.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@pascalabcnet/ArrPragmatic</guid><link>https://teletype.in/@pascalabcnet/ArrPragmatic?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet</link><comments>https://teletype.in/@pascalabcnet/ArrPragmatic?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet#comments</comments><dc:creator>pascalabcnet</dc:creator><title>Задачи на массивы - прагматичный подход</title><pubDate>Sat, 11 May 2024 07:54:54 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/10/e3/10e334ec-4112-4197-af92-1f5bc9d0c736.png"></media:content><description><![CDATA[Во всех задачах на массивы требуется уметь описывать массивы, вводить их, заполнять случайными числами, выводить. Очень часто требуется цикл по массиву с перебором всех элементов.]]></description><content:encoded><![CDATA[
  <p id="7HYM">Данный текст опубликован в рамках подготовки к экзамену учеников Компьютерной школы мехмата ЮФУ (<a href="https://sunschool.mmcs.sfedu.ru" target="_blank">sunschool.mmcs.sfedu.ru</a>) по курсу &quot;Программирование 2 ступень&quot;.</p>
  <p id="RqvY">Во всех задачах на массивы требуется уметь описывать массивы, вводить их, заполнять случайными числами, выводить. Очень часто требуется цикл по массиву с перебором всех элементов.</p>
  <h3 id="urpE">Описание массива и выделение памяти</h3>
  <p id="Tq65"><code>var a: array of integer := new integer[10];</code></p>
  <p id="ucbz">Можно без указания типа:</p>
  <p id="sDkU"><code>var a := new integer[10];</code></p>
  <p id="Mgad">Если в задаче требуется создать массив из N элементов, то N надо предварительно ввести:</p>
  <p id="oslb"><code>var N := ReadInteger;<br />var a := new integer[N];</code></p>
  <p id="atWa">Цикл по массиву</p>
  <p id="HXvU"><code>for var i:=1 to a.Length-1 do<br />  Print(a[i])</code></p>
  <p id="zeex">или</p>
  <p id="AHZu"><code>foreach var x in a do<br />  Print(x)</code></p>
  <p id="xTOF">В случае foreach невозможно изменение элементов.</p>
  <h3 id="Ta8K">Ввод массива с клавиатуры</h3>
  <p id="kE0G">Массив можно ввести циклом</p>
  <p id="LvBG"><code>var N := ReadInteger;<br />var a := new integer[N];<br />for var i:=1 to a.Length-1 do<br />  a[i] := ReadInteger;</code></p>
  <p id="Kvkn">или методом</p>
  <p id="MWe4"><code>var N := ReadInteger;<br />var a := ReadArrInteger(N);</code></p>
  <h3 id="AH9D">Вывод массива</h3>
  <p id="NMqx"><code>a.Println;</code></p>
  <h3 id="PT5h">Поиск в массиве элементов и их индексов</h3>
  <p id="cdwb">Для проверки, есть ли элемент в массиве, используется операция in:</p>
  <p id="ECEH"><code>3 in a<br />(3 in a) or (4 in a)</code></p>
  <p id="McME">Найти индекс первого элемента, равного чему то:</p>
  <p id="0vEm"><code>var ind := a.IndexOf(3); // индекс первой 3 или -1 если не найдено</code></p>
  <p id="JOeZ">Найти индекс первого элемента, удовлетворяющего условию:</p>
  <p id="oADc"><code>var ind := a.FindIndex(x -&gt; x mod 2 = 0); // индекс первого четного или -1 если не найдено</code></p>
  <p id="CKPp">FindIndex можно также использовать для проверки, есть ли элементы, удовлетворяющие условию:</p>
  <p id="IVZb"><code>var Has := a.FindIndex(x -&gt; x in 3..5) &lt;&gt; -1; // есть ли элементы от 3 до 5</code></p>
  <h3 id="zMPO">Генерация массива по заданному правилу</h3>
  <p id="a6FF"><code>var a := ArrFill(N,1); // заполнить N элементов значением 1</code></p>
  <p id="RTzj">Для заполнения каждого элемента по правилу a[i] := i + 1 пользуемся функцией ArrGen:</p>
  <p id="gmQz"><code>var a := ArrGen(N, i -&gt; i+1);</code></p>
  <p id="xTDp">Но можно и циклом:</p>
  <p id="UwPn"><code>var a := new integer[N];<br /> for var i:=1 to a.Length-1 do<br />  a[i] := i + 1;</code></p>
  <p id="xeZg">Для заполнения массива, в котором задан первый элемент, а каждый следующий определяется по предыдущему, пользуемся</p>
  <p id="OTbv"><code>var a := ArrGen(N, 1, x -&gt; следующий);</code></p>
  <p id="fE8V">Здесь N - количество элементов, 1 – первый элемент.</p>
  <p id="FNJk">Например, если каждый следующий больше предыдущего <strong>на</strong> 2:</p>
  <p id="zzeW"><code>var a := ArrGen(N, 1, x -&gt; x + 2);</code></p>
  <p id="bZ6c">Если же каждый следующий больше предыдущего <strong>в 2 раза</strong>:</p>
  <p id="inC7"><code>var a := ArrGen(N, 1, x -&gt; x * 2);</code></p>
  <h3 id="x0y6">Преобразование элементов по заданному правилу</h3>
  <p id="LZuf">Будем считать массив a заданным (например, введенным с клавиатуры).</p>
  <p id="1Lmb">Чтобы увеличить все элементы массива на 1, пользуемся методом-процедурой Transform:</p>
  <p id="Gqmw"><code>a.Transform(x -&gt; x + 1)</code></p>
  <p id="9pSt">Иногда удобно преобразовывать элементы массива, используя цепочку методов. В этом случае следует использовать метод Select. Возможно потребуется преобразование назад к массиву, тогда используем дополнительно метод ToArray:</p>
  <p id="Ps5J"><code>a := a.Select(x -&gt; x + 1).ToArray;</code></p>
  <p id="wJMe">Если некоторые элементы надо преобразовать по одному правилу, а остальные – по другому или не преобразовывать вовсе, то лучше воспользоваться циклом. Например, для обнуления нечетных:</p>
  <p id="pLOr"><code>for var i:=1 to a.Length-1 do<br />  if a[i] mod 2 &lt;&gt; 0 then<br />    a[i] := 0;</code></p>
  <p id="ecmE">Если нечетные надо уменьшить на 1, а четные увеличить на 1:</p>
  <p id="fd8M"><code>for var i:=1 to a.Length-1 do<br />  if a[i] mod 2 &lt;&gt; 0 then<br />    a[i] -= 1<br />  else a[i] += 1;</code></p>
  <p id="pXSu">Немного менее понятным будет код с Transform и использованием <strong>условной операции</strong>:</p>
  <p id="SdWs"><code>a.Transform(x -&gt; x mod 2 = 0 ? x - 1 : x + 1);</code></p>
  <h3 id="kiSs">Фильтрация элементов по условию</h3>
  <p id="xLPu">Если в массиве надо отобрать элементы, удовлетворяющие определенному условию, это называется <strong>фильтрацией</strong>. Фильтрацию проще всего выполнять с помощью метода Where. Например, если требуется отфильтровать и вывести элементы массива, меньшие 5, то вот код:</p>
  <p id="K4uH"><code>a.Where(x -&gt; x &lt; 5).Println;</code></p>
  <p id="ZkLh">Если надо отфильтровать данные и записать результат в тот же массив, то используем преобразование к массиву методом ToArray:</p>
  <p id="thsM"><code>a := a.Where(x -&gt; x &lt; 5).ToArray;</code></p>
  <p id="VoWZ">Бывает так, что после фильтрации надо вычислить какую-то характеристику отфильтрованных элементов – например, найти их сумму, среднее или минимум-максимум:</p>
  <p id="UUTN"><code>a.Where(x -&gt; x &gt; 10).Sum.Print;<br /> a.Where(x -&gt; x in 2..8).Average.Print;<br /> a.Where(x -&gt; x mod 2 = 0).Min.Print;</code></p>
  <p id="2z7w">С помощью</p>
  <p id="Z9aG"><code>a.Where(x -&gt; x in 2..8).Count</code></p>
  <p id="yup6">можно вычислять количество элементов, удовлетворяющих некоторому условию. Однако то же можно сделать с помощью метода Count с параметром (см. следующий пункт). </p>
  <p id="fM8y">Кроме того, отфильтрованные элементы можно далее преобразовывать методом Select, используя <strong>цепочку методов</strong>:</p>
  <p id="Gx0J"><code>a.Where(x -&gt; x in 2..8).Select(x -&gt; x + 10).Println;</code></p>
  <h3 id="2Gpt">Задачи на поиск количества элементов, удовлетворяющих некоторому условию</h3>
  <p id="T6Ed">Если надо найти количество опеределенных элементов, то используется метод CountOf:</p>
  <p id="psdF"><code>a.CountOf(3) – количество троек в массиве</code></p>
  <p id="w23W">Если же надо найти количество элементов, удовлетворяющих условию, то используется метод Count с параметром:</p>
  <p id="rzTO"><code>a.Count(x -&gt; x in 2..8)</code></p>
  <h3 id="IQ6e">Задачи на использование срезов</h3>
  <p id="jGL1"><strong>Срез массива</strong> – это набор элементов массива, расположенных последовательно или с некоторым шагом.</p>
  <p id="1opK">Если есть массив из 10 элементов</p>
  <p id="9euB"><code>var a := Arr(1..10);</code></p>
  <p id="rAlY">то <code>a[1:5]</code> – это срез элементов массива с индексами от 1 до 4</p>
  <p id="4T9O">В срезах можно пропускать первый или последний индекс:</p>
  <p id="CiAg"><code>a[:5]</code> – первые 5 элементов</p>
  <p id="raDb"><code>a[5:]</code> – срез с элемента с индексом 5 до конца</p>
  <p id="9PZu">Кроме этого, есть срезы с шагом:</p>
  <p id="NwmU"><code>a[::2]</code> – все элементы с четными индексами</p>
  <p id="JHrb"><code>a[1::2]</code> – все элементы с нечетными индексами</p>
  <p id="rJWl"><code>a[::-1]</code> – инвертированный массив (все элементы в обратном порядке)</p>
  <p id="i6Mr">У срезов можно вычислять различные характеристики, например, сумму элементов, среднее или максимальный:</p>
  <p id="NpAQ"><code>a[::2].Sum</code> – сумма элементов с четными индексами</p>
  <p id="Z6CS"><code>a[1::2].Average</code> – среднее элементов с нечетными индексами</p>
  <p id="lp0i"><code>a[:5].Min</code> – минимальный среди первых 5 элементов</p>
  <p id="KnxK">Можно комбинировать срезы и методы массивов:</p>
  <p id="7xIg"><code>a[:5].Where(x -&gt; x mod 2 = 0)</code> – отфильтровать четные элементы среди первых пяти элементов массива</p>
  <h1 id="BWRQ">Индексация с конца и срезы</h1>
  <p id="WEwu">В ряде задач может встречаться индексация элементов с конца:</p>
  <p id="fJx3"><code>a[^1]</code> – это первый элемент с конца (последний)</p>
  <p id="9ICI"><code>a[^2]</code> – это второй элемент с конца (предпоследний)</p>
  <p id="pymt">Индексация с конца может сочетаться со срезами, например,</p>
  <p id="pFUw"><code>a[:^1]</code> – все элементы кроме последнего</p>
  <p id="rrZi"><code>a[^2:]</code> – два последних элемента массива</p>
  <p id="oYro"><code>a[1:^1].Sum</code> – сумма всех элементов кроме нулевого и последнего</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@pascalabcnet/LightPT_First</guid><link>https://teletype.in/@pascalabcnet/LightPT_First?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet</link><comments>https://teletype.in/@pascalabcnet/LightPT_First?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet#comments</comments><dc:creator>pascalabcnet</dc:creator><title>Система невидимой автоматической проверки заданий LightPT. Часть 1</title><pubDate>Wed, 01 Nov 2023 10:52:09 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/d9/f1/d9f19387-969b-4bea-8562-f08b3d0374b1.png"></media:content><description><![CDATA[<img src="https://img1.teletype.in/files/08/36/08367ed6-b491-46a8-b2d5-f1beb4835dc5.png"></img>В PascalABC.NET встроена система автоматической проверки LightPT, которая позволяет легко составлять автоматически проверяемые задания.]]></description><content:encoded><![CDATA[
  <p id="0kf4">В PascalABC.NET встроена система автоматической проверки LightPT, которая позволяет легко составлять автоматически проверяемые задания.</p>
  <p id="wrea">Рассмотрим данный процесс на примере.</p>
  <h2 id="flnW">Начальные действия</h2>
  <p id="b6to">Чтобы задания проверялись автоматически, необходимо создать Урок с заданиями. Урок представляет собой папку, в которой находятся .pas-файлы - заготовки с формулировками заданий, а также специальный файл lightpt.dat. Этот файл может быть пустым или содержать название урока. Именно этот файл является маркером того, что все задания в этой папки будут проходить через невидимую проверяющую систем</p>
  <p id="44hO">Итак, создадим папку Lesson1 и в ней файл lightpt.dat, в котором напишем одну строчку в кодировке Windows 1251: <strong>Урок-1. Задания на массивы</strong></p>
  <figure id="CYaA" class="m_column">
    <img src="https://img1.teletype.in/files/cb/ca/cbca3b84-0011-4542-a26e-6d92aa7a8e42.png" width="1625" />
  </figure>
  <h2 id="Nk8b">Создание задания</h2>
  <p id="XrX8">Теперь создадим в этой папке первый файл с заданием ArrTask1.pas со следующим содержимым:</p>
  <figure id="TAuK" class="m_column">
    <img src="https://img3.teletype.in/files/ef/a8/efa891b7-5338-4677-8eb9-8655cebcadd2.png" width="1433" />
  </figure>
  <p id="rxBA">Обязательно сохраним указанный файл в папку с уроком.</p>
  <h2 id="Rp3S">Создание модуля проверки заданий</h2>
  <p id="anSg">Теперь создадим модуль, который будет проверять все выполненные задания в уроке (в олимпиадном программировании такие подпрограммы называются <strong>чекерами</strong>). Для этого в той же папке создадим файл Tasks.pas со следующим содержимым (важно использовать именно это имя файла):</p>
  <figure id="MHPq" class="m_column">
    <img src="https://img3.teletype.in/files/af/09/af097781-7022-4549-b848-3094fcf2a6ae.png" width="1680" />
  </figure>
  <p id="w7ab">Вот полный текст проверяющего модуля Tasks:</p>
  <pre id="ubpa" data-lang="pascal">unit Tasks;

uses LightPT;

procedure CheckTaskT(name: string);
begin
  case name of
    &#x27;ArrTask1&#x27;: begin

    end;
  end;
end;

initialization
  CheckTask := CheckTaskT;
end.</pre>
  <h2 id="WBF9">Код проверки задания</h2>
  <p id="oma4">Далее напишем самую главную часть - код проверки:</p>
  <figure id="dYEu" class="m_column">
    <img src="https://img2.teletype.in/files/1e/c6/1ec6cf93-87aa-4087-8671-c771bf1daca3.png" width="1680" />
  </figure>
  <p id="1nf3">Вот полный текст модуля:</p>
  <pre id="18xw" data-lang="pascal">unit Tasks;

uses LightPT;

procedure CheckTaskT(name: string);
begin
  case name of
    &#x27;ArrTask1&#x27;: begin
      var N := Int;
      CheckData(Input := |cInt| + |cRe| * N);
      var a := ReArr(N);
      var cnt := a.Count(x -&gt; x &gt; 5);
      CheckOutput(cnt);
    end;
  end;
end;

initialization
  CheckTask := CheckTaskT;
end.</pre>
  <p id="HXmt">Поясним отдельные строки.</p>
  <p id="6A5S">1. Введенное учеником первое целоезначение, характеризующее размер массива,  запоминается в переменной N</p>
  <pre id="lY2P">var N := Int;</pre>
  <p id="H7cx">2. В процедуре CheckData указывается, что входные данные Input должны состоять из целого числа (константа cInt) и N вещественных чисел (константа cRe). Все данные объединяются в массив входных данных с помощью конструктора массива |cInt| + |cRe| * N. </p>
  <pre id="8TOp">CheckData(Input := |cInt| + |cRe| * N);</pre>
  <p id="F3P7">Константы cInt, cRe, cStr, cBool, cChar хранят в себе элементарные типы значений - именно из них конструируются типы входных данных и соответствие именно этим типам проверяются чекером у входных данных.</p>
  <p id="YpNP">Если мы введем неверное количество входных данных или неверно укажем их типы, система проверки выведет соответствующее сообщение об ошибке.</p>
  <p id="XyUg">3. С помощью функции ReArr введенные учеником N вещественных значений заносятся в массив a</p>
  <pre id="tBjA">var a := ReArr(N);</pre>
  <p id="HOuO">4. Количество элементов, больших 5, вычисляется с помощью стандартного метода массива a.Count и заносится в переменную cnt.</p>
  <pre id="uQkh">var cnt := a.Count(x -&gt; x &gt; 5);</pre>
  <p id="jYNK">5. Проверка правильности вывода осуществляется с помощью процедуры CheckOutput:</p>
  <pre id="hQ0a">CheckOutput(cnt);</pre>
  <p id="elsq">Всё!</p>
  <h2 id="oBwi">Решение задания учеником</h2>
  <p id="Nu0J">Теперь всё готово к выполнению задания под управлением <strong>системы невидимой автоматической проверки</strong>. </p>
  <p id="cpzo">Закроем среду PascalABC.NET и промоделируем действия ученика на уроке.</p>
  <p id="mcdG">Ученик <strong>обязательно должен запустить PascalABC.NET из файла урока </strong>щелчком мыши на файле с заданием:</p>
  <figure id="2RPG" class="m_column">
    <img src="https://img2.teletype.in/files/12/61/126137a3-c097-481a-9c30-0324d382fe76.png" width="1680" />
  </figure>
  <p id="EZ7V"><strong>Кнопка белого человечка</strong> на тулбаре является признаком того, что задание находится под управлением системы невидимой проверки.</p>
  <p id="vv5s">Наберем часть решения и запустим:</p>
  <figure id="cnxM" class="m_column">
    <img src="https://img1.teletype.in/files/82/10/8210ac44-2d2d-4ed0-a125-da3bb8c4202d.png" width="1433" />
  </figure>
  <p id="aLwu">Мы видим, что система автоматической проверки начала свою работу и показала, что необходимо ввести дополнительные данные.</p>
  <p id="qBng">Введем массив N вещественных любым доступным способом:</p>
  <figure id="7UAT" class="m_column">
    <img src="https://img1.teletype.in/files/8b/c5/8bc578f2-d907-44c1-a589-2cf0e1c26043.png" width="1544" />
  </figure>
  <p id="VFON">Здесь мы ошиблись - надо ввести вещественный массив:</p>
  <figure id="Mi0g" class="m_column">
    <img src="https://img4.teletype.in/files/f5/58/f558a9c4-faa2-4f82-a7b8-80c3601271f0.png" width="1544" />
  </figure>
  <p id="SB4S">Это сообщение системы невидимой проверки выводится серым, что означает, что ошибки нет, и мы на верном пути.</p>
  <p id="efCL">Попытаемся обмануть систему и выведем неверное значение:</p>
  <figure id="2tyv" class="m_column">
    <img src="https://img2.teletype.in/files/d6/8c/d68c8213-a343-435d-82c0-dc4f3bfed4b5.png" width="1680" />
  </figure>
  <p id="auGs">Система невидимой проверки показала, что результат неверен.</p>
  <p id="SXNH">И наконец правильно решим задачу:</p>
  <figure id="zdqC" class="m_column">
    <img src="https://img2.teletype.in/files/1f/6d/1f6d6de0-6ebe-4787-b2da-ea57412754ac.png" width="1680" />
  </figure>
  <p id="aB6Y">Система невидимой проверки зеленым вывела, что задание выполнено. </p>
  <p id="UGHl"><strong>Поздравляем!</strong> Вы составили и выполнили первое задание под управлением системы невидимой автоматической проверки!</p>
  <h2 id="NQ9r">Составление нескольких тестов</h2>
  <p id="dd1f">Ученик может обмануть данный алгоритм проверки. Например, он может ввести три единицы и не записывая алгоритм, вывести 0 в качестве результата:</p>
  <figure id="wDob" class="m_column">
    <img src="https://img4.teletype.in/files/33/13/3313b1d5-dfcd-4e46-8d33-7abbeb26f24c.png" width="1680" />
  </figure>
  <p id="DI15">Это означает, что проверка задания составлена некачественно. </p>
  <p id="pLGW">Вернемся к заданию и добавим к проверке дополнительные тесты. Сделать это просто:</p>
  <figure id="aPJ7" class="m_column">
    <img src="https://img2.teletype.in/files/13/2e/132e1b50-13b4-4e56-a365-a010c7536815.png" width="1680" />
  </figure>
  <p id="rDLe">Строка</p>
  <pre id="piQW">GenerateTests(10,tInt(N,N) + tRe(1,10) * N);</pre>
  <p id="uo2T">означает &quot;сгенерировать 10 автоматических тестов, каждый тест в качестве входных данных будет содержать одно целое число в диапазоне от N до N (то есть, само число N) и затем N вещественных чисел в диапазоне от 1 до 10&quot;. </p>
  <p id="xZpo">Здесь tInt, tRe, tChr, tBoo - это функции, которые вызываются только при генерации тестов для задания алгоритма генерации случайного тестового входного значения нужного типа в указанном диапазоне. </p>
  <p id="GQ6k">После этого запуск предыдущего неправильного решения ученика приведет к следующему выводу:</p>
  <figure id="HJUn" class="m_column">
    <img src="https://img2.teletype.in/files/d0/6a/d06a6f7f-a428-4c46-a0f0-cf8b947973e6.png" width="1680" />
  </figure>
  <p id="1ntX">Итак, произошло следующее:</p>
  <ol id="UGrY">
    <li id="94a4">Ученик попытался обмануть систему, ввел определённые данные, не написал алгоритм и вывел результат для этих данных.</li>
    <li id="4mk3">Система невидимой проверки проверила его решение и посчитала его верным.</li>
    <li id="w7Lm">Система невидимой проверки не поверила ученику и предложила 10 своих тестов, где в качестве входных данных сгенерировала случайные значения по спецификации tInt(N,N) + tRe(1,10) * N</li>
    <li id="tcVQ">На одном из таких тестов 3 7 8 5 (сгенерировано автоматически 3 вещественных числа 7 8 5) система &quot;поймала&quot; ученика, вывела данные теста, вывела неправильный ответ ученика и правильный результат, который необходимо было вывести если бы эти данные были бы введены.</li>
  </ol>
  <p id="jrpw">То есть, систему проверки обмануть не удалось. Теперь только правильный алгоритм будет оценен чекером как выполненное задание:</p>
  <figure id="QYSd" class="m_column">
    <img src="https://img2.teletype.in/files/dc/82/dc823629-b270-4781-8baa-4fdf7b6e0604.png" width="1680" />
  </figure>
  <h2 id="6ohB">Протокол выполнения заданий учеником</h2>
  <p id="Rc5J">Протокол выполнения заданий учеником хранится в файле db.txt в папке урока. Он имеет вид:</p>
  <pre id="4q7p">Урок-1. ArrTask1 2023-11-01 11:36:05Z IOError InputCount(1,4)
Урок-1. ArrTask1 2023-11-01 11:36:59Z IOError InputCount(1,4)
Урок-1. ArrTask1 2023-11-01 11:38:05Z IOError InputCount(1,4)
Урок-1. ArrTask1 2023-11-01 11:56:53Z IOError InputCount(1,4)
Урок-1. ArrTask1 2023-11-01 11:59:13Z IOError InputType(2,real,integer)
Урок-1. ArrTask1 2023-11-01 12:00:05Z IOError OutputCount(0,2)
Урок-1. ArrTask1 2023-11-01 12:46:46Z BadSolution NoInfo
Урок-1. ArrTask1 2023-11-01 12:48:19Z Solved NoInfo</pre>
  <p id="qKrX">При каждом запуске в конец этого файла добавляется строка, характеризующая текущий запуск. Она содержит имя урока, имя задания, время запуска и результат запуска - степень выполнения и дополнительные параметры. По данному содержимому видно, что ученик 4 раза запустил программу, в которой было введено всего одно число, после этого он ввел неверный тип при вводе массива (ошибка InputType(2,real,integer) во втором элементе), затем он выводил неверное количество данных (ошибка OutputCount(0,2) - выведено 0, а надо вывести 2 элемента), после этого неправильно решил задание (BadSolution) и наконец решил правильно (Solved).</p>
  <h2 id="loHM">Разработка следующих заданий урока</h2>
  <p id="1uYF">Следующие задания разрабатываются существенно проще.</p>
  <p id="1WW2">Скажем, мы добавляем в папку урока новое задание ArrTask2.pas с формулировкой задания в комментарии. Тогда чтобы оно стало проверяемым, мы также должны добавить в модуль Tasks.pas строки </p>
  <p id="bZox">Мы также должны добавить в модуль Tasks.pas строки </p>
  <pre id="VL08" data-lang="pascal">    &#x27;ArrTask2&#x27;: begin
      // Здесь - текст чекера
    end;</pre>
  <h2 id="QC83">Заключение</h2>
  <p id="fFXv">В данной статье показано использование системы невидимой автоматической проверки, имеющей существенные отличия от всех известных систем автоматической проверки:</p>
  <p id="mYce">1. Разработка заданий осуществляется в той же среде, что и решение заданий учеником</p>
  <p id="d1bq">2. Разработка заданий проста и для последующих задач по данной теме занимает 2-3 минуты на задание.</p>
  <p id="JCJ4">3. Задания проверяют типы при вводе-выводе (в системах олимпиадного тестирования из файла считываются лишь текстовые данные, что крайне бедно)</p>
  <p id="gPUm">4. Решение задания учеником выполняется непосредственно в среде PascalABC.NET без каких-то дополнительных действий. Текст программы не надо копировать на интернет-страницу сетевой тестирующей системы.</p>
  <p id="AuQq">5. Задание проверяется в момент запуска программы ученика - нет ожидания ответа от сервера.</p>
  <p id="FEes"></p>
  <p id="R7Vx">В следующих статьях о системе LightPT мы рассмотрим более сложные случаи составления заданий.<br /> </p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@pascalabcnet/Visitors</guid><link>https://teletype.in/@pascalabcnet/Visitors?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet</link><comments>https://teletype.in/@pascalabcnet/Visitors?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=pascalabcnet#comments</comments><dc:creator>pascalabcnet</dc:creator><title>Пишем интерпретатор на PascalABC.NET. Визиторы по синтаксическому дереву</title><pubDate>Mon, 10 Jul 2023 08:46:00 GMT</pubDate><description><![CDATA[Этот текст - продолжение публикации https://teletype.in/@pascalabcnet/InterpretASTTree. ]]></description><content:encoded><![CDATA[
  <p id="EYsJ">Этот текст - продолжение публикации <a href="https://teletype.in/@pascalabcnet/InterpretASTTree" target="_blank">https://teletype.in/@pascalabcnet/InterpretASTTree</a>. </p>
  <p id="SMJN"><strong>Пока это последняя статья цикла</strong>.</p>
  <p id="ZHW3">В данной статье мы рассмотрим, как обойти полученное синтаксическое дерево AST и какие задачи с помощью этого обхода можно решать.</p>
  <p id="lOQm">Итак, мы построили синтаксическое дерево. Его корнем является узел всей программы, остальные вершины являются операторами, выражениями и листовые вершины являются терминалами: целыми и вещественными константами, идентификаторами.</p>
  <p id="e0VB">По синтаксическому дереву можно собрать большое количество полезной информации о программе: например, сколько в программе присваиваний, циклов, какие используются переменные и проч.</p>
  <p id="lUvZ">По синтаксическому дереву можно восстановить текст программы - причем, можно это сделать на другом языке: например, программа будет на языке C, а восстановим мы ее на языке Паскаль.</p>
  <p id="h8B7">По синтаксическому дереву можно сделать ряд семантических проверок: верно ли описаны переменные, правильно ли типизированы выражения и проч. Семантическими проверками мы займемся в одной из следующих статей.</p>
  <p id="Fra4">А пока нам надо понять, как решать разные задачи обхода синтаксического дерева, где накапливать полученную информацию. </p>
  <p id="mTd0">Для решения этой важнейшей задачи нам поможет так называемый <a href="https://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D1%81%D0%B5%D1%82%D0%B8%D1%82%D0%B5%D0%BB%D1%8C_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)" target="_blank">паттерн проектирования Визитор</a> (Посетитель).</p>
  <p id="RPxd">Прежде всего, давайте разберемся, что такое <strong>паттерны проектирования</strong>. Паттерн проектирования - это повторяющаяся в разных проектах архитектурная конструкция кода, состоящая из нескольких взаимодействующих классов и решающая определенную задачу. Паттерны проектирования впервые были описаны в книге Э.Гаммы и др. &quot;<a href="https://ru.wikipedia.org/wiki/Design_Patterns" target="_blank">Приёмы объектно-ориентированного проектирования. Паттерны проектирования</a><strong>&quot;</strong>. </p>
  <p id="6e68">Паттерн Визитор решает следующую проблему. Узлы синтаксического дерева имеют разные типы, и в каждом узле в зависимости от его типа необходимо выполнить определенное действие. Например, в каждом узле можно вернуть строку - правильно отформатированный код части программы, представляемый этим узлом. Но таких задач может быть много. Встраивать подобные действия в узлы дерева неэффективно - для ькаждой задачи в узел потребуется добавить один метод, и количество таких методов будет разрастаться. Паттерн Визитор предлагает выделить все действия по решению определенной задачи в отдельный класс, называемый визитором. Тем самым код узлов синтаксического дерева не будет захламляться. </p>
  <p id="asJo">Рассмотрим интерфейс визитора по нашему синтаксическому дереву:</p>
  <pre id="T53n">type
  IVisitor&lt;T&gt; = interface
    function VisitNode(bin: Node): T;
    function VisitExprNode(bin: ExprNode): T;
    function VisitStatementNode(bin: StatementNode): T;
    function VisitBinOp(bin: BinOpNode): T;
    function VisitStatementList(stl: StatementListNode): T;
    function VisitExprList(exlist: ExprListNode): T;
    function VisitInt(n: IntNode): T;
    function VisitDouble(d: DoubleNode): T;
    function VisitId(id: IdNode): T;
    function VisitAssign(ass: AssignNode): T;
    function VisitAssignPlus(ass: AssignPlusNode): T;
    function VisitIf(ifn: IfNode): T;
    function VisitWhile(whn: WhileNode): T;
    function VisitProcCall(p: ProcCallNode): T;
    function VisitFuncCall(f: FuncCallNode): T;
  end;</pre>
  <p id="H4Ke">Он состоит из заголовков функций - каждая отвечает за свой тип узла и возвращает некоторое значение типа T. В классах, реализующих этот интерфейс, мы будем возвращать конкретные типы T. Например, в визиторе PrettyPrinter T=string и мы будем в каждой функции строковое представление части кода, связанной с этим узлом.</p>
  <p id="aKGr">Обратим внимание, что мы можем создать какое угодно количество конкретных визиторов - классов, реализующих этот интерфейс для решения разных задач - код узлов дерева при этом не изменится!</p>
  <p id="tZdf">Для того чтобы паттерн Визитор автоматически вызывал эти методы для каждого типа узла, мы должны встроить в каждый узел синтаксического дерева специальный метод Visit:</p>
  <pre id="dETl"> type
  Node = class
    pos: Position;    
  public
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; virtual := v.VisitNode(Self);
  end;
  ExprNode = class(Node)
  public
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitExprNode(Self);
  end;
  StatementNode = class(Node)
  public
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override 
      := v.VisitStatementNode(Self);
  end;
  BinOpNode = class(ExprNode)
  public
    Left,Right: ExprNode;
    Op: OpType;
    constructor (Left,Right: ExprNode; Op: OpType; pos: Position) := 
      (Self.Left,Self.Right,Self.Op,Self.pos) := (Left,Right,Op,pos);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitBinOp(Self);
  end;
  StatementListNode = class(StatementNode)
  public
    lst: List&lt;StatementNode&gt; := new List&lt;StatementNode&gt;;
    procedure Add(st: StatementNode) := lst.Add(st);    
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitStatementList(Self);
  end;
  ExprListNode = class(Node)
  public
    lst: List&lt;ExprNode&gt; := new List&lt;ExprNode&gt;;
    procedure Add(ex: ExprNode) := lst.Add(ex);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitExprList(Self);
  end;
  IntNode = class(ExprNode)
  public
    Value: integer;
    constructor(Value: integer; pos: Position) 
    := (Self.Value,Self.pos) := (Value,pos);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitInt(Self);
  end;
  DoubleNode = class(ExprNode)
  public
    Value: real;
    constructor(Value: real; pos: Position) := (Self.Value,Self.pos) := (Value,pos);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitDouble(Self);
  end;
  IdNode = class(ExprNode)
  public
    Name: string;
    constructor(name: string; pos: Position) := (Self.Name,Self.pos) := (name,pos);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitId(Self);
  end;
  AssignNode = class(StatementNode)
  public
    Ident: IdNode;
    Expr: ExprNode;
    constructor(Ident: IdNode; Expr: ExprNode; pos: Position) := (Self.Ident,Self.Expr,Self.pos) := (Ident,Expr,pos);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitAssign(Self); 
  end;
  AssignPlusNode = class(StatementNode)
  public
    Ident: IdNode;
    Expr: ExprNode;
    constructor(Ident: IdNode; Expr: ExprNode; pos: Position) := (Self.Ident,Self.Expr,Self.pos) := (Ident,Expr,pos);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitAssignPlus(Self); 
  end;
  IfNode = class(StatementNode)
  public
    Condition: ExprNode;
    ThenStat,ElseStat: StatementNode;
    constructor(Condition: ExprNode; ThenStat,ElseStat: StatementNode; pos: Position) 
      := (Self.Condition,Self.ThenStat,Self.ElseStat,Self.pos) := (Condition,ThenStat,ElseStat,pos);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitIf(Self);
  end;
  WhileNode = class(StatementNode)
  public
    Condition: ExprNode;
    Stat: StatementNode;
    constructor(Condition: ExprNode; Stat: StatementNode; pos: Position) 
      := (Self.Condition,Self.Stat,Self.pos) := (Condition,Stat,pos);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitWhile(Self);
  end;
  ProcCallNode = class(StatementNode)
  public
    Name: IdNode;
    Pars: ExprListNode;
    constructor(Name: IdNode; Pars: ExprListNode; pos: Position) := (Self.Name,Self.Pars,Self.pos) := (Name,Pars,pos);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitProcCall(Self);
  end;
  FuncCallNode = class(ExprNode)
  public
    Name: IdNode;
    Pars: ExprListNode;
    constructor(Name: IdNode; Pars: ExprListNode; pos: Position) := (Self.Name,Self.Pars,Self.pos) := (Name,Pars,pos);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitFuncCall(Self);
  end;</pre>
  <p id="wrGQ">В каждом методе Visit мы передаем единственным параметром интерфейс описанного нами визитора и в теле метода вызываем соответствующий данному синтаксическому узлу метод визитора. Например, в теле метода Visit класса BinOpNode мы вызываем метод v.VisitBinOp(Self), передавая ему в качестве параметра Self - ссылку на текущий узел синтаксического дерева.</p>
  <p id="zf7X">Что мы таким образом приобретаем? </p>
  <p id="N7Yz"></p>
  <p id="X1tB">Предположим, мы написали определенный визитор MyVisitor, реализующий интерфейс IVisitor&lt;T&gt; и создали объект этого визитора в переменной v. Тогда вызов метода  Visit например для узла BinOpNode вызовет, как мы уже отметили, метод v.VisitBinOp(Self) для нашего класса MyVisitor, тем самым, выполнится конкретное действие, которое мы предусмотрели для узла BinOpNode в классе MyVisitor. </p>
  <p id="kD7a">В итоге каждый визитор реализует свой набор действий для разных типов узлов синтаксического дерева, решая какую-то конкретную задачу. А таких задач будет много.</p>
  <p id="mUY2">Приведем полный код модуля ASTNodes с интерфейсом визитора:</p>
  <pre id="PhkU">unit ASTNodes;
uses Common;

type OpType = (opPlus, opMinus, opMultiply, opDivide, 
  opEqual, opLess, opLessEqual, opGreater, opGreaterEqual, opNotEqual, 
  opAnd, opOr, opNot, opBad);

var OpToStr := Dict(
    (opPlus,$&#x27;+&#x27;),(opMinus,$&#x27;-&#x27;),(opMultiply,$&#x27;*&#x27;),(opDivide,$&#x27;/&#x27;),
    (opEqual,$&#x27;==&#x27;),(opLess,$&#x27;&lt;&#x27;),(opLessEqual,$&#x27;&lt;=&#x27;),(opGreater,$&#x27;&gt;&#x27;),(opGreaterEqual,$&#x27;&gt;=&#x27;),(opNotEqual,$&#x27;!=&#x27;),
    (opAnd,$&#x27;&amp;&amp;&#x27;),(opOr,$&#x27;||&#x27;),(opNot,$&#x27;!&#x27;)
  );

type 
  Node = class;
  ExprNode = class;
  StatementNode = class;
  BinOpNode = class;
  StatementListNode = class;
  ExprListNode = class;
  IntNode = class;
  DoubleNode = class;
  IdNode = class;
  AssignNode = class;
  AssignPlusNode = class;
  IfNode = class;
  WhileNode = class;
  ProcCallNode = class;
  FuncCallNode = class;
  IVisitor&lt;T&gt; = interface
    function VisitNode(bin: Node): T;
    function VisitExprNode(bin: ExprNode): T;
    function VisitStatementNode(bin: StatementNode): T;
    function VisitBinOp(bin: BinOpNode): T;
    function VisitStatementList(stl: StatementListNode): T;
    function VisitExprList(exlist: ExprListNode): T;
    function VisitInt(n: IntNode): T;
    function VisitDouble(d: DoubleNode): T;
    function VisitId(id: IdNode): T;
    function VisitAssign(ass: AssignNode): T;
    function VisitAssignPlus(ass: AssignPlusNode): T;
    function VisitIf(ifn: IfNode): T;
    function VisitWhile(whn: WhileNode): T;
    function VisitProcCall(p: ProcCallNode): T;
    function VisitFuncCall(f: FuncCallNode): T;
  end;
  IVisitorP = interface
    procedure VisitNode(bin: Node);
    procedure VisitExprNode(bin: ExprNode);
    procedure VisitStatementNode(bin: StatementNode);
    procedure VisitBinOp(bin: BinOpNode);
    procedure VisitStatementList(stl: StatementListNode);
    procedure VisitExprList(exlist: ExprListNode);
    procedure VisitInt(n: IntNode);
    procedure VisitDouble(d: DoubleNode);
    procedure VisitId(id: IdNode);
    procedure VisitAssign(ass: AssignNode);
    procedure VisitAssignPlus(ass: AssignPlusNode);
    procedure VisitIf(ifn: IfNode);
    procedure VisitWhile(whn: WhileNode);
    procedure VisitProcCall(p: ProcCallNode);
    procedure VisitFuncCall(f: FuncCallNode);
  end;
  
  Node = class
    pos: Position;    
  public
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; virtual := v.VisitNode(Self);
  end;
  ExprNode = class(Node)
  public
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitExprNode(Self);
  end;
  StatementNode = class(Node)
  public
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitStatementNode(Self);
  end;
  BinOpNode = class(ExprNode)
  public
    Left,Right: ExprNode;
    Op: OpType;
    constructor (Left,Right: ExprNode; Op: OpType; pos: Position) := 
      (Self.Left,Self.Right,Self.Op,Self.pos) := (Left,Right,Op,pos);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitBinOp(Self);
  end;
  StatementListNode = class(StatementNode)
  public
    lst: List&lt;StatementNode&gt; := new List&lt;StatementNode&gt;;
    procedure Add(st: StatementNode) := lst.Add(st);    
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitStatementList(Self);
  end;
  ExprListNode = class(Node)
  public
    lst: List&lt;ExprNode&gt; := new List&lt;ExprNode&gt;;
    procedure Add(ex: ExprNode) := lst.Add(ex);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitExprList(Self);
  end;
  IntNode = class(ExprNode)
  public
    Value: integer;
    constructor(Value: integer; pos: Position) := (Self.Value,Self.pos) := (Value,pos);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitInt(Self);
  end;
  DoubleNode = class(ExprNode)
  public
    Value: real;
    constructor(Value: real; pos: Position) := (Self.Value,Self.pos) := (Value,pos);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitDouble(Self);
  end;
  IdNode = class(ExprNode)
  public
    Name: string;
    constructor(name: string; pos: Position) := (Self.Name,Self.pos) := (name,pos);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitId(Self);
  end;
  AssignNode = class(StatementNode)
  public
    Ident: IdNode;
    Expr: ExprNode;
    constructor(Ident: IdNode; Expr: ExprNode; pos: Position) := (Self.Ident,Self.Expr,Self.pos) := (Ident,Expr,pos);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitAssign(Self); 
  end;
  AssignPlusNode = class(StatementNode)
  public
    Ident: IdNode;
    Expr: ExprNode;
    constructor(Ident: IdNode; Expr: ExprNode; pos: Position) := (Self.Ident,Self.Expr,Self.pos) := (Ident,Expr,pos);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitAssignPlus(Self); 
  end;
  IfNode = class(StatementNode)
  public
    Condition: ExprNode;
    ThenStat,ElseStat: StatementNode;
    constructor(Condition: ExprNode; ThenStat,ElseStat: StatementNode; pos: Position) 
      := (Self.Condition,Self.ThenStat,Self.ElseStat,Self.pos) := (Condition,ThenStat,ElseStat,pos);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitIf(Self);
  end;
  WhileNode = class(StatementNode)
  public
    Condition: ExprNode;
    Stat: StatementNode;
    constructor(Condition: ExprNode; Stat: StatementNode; pos: Position) 
      := (Self.Condition,Self.Stat,Self.pos) := (Condition,Stat,pos);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitWhile(Self);
  end;
  ProcCallNode = class(StatementNode)
  public
    Name: IdNode;
    Pars: ExprListNode;
    constructor(Name: IdNode; Pars: ExprListNode; pos: Position) := (Self.Name,Self.Pars,Self.pos) := (Name,Pars,pos);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitProcCall(Self);
  end;
  FuncCallNode = class(ExprNode)
  public
    Name: IdNode;
    Pars: ExprListNode;
    constructor(Name: IdNode; Pars: ExprListNode; pos: Position) := (Self.Name,Self.Pars,Self.pos) := (Name,Pars,pos);
    function Visit&lt;T&gt;(v: IVisitor&lt;T&gt;): T; override := v.VisitFuncCall(Self);
  end;
end.</pre>
  <p id="I3w3">Напишем простой прикладной визитор простой печати текста программы по синтаксическому дереву. Унаследуем его от IVisitor&lt;string&gt;. Это значит, что каждый его метод будет возвращать строку - часть текста программы, соответствующая данному узлу. Поместим этот визитор в модуль SimplePrint:</p>
  <pre id="u4Im">unit SimplePrint;
uses ASTNodes;
type 
  SimplePrintVisitor = class(IVisitor&lt;string&gt;)
  public  
    function VisitNode(n: Node): string := n.Visit(Self);
    function VisitExprNode(ex: ExprNode): string := ex.Visit(Self);
    function VisitStatementNode(st: StatementNode): string := st.Visit(Self);
    function VisitBinOp(bin: BinOpNode): string := bin.Left.Visit(Self) + OpToStr[bin.Op] + bin.Right.Visit(Self);
    function VisitStatementList(stl: StatementListNode): string := 
      stl.lst.Select(x -&gt; x.Visit(Self)).JoinToString(NewLine);
    function VisitExprList(exlist: ExprListNode): string := exlist.lst.Select(x -&gt; x.Visit(Self)).JoinToString(&#x27;,&#x27;);
    function VisitInt(n: IntNode): string := n.Value.ToString;
    function VisitDouble(d: DoubleNode): string := d.Value.ToString;
    function VisitId(id: IdNode): string := id.Name;
    function VisitAssign(ass: AssignNode): string := ass.Ident.Name + &#x27; := &#x27; + ass.Expr.Visit(Self);
    function VisitAssignPlus(ass: AssignPlusNode): string := ass.Ident.Name + &#x27; += &#x27; + ass.Expr.Visit(Self);
    function VisitIf(ifn: IfNode): string 
      := &#x27;if &#x27; + ifn.Condition.Visit(Self) + &#x27; then &#x27;+ ifn.ThenStat.Visit(Self) 
        + (ifn.ElseStat = nil ? &#x27;&#x27; : ifn.ElseStat.Visit(Self));
    function VisitWhile(whn: WhileNode): string
      := &#x27;while &#x27;+ whn.Condition.Visit(Self) + &#x27; do &#x27;+ NewLine + whn.Stat.Visit(Self);
    function VisitProcCall(p: ProcCallNode): string := p.Name.Name + &#x27;(&#x27; + p.Pars.Visit(Self) + &#x27;)&#x27;;
    function VisitFuncCall(f: FuncCallNode): string := f.Name.Name + &#x27;(&#x27; + f.Pars.Visit(Self) + &#x27;)&#x27;;
  end;
end.</pre>
  <p id="hFhp">Воспользуемся им в основной программе:</p>
  <pre id="IV9J">uses LexUnit,ParseUnit,SemanticCheck,Common,SimplePrint;
begin
  var text := &#x27;i = 1; sum = 0.0; while i&lt;100000000 do {sum += 1/i; i += 1}; print(sum)&#x27;;  
  var lex := new Lexer(text);
  try
    var par := new Parser(lex);
    var progr := par.MainProgram;
    var semcheck := new SemanticCheckVisitor;
    progr.VisitP(semcheck);
    progr.Visit(new SimplePrintVisitor).Println;
  except
    on e: LexerException do
      OutputError(&#x27;Лексическая ошибка:&#x27;,e,lex.Lines);
    on e: SyntaxException do
      OutputError(&#x27;Синтаксическая ошибка:&#x27;,e,lex.Lines);
    on e: SemanticException do
      OutputError(&#x27;Семантическая ошибка:&#x27;,e,lex.Lines);
  end;  
end.</pre>
  <p id="Wf5e">Результат:</p>
  <pre id="b1rn">i := 1
sum := 0
while i&lt;100000000 do 
sum += 1/i
i += 1
print(sum) </pre>
  <p id="wYFX"><strong>Заключение</strong>. Мы рассмотрели важный для создания интерпретаторов и достаточно сложный паттерн проектирования Визитор, применили его к нашему синтаксическому дереву и реализовали конкретный визитор для упрощенного вывода текста программы, занимающий около 20 строк.</p>

]]></content:encoded></item></channel></rss>