- Go Back -

Олимпиадное программирование, или как мы убиваем читабильность и качество кода

Во всех примерах я буду использовать либо C++, либо Python

Предисловие


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

Вопросы автору:


Первое, о чем мне бы хотелось поговорить, это читабильность кода.

Ален Голуб в своей книге «Веревка достаточной длины, чтобы выстрелить себе в ногу» пишет, что «читабельность кода — это способность человека понять, что делает код, не запуская его». Читабельность кода важна не только для того, чтобы другие разработчики могли легко разобраться в вашем коде, но и для того, чтобы вы сами могли поддерживать и улучшать свой код в будущем.

В своей жизни, я много вижу, как люди укорачивают названия переменных на олимпиадах, и это больше всего меня злит. Почему? Потому что во многих книгах, да взять даже того же Роберта Мартина с книгой «Чистый код / Создание, анализ и рефакторинг», то даже там говорится, что название переменной, функции или структуры данных должно иметь название с кратким описанием, смотря на которое мы сразу сможем понять, зачем этот кусок кода нужен. Я соглашусь использовать короткие наименования, если дело касается математических переменных. Давайте посмотрим на пример:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//-| Плохой стиль именований |----
#include <iostream>

int x;  // Переменная с неясным предназначением
void p();  // Функция с неясным назначением

int main() 
{
    x = 5;
    p();

    std::cout << "x: " << x << std::endl;
    return 0;
}

void p() 
{
    // Изменение значения неясной переменной
    x = 10;
}



//-| Хороший стиль именований |----
#include <iostream>

int userAge;  // Возраст пользователя
void printUserAge();  // Функция для вывода возраста пользователя

int main() 
{
    userAge = 25;
    printUserAge();

    std::cout << "User's Age: " << userAge << std::endl;
    return 0;
}

void printUserAge() 
{
    // Изменение и вывод возраста пользователя
    userAge = 30;
    std::cout << "Updated User's Age: " << userAge << std::endl;
}

Из этого примера мы видим, как важно правильно именовать функции и переменные.

А теперь поговорим о качестве кода

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

Так в чем же суть, на чем я остановился? Правильно, на качестве. Программа подобна автомобилю, в который мы можем установить качественную или не качественную деталь. Будет ли автомобиль двигаться с не качественной деталью? Да, конечно. На долго ли? Возможно, и на долго, но, как правило, нет. Так и с программой. Для меня главным правилом является "1 функция - 1 действие". Как это понимать, рассмотрим на примере:

    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
// Функция, которая делает две вещи: проверяет, является ли число простым, и выводит его на экран
   void check_and_print(int n) {
     bool prime = true; // Флаг, указывающий, является ли число простым
     for (int i = 2; i <= sqrt(n); i++) {
       if (n % i == 0) {
         prime = false; // Число не простое, если делится на i
         break; // Выходим из цикла
       }
     }
     if (prime) {
       cout << n << " "; // Выводим простое число
     }
   }
   
   // Главная функция программы
   int main() {
     int limit;
     cout << "Введите предел для поиска простых чисел: ";
     cin >> limit;
     for (int n = 2; n <= limit; n++) {
       check_and_print(n); // Вызываем функцию, которая делает две вещи
     }
     cout << endl;
     return 0;
   }
   
   /////////////////////////////////////
   
   // Функция, которая проверяет, является ли число простым
   bool is_prime(int n) {
     for (int i = 2; i <= sqrt(n); i++) {
       if (n % i == 0) {
         return false; // Число не простое, если делится на i
       }
     }
     return true; // Число простое, если не нашлось делителей
   }
   
   // Функция, которая выводит все простые числа до заданного предела
   void print_primes(int limit) {
     for (int n = 2; n <= limit; n++) {
       if (is_prime(n)) {
         cout << n << " "; // Выводим простое число
       }
     }
     cout << endl;
   }
   
   // Главная функция программы
   int main() {
     int limit;
     cout << "Введите предел для поиска простых чисел: ";
     cin >> limit;
     print_primes(limit); // Вызываем функцию для вывода простых чисел
     return 0;
   }
   

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

Рекомендации от автора

Подчеркнем главные мысли. 1. Я не хочу сказать, что олимпиады ненужны, я лишь хочу сказать - что они убивают новичков, с точки зрения проффесиональности. 2. Не бойтесь использовать структуры данных.

На этой замечательной ноте, я хочу с вами попращаться!


27.12.2023 21:24 | Max Makaluk