Редактировать

PascalABC.NET - современный диалект языка программирования Паскаль, позволяющий записывать код компактно и понятно, используя современные языковые возможности. Это позволяет записывать программу яснее и как следствие сократить число возможных ошибок на ЕГЭ по информатике, связанных с волнением и другими субъективными причинами.

Данный текст составлен разработчиками языка и рассматривает ряд вопросов, связанных с использованием PascalABC.NET при сдаче ЕГЭ по информатике. Он ориентирован:

  • на школьников, использующих при сдаче ЕГЭ PascalABC.NET как язык реализации программ
  • на преподавателей, которые при подготовке школьников к сдаче ЕГЭ по информатике используют PascalABC.NET
  • на проверяющих ЕГЭ (данный текст можно использовать как справочник по конструкциям, отсутствующим в стандартном Паскале)

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

Важно! Данный текст не рассматривает вопросы, связанные с методикой решения задач. Он лишь описывает то, как на PascalABC.NET сделать запись алгоритмов лучше, сохранив при этом эффективность.

Прежде всего, речь идёт только о задачах 25 и 27, где требуется написать текст программы на языке программирования (рассматривается Демо ЕГЭ по информатике 2020). Причем, в задаче 25 уже дан код на стандартном языке Паскаль, но сказано, что можно реализовывать на другом языке, используя те же самые исходные данные и переменные, что приведены в модельном решении.

Итак, самое главное: в каждом из заданий 25 и 27 следует указать язык и версию реализованной программы. Здесь в начале решения следует написать “Используется версия PascalABC.NET 3.6.3 или выше” - это снимет все вопросы у добросовестного проверяющего.

Отметим, что только в задаче 27 требуется эффективность программы по времени и по памяти, в задаче 25 этого не требуется.

PascalABC.NET имеет множество языковых возможностей и множество стилей программирования, поскольку обобщает современные языковые и библиотечные возможности сразу нескольких современных языков программирования (C#, Python, Kotlin).

При решении задач ЕГЭ по информатике мы рекомендуем использовать лишь ограниченный набор возможностей PascalABC.NET, которые делают текст программы яснее и короче, позволяя концентрироваться на сути алгоритма, а не на технических деталях.

К базовым возможностям языка, рекомендуемым нами при решении задач ЕГЭ, относятся:

  1. Описания переменных внутри блока в том месте, где они впервые потребовались. Это ликвидирует длинные перечни описания переменных до beginа основной программы, ухудшающие читаемость и лёгкость написания программы.
  2. Автовывод типа переменной при описании с инициализацией (var a := 1).
  3. Использование описания счётчика цикла for в заголовке цикла (for var i).
  4. Функции ввода вида ReadInteger, ReadReal, ReadInteger2 и т.д., позволяющие одной строкой описывать и вводить переменную в любом месте операторного блока программы (var a := ReadInteger).
  5. Процедуры вывода Print, Println, автоматически разделяющие элементы вывода пробелами.
  6. Цикл loop - аналог цикла for, использующийся когда счётчик цикла не нужен.
  7. Кортежи и распаковка кортежей в переменные, называемая также множественным присваиванием: (a,b) := (1,1).

Кроме того, в некоторых алгоритмах уместно использование лямбда-выражений как параметров стандартных методов (об этом мы будем говорить позже - при решении задачи 25).

Все представленные здесь возможности направлены на лучшую запись кода, которая должна быть хорошо понятна проверяющим. Кроме того, эффективность некоторых используемых возможностей (языковых или библиотечных) нами будет особо оговариваться для каждой конкретной задачи ЕГЭ.

Для того чтобы сразу продемонстрировать преимущества использования PascalABC.NET перед стандартным Паскалем, рассмотрим 2 решения задачи 27 (формулировку см. здесь: Демо ЕГЭ по информатике 2020).

Решение 1 задачи 27 (Демо ЕГЭ 2020 года). Стандартный Паскаль, решение предлагается в тексте Демо ЕГЭ по информатике 2020 как эталонное

const
  m = 120; {количество различных остатков}

var
  {хранение максимального значения для каждого из остатков}
  r: array[0..m - 1] of integer;
  n, a, i, p, left, right: integer;

begin
  readln(n);
  {обнуление массива r}
  for i := 0 to m - 1 do
    r[i] := 0;
  {обнуление переменных для записи ответа}
  left := 0; right := 0;
  {ввод значений, поиск искомой пары}
  for i := 1 to n do
  begin
    readln(a); {считываем очередное значение}
    p := a mod m;
    if p = 0 then
    begin
      if (r[0] > a) and (r[0] + a > left + right) then
      begin
        left := r[0]; right := a {обновление ответа}
      end
    end
    else
    begin
      if (r[m - p] > a) and (r[m - p] + a > left + right) then
      begin
        left := r[m - p]; right := a {обновление ответа}
      end
    end;
    {обновление элемента r для соответствующего остатка}
    if a > r[p] then r[p] := a
  end;
  writeln(left, ' ', right)
end.

Решение 2 задачи 27 (Демо ЕГЭ 2020 года). Используются возможности PascalABC.NET

// PascalABC.NET версии 3.6.3
const
  m = 120;

begin
  var r := ArrFill(m, 0);
  var (left, right) := (0, 0);
  
  var n := ReadInteger;
  loop n do
  begin
    var a := ReadInteger;
    var p := a mod m;
    var b := r[(m - p) mod m];
    
    if (b > a) and (b + a > left + right) then
      (left, right) := (b, a);
    
    if a > r[p] then r[p] := a
  end;
  Println(left, right)
end.

Решение на PascalABC.NET полностью идентично эталонному, но оно в два раза более короткое, проще воспринимается и имеет меньшую вероятность совершить ошибку в стрессовой ситуации экзамена.

Рассмотрим его основные элементы и их отличие от стандартного Паскаля.

  • ArrFill(m,0) - стандартная функция, выделяющая память под m элементов динамического массива, заполняющего его нулями и возвращающая ссылку на него.

  • В операторе var r := ArrFill(m,0) описывается и инициализируется массив r, при этом тип r автоматически выводится компилятором: это array of integer. В любом случае вопрос о неинициализации массива снимается (!)

  • В записи var (left,right) := (0,0); описываются сразу две переменные целого типа, и они инициализируются нулями (используется так называемая распаковка кортежа в переменные при инициализации).

  • Описание целой переменной n и инициализация её целым значением, вводимым с клавиатуры, осуществляется оператором var n := ReadInteger;

  • Множественное присваивание (left, right) := (b, a); избавляет нас от необходимости использовать составной оператор по ветви then оператора if. Сравним:

    if (b > a) and (b + a > left + right) then
    begin
     left := b;
     right := a;
    end;    
    

    и

    if (b > a) and (b + a > left + right) then
      (left, right) := (b, a);
    

Рассмотрим также три решения задачи 25 (формулировку см. здесь: Демо ЕГЭ по информатике 2020). Кратко также приводим формулировку ниже:

Дан целочисленный массив из 30 элементов. Элементы массива могут принимать натуральные значения от 1 до 10 000 включительно. Опишите на одном из языков программирования алгоритм, который находит минимум среди элементов массива, не делящихся нацело на 6, а затем заменяет каждый элемент, не делящийся нацело на 6, на число, равное найденному минимуму. Гарантируется, что хотя бы один такой элемент в массиве есть. В качестве результата необходимо вывести изменённый массив, каждый элемент выводится с новой строчки.

В качестве ответа Вам необходимо привести фрагмент программы, который должен находиться на месте многоточия.

const
  N = 30;
var
  a: array [1..N] of longint;
  i, j, k: longint;

begin
  for i := 1 to N do
    readln(a[i]);
  ...
end.

Вы можете записать решение также на другом языке программирования (укажите название и используемую версию языка программирования, например Free Pascal 2.6). В этом случае Вы должны использовать те же самые исходные данные и переменные, какие были предложены в условии.

Решение 1 задачи 25 (Демо ЕГЭ 2020 года). Стандартный Паскаль, решение предлагается в тексте Демо ЕГЭ по информатике 2020 как эталонное

const
  N = 30;

var
  a: array [1..N] of longint;
  i, j, k: longint;

begin
  for i := 1 to N do
    readln(a[i]);
  k := 10000;
  for i := 1 to N do
    if (a[i] mod 6 <> 0) and (a[i] < k) then
      k := a[i];
  for i := 1 to N do
  begin
    if (a[i] mod 6 <> 0) then
      a[i] := k;
    writeln(a[i]);
  end;
end.

Решение 2 задачи 25 (Демо ЕГЭ 2020 года). Используется PascalABC.NET

// PascalABC.NET версия 3.6.3
begin
  var a := ReadArrInteger(30);

  var k := a.Where(j -> j mod 6 <> 0).Min;
  
  for var i:=0 to a.Length-1 do
    if a[i] mod 6 <> 0 then
      a[i] := k;
  
  a.PrintLines;
end.

Данное решение структурировано существенно лучше, чем оригинальное:

  1. Описание и ввод массива осуществляется одной строкой: var a := ReadArrInteger(30)

  2. Фильтрация элементов и поиск среди них минимального отделены друг от друга: мы вначале фильтруем элементы a.Where(k -> k mod 6 <> 0) и затем находим у отфильтрованных минимальный (согласно условию задачи он обязательно есть).

  3. Важно отметить, что оригинальное решение содержит “одноразовый” способ вычисления минимума - переменной k вначале присваивается значение 10000. Это признак некачественной программы - стОит немного изменить условие (данные в бОльшем диапазоне) - и решение окажется неправильным. Кроме того, минимум вычисляется вместе с проверкой условия, т.е. смешиваются две задачи, а в решении на PascalABC.NET вначале делается фильтрация и только потом - поиск минимума. При этом после фильтрации данные не сохраняются в промежуточной структуре, поэтому решение столь же эффективно, что и оригинальное.

  4. Затем мы вновь делаем цикл по всем элементам, но поскольку нам некоторые надо менять, фильтрация с помощью Where нам не поможет - она лишь отбирает элементы, но “забывает” про их индексы. Поэтому просто проверяем условие a[i] mod 6 <> 0 как в оригинальном решении

  5. Вывод массива по строкам осуществляется одной строкой: a.PrintLines и не смешивается с алгоритмом как в оригинальном решении

Решение 3 задачи 25 (Демо ЕГЭ 2020 года). Используется PascalABC.NET

// PascalABC.NET версия 3.6.3
begin
  var a := ReadArrInteger(30);

  var k := a.Where(j -> j mod 6 <> 0).Min;
  foreach var i in a.Indices(j -> j mod 6 <> 0) do
    a[i] := k;
  
  a.PrintLines;
end.

В алгоритме 3 мы показываем естественную модификацию решения 2. Здесь необходимо использовать метод a.Indices, отбирающий только те индексы в массиве a, элементы которых удовлетворяют указанному условию.

Отметим, что этот способ лишь немного короче предыдущего и содержит дополнительный “редкий” метод, который надо запомнить.

Поэтому для решения задачи 25 демо ЕГЭ 2020 мы рекомендуем способ 2.