Поговорим о рефакторинге и оптимизации кода

newbie_

Опубликован:  2019-07-02T05:21:59.215664Z

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

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

#include <iostream>
#include <string>
#include <cmath>

unsigned long to_integer(const std::string & s);
int find_i(const char s);

int main()
{
  std::string first = "54709879023";
  std::string second = "23450980912";
  std::cout << first << std::endl << " + " << std::endl << second
            << " = " << std::endl
            << to_integer(first) + to_integer(second) << std::endl;
  return 0;
}

int find_i(const char s)
{
  const std::string digits = "0123456789";
  int r;
  for (int i = 0; i < 10; i++)
  {
      if (s == digits[i])
      {
          r = i;
          break;
      }
  }
  return r;
}

unsigned long to_integer(const std::string & s)
{
  const int L = s.length();
  unsigned long sum = 0;
  for (int i = 0; i < L; i++)
    sum += find_i(s[i]) * std::pow(10, L - i -1);
  return sum;
}

В этом коде, хоть он и является рабочим и решает поставленную задачу, есть излишества. А именно, совершенно лишней и бессмысленной является функция find_i, потому что она:

  • слишком громоздкая для начальной задачи и занимает 8 строк кода;
  • создаёт при исполнении программы избыточные, совершенно ненужные сущности, которые ещё и занимают память компьютера.

Сама по себе функция преобразует символ (тип char) в целое число (тип int), и в данном случае такое преобразование можно сделать кратно проще. Дело в том, что каждый символ ASCII хранится в памяти как целое восьмибитное число, и в сущности может быть приведён к этому значению. Рассмотрим, как могут быть представлены символы от 0 до 9, для этого я создам временный файл и напишу в него следующий код.

#include <iostream>
#include <string>

int main()
{
  using namespace std;
  string digits = "0123456789";
  for (int i = 0; i < 10; i++)
  {
    cout << "char: " << digits[i]
         << " is int: " << int(digits[i]) << endl;
  }
  return 0;
}

Если откомпилировать файл tempo.cpp и исполнить его, то можно увидеть следующую картину.

e5943nMwT5.png

Таким образом, если из численного значения любого символа от 0 до 9 вычесть целое число 48, то получится целое число, соответствующее этому символу. Это простое свойство приведения типов можно вполне использовать для решения обозначенной задачи. Возвращаюсь к коду файла toint.cpp, нахожу в нём функцию to_integer и в этой функции цикл for. Тело этого цикла в настоящий момент выглядит так:

  sum += find_i(s[i]) * std::pow(10, L - i -1);

Заменяю в этом выражении вызов функции find_i на вычисление целого значения с приведением типов.

  sum += (s[i] - 48) * std::pow(10, L - i - 1);

Функцию find_i теперь можно вообще удалить из кода программы.

vQa7Hbs00N.png

После компиляции файла и исполнения программы вывод не изменился.

6Bylby1jOd.png

Оптимизация кода вполне удалась, программа получилась короче, а при её исполнении более оптимально будут расходоваться ресурсы процессора и занимаемая память.

Здесь мне хочется сделать краткое лирическое отступление... Когда мы разрабатываем код программы, отлаживаем его, тестируем, смотрим все приходящие на ум возможные варианты, очень часто наш глаз замыливается, и мы перестаём замечать некоторые очевидные вещи. Очень полезно показывать свой код другим людям, обсуждать его с коллегами, иногда всплывают вот такие интересные варианты оптимизации. Например, предыдущий выпуск блога с решением этой задачи я опубликовал на Дзене, и тут же получил очень дельный комментарий с предложенной оптимизацией кода от пользователя crem651, за что выражаю этому человеку искреннюю благодарность и признательность. Я сам вряд ли увидел бы такое простое и очевидное решение.

Комментарии: