Ещё раз о рефакторинге и оптимизации кода

newbie_

Опубликован:  2019-07-12T07:30:26.312070Z

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

После публикации своего решения в Дзене я получил интересный комментарий следующего содержания:

Можно через один цикл все прогнать, одновременно определив и число разрядов и конвертируя каждую цифру в символ. Меньше циклов - быстрее работает программа. Если есть возможность заменить операцию деления - всегда это делайте, поскольку это тяжелая операция для процессора, которая не только медленная (от 20 тактов и выше), но еще и неконвейерная - то есть любая следующая операция деления не будет выполнена пока обрабатывается первая, как видно у нас цикл - значит это тот самый случай. Как известно деление это перевернутое умножение (в нашем случае деление на 10 - это умножение на 0.1) - значит операции с плавающей запятой обработаются быстрее (они могут выполняться каждый новый такт) и занимают меньше времени в логическом порту процессора.

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

nIoYex430P.png

Всё решение уместилось в менее чем 30 строчек кода. Программе потребовалась единственная директива препроцессора, вместо трёх в предыдущем решении. Из главной функции бесследно исчез процедурный вызов find_o, что естественно, но самые значительные изменения претерпела функция conv_n. На разборе её кода стоит сосредоточить основное внимание.

Функция conv_n получает в качестве параметра целое число, а возвращает стоку.

std::string conv_n(unsigned long n)
{

В теле этой функции создаётся переменная типа double.

  double full_number;

Переменная типа string.

  std::string s;

Переменная типа char.

  char c;

Затем функция воспроизводит цикл while до тех пор, пока переменная n не получает нулевое значение.

while (n)
{
  // первая итерация цикла n = 54709879023
  full_number = n * 0.1;
  // тогда full_number = 5470987902.3
  // отбрасываем от full_number дробную часть и присваиваем это
  // значение переменной n
  n = (unsigned long) full_number;
  // таким образом n получает значение 5470987902
  // вычитаем из значения full_number значение n
  // 5470987902.3 - 5470987902 = 0.3
  // полученное значение умножаем на 10
  // 0.3 * 10 = 3
  // и прибавляем к полученному значению 48
  // 3 + 48 = 51
  // сохраняем полученное значение в переменной типа char
  c = (full_number - n) * 10 + 48;
  // вставляем полученный символ c в начало строки s
  s.insert(s.begin(), c);
  // таким образом значение s на первой итерации цикла s = "3"
  // переходим к следующей итерации цикла
  // на второй итерации цикла в начало строки s будет вставлен
  // символ '2' и так далее...
}

Следует обратить внимание, что полученный в результате подсчёта символ на каждой итерации цикла вставляется в начало созданной строки. Цикл выполняется пока значение n не равно нулю, как только n получает значение 0, цикл завершается. После завершения цикла осталось вернуть полученную строку.

  return s;
}

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

Теперь посмотрим, как теория сочетается с практикой... Иду в терминал, компилирую полученный код и исполняю его.

QrezpMJGUd.png

Как говорят французы, voilà... Задача решена.

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

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