Как перестать писать плохой код на Bash: практические советы

Хотите, чтобы ваш код был ещё эффективнее? С помощью примеров показываем, почему писать нормальный код на Bash проще, чем кажется.

Как перестать писать плохой код на Bash: практические советы

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

Основная цель − упрощение коммуникации

Поскольку потребители конечного продукта почти никогда не понимают, из каких соображений реализована та или иная функция, главная задача программиста состоит в том, чтобы сделать продукт максимально понятным и полезным, с учетом особенностей user experience и предыдущих версий проекта. Даже если заказчик настаивает на изменении структуры проекта, нужно попытаться найти компромисс, чтобы не было ущерба удобству пользователей.

И здесь совершенно неважно, пишете ли вы код на Bash или используете другие технологии.

Практика

Давайте рассмотрим процесс оптимизации рабочего процесса на примере задачи о зёрнах на шахматной доске.

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

Допустим, мы нашли следующее решение:

bc <<< 'ibase=16;FFFFFFFFFFFFFFFF'

Структура работает, потому что вся задача вращается вокруг степеней двойки, двоичной и, как следствие, шестнадцатеричной систем.

Но о чем же говорит этот код? Так ли важна шестнадцатеричная система? Конечно, нет. Именно поэтому стоит попытаться достичь желаемого более оптимальным и понятным способом. Рассмотрим несколько вариантов.

Бинарный способ

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

  • На первом квадрате лежит одно зерно, в двоичном виде записанное как 0b1.
  • На втором − два, в переводе − 0b10. Их сумма − три, или же 0b11.
  • На третьем лежит четыре зерна: запишем как 0b100. В итоге 7 или 0b111.
  • Четвертый: 8, 0b1000. Общее − 15, 0b1111.

Заметили закономерность? Значение каждого квадрата представляет собой простое бинарное значение. Но при его сложении с предыдущими, мы каждый раз получаем кучу единиц.

Заменим в нашем первом решении F на 64 единицы, по одной на каждый квадрат доски.

bc <<< "ibase=2;1111111111111111111111111111111111111111111111111111111111111111"

Данное решение уже намного лучше отражает закономерности. Но бесконечная строка из цифр, естественно, не является грамотным решением.

Брутфорс

Как перестать писать плохой код на Bash: практические советы

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

total=0
current_grains=1
for square in {1..64}; do
  total=$( bc <<< "$total + $current_grains" )
  current_grains=$( bc <<< "$current_grains * 2" )
done
echo "$total"

В этом случае отлично видно, что значение количества клеток на шахматной доске наряду с постоянным удваиванием значения являются главными переменными задачи. Хорошо, но очень медленно. Цикл, расчет и повтор команды? Разве так работают профессионалы?

Прямые вычисления

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

---------------------
| 1 | 2 | 4 | 8 |16 |
---------------------

Их сумма будет 1+2+4+8+16=31. Еще ничего не понятно, да? Двигаемся дальше. Как насчет доски с шестью квадратами? Сразу проведем необходимые расчеты.

-------------------------
| 1 | 2 | 4 | 8 |16 |32 |
|   | 3 | 7 |15 |31 |63 |
-------------------------

И их общее значение будет таким: 1+2+4+8+16+32=63. Начинаете догадываться? Двигаемся дальше.

-----------------------------
| 1 | 2 | 4 | 8 |16 |32 |64 |
|   | 3 | 7 |15 |31 |63 |127|
-----------------------------

1+2+4+8+16+32+64=127. Что вам говорят значения 31, 63, 127? Все это − двойки в степени n, из которых вычли единицу.

Вот еще один пример. Представьте доску с двенадцатью квадратами. Чтобы найти конечное значение, мы просто умножаем единицу на два одиннадцать раз, что также можно записать как 2^11. Получилось 2048. Удвоим заново и получим 4096, 2^12. Не забываем вычесть единицу. Получаем 4095. Проверяем простейшим расчетом:

1+2+4+8+16+32+64+128+256+512+1024+2048=4095 − все верно.

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

Число зерен в последней клетке равно 2^63 (нулевой индекс, помните?), а общее значение − 2^64-1.

bc <<< "2^64 - 1"

А что же в двоичном коде?

0b1111...  # 64 ones

Количество зерен в несуществующем 65 квадрате?

0b10000... # 1 and 64 zeros

Как получить из единицы и 64 нулей 64 единицы? Вычтите 1.

Если вам не кажется, что это работает хорошо − улучшайте

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

Понравилась статья о том, как писать более простой код на Bash? Другие материалы по теме:

Источник: Хватит писать сложный код на Bash на Exercism

МЕРОПРИЯТИЯ

Комментарии

ВАКАНСИИ

Добавить вакансию
Go-разработчик
по итогам собеседования
Java Team Lead
Москва, по итогам собеседования

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ