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

По общему признанию, написание функций в стиле гольф-кода не имеет большого практического смысла (вам определенно не следует использовать их в своем производственном коде!). Однако есть очевидные преимущества. Во-первых, они заставляют вас глубоко понимать все тонкости того языка, который вы используете. Кроме того, принципы сжатия кода при ограниченном использовании могут принести пользу вашему повседневному стилю кодирования. В результате практики ката на Codewars я обнаружил, что мой собственный код становится все более чистым и СУХИМ, сохраняя при этом удобочитаемость.

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

Основные правила Fizzbuzz выглядят примерно так:

  • Вернуть массив, содержащий числа от 1 до n.
  • Если число кратно 3, верните «Fizz» вместо числа.
  • Если число кратно 5, верните «Buzz» вместо числа.
  • Если значение кратно 3 и 5, верните «Fizzbuzz» вместо числа.

Начнем с простого решения. Если бы вы попросили меня написать Fizzbuzz, когда я только учился программировать, я бы, вероятно, придумал что-то вроде этого:

Шаг 0:

18 строк и 313 символов

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

Шаг 1:

18 строк и 316 символов

Прежде всего: я преобразовал объявление функции в стрелочную функцию. На этом шаге фактически добавлено несколько символов (ух!), Но, как мы увидим, в конечном итоге он сократит функцию немного позже в процессе, потому что я смогу использовать неявные возвраты.

Шаг 2:

11 строк и 269 символов

Несколько простых изменений немного сократят код. Во-первых, я избавился от блоков в операторах if и else. Затем я использовал сокращенные операторы присваивания в строках 5 и 6. На практике я видел, как некоторые руководства по стилю утверждают, что использование однострочных операторов if и else сбивает с толку. Но если в блоках есть только один оператор, я почти всегда их опускаю. В этом случае я, вероятно, использовал бы блоки для строк 7 и 8, чтобы сделать логику более очевидной.

Шаг 3:

11 строк и 258 символов

Этот шаг представляет собой первое существенное изменение логики моей функции. Все вышеперечисленное работало более или менее так же, как и исходное решение. Просматривая код, я увидел, что полагаюсь на проверки равенства для объединения «Fizz» или «Buzz», а также для нажатия i, если длина строки равна 0. Вместо этого я немного изменю логику и воспользуюсь правдивостью. для моих if заявлений. Поскольку я ищу случаи, когда выражения if оцениваются как 0, я могу просто использовать оператор !, чтобы отрицать их. Итак, теперь, когда операторы оценивают значение 0, они возвращают true и возвращают false во всех остальных случаях.

Шаг 4:

9 строк и 182 символа

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

Напомним, что Array(n) инициализирует массив длиной n. Однако инициализированный массив на самом деле не содержит никаких значений, поэтому я использовал .fill для вставки значений в пустые слоты. .fill без аргумента вставляет undefinedв каждый слот.

Наконец, поскольку я сейчас использую .map, он ожидает возвращаемое значение на каждой итерации. Раньше, когда я просто помещал значения в массив, не было смысла использовать троичную систему. Теперь это так, поэтому я использую один в строке 7.

Шаг 5:

7 строк и 164 символа

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

Шаг 6:

4 строки и 124 символа

Теперь мы действительно вникаем в сорняки. Вместо того, чтобы полагаться на конкатенацию строк, я буду использовать литерал шаблона. Обратите внимание, что я все еще полагаюсь на правду в своей логике, но я избавился от оператора !.

Давайте разберемся, как это работает. Если значение кратно 3, i % 3 оценивается как 0. Соответственно, первый тернар вызывает аргумент после двоеточия (ложный случай). Таким образом, выражение внутри заполнителя (т.е. ${}) оценивается как «Fizz». Та же логика применяется ко второму заполнителю, который проверяет наличие 5. Если значение кратно как 3, так и 5, оно определяется как фактическое`${"Fizz"}${"Buzz"}`, что, конечно же, оценивается как «FizzBuzz». Если значение не кратно 3 или 5, литерал шаблона оценивается как пустая строка "", которая является ложным значением. В этом случае оператор || вызывает возвращение числа, а не «Fizz», «Buzz» или «FizzBuzz».

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

Шаг 7:

1 строка и 105 символов

На этом этапе много чего происходит, поэтому я постараюсь объяснить это довольно подробно.

Первую часть я обнаружил случайно. Я пытался уменьшить Array(n).fill() с помощью оператора ... (распространение) и на мгновение заменил его на [...Array(n).fill()]. Интуитивно я исключил .fill(), оставив [...Array(n)]. Как оказалось, использование оператора ... (распространение) в построенном массиве приводит все значения к undefined. Милая!

Затем я хотел уместить все в одну строку, воспользовавшись преимуществом другого неявного возврата. Но есть проблема, мне нужно сначала увеличить индекс. На самом деле это довольно просто исправить! Мне просто нужно увеличить индекс внутри нашего первого литерала-заполнителя шаблона. Однако мне нужно учесть это в логике заполнителя. Если я использую постфиксное приращение, то есть i++, число будет увеличиваться после теста, чтобы увидеть, кратно ли оно трем. Итак, я могу просто использовать приращение префикса, то есть ++i, которое будет увеличивать число перед тестом.

Что касается решений, я бы сказал, что это максимум, на что я могу пойти, сохраняя «законный» синтаксис. Но в образовательных целях давайте просто посмотрим, насколько коротко я могу это сделать.

Шаг 8:

1 строка и 77 символов

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

Последний шаг - воспользоваться небольшой ошибкой на стороне Codewar. Хотя тесты ката Fizzbuzz требуют, чтобы функция называлась «fizzbuzz», то есть я не могу просто назвать ее «f», я могу полагаться на тот факт, что ката позволяет мне просто переназначить функцию, а не использовать ключевое слово присваивания. Если бы мне пришлось использовать ключевое слово присваивания, я бы, конечно, использовал let вместо const, потому что это на один символ короче. 😉

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

Кроме того, если вы можете придумать что-нибудь, чтобы сделать эту функцию еще короче, обязательно дайте мне знать!

Спасибо за прочтение!