Произведите впечатление на интервьюеров и улучшите свой код с помощью этих пяти решений.

Задача FizzBuzz - это классический тест, который дают на собеседовании по программированию. Задача простая:

Выведите целые числа от 1 до N, но выведите «Fizz», если целое число делится на 3, «Buzz», если целое число делится на 5, и «FizzBuzz», если целое число делится как на 3, так и на 5.

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

Ниже я объяснил пять подходов к проблеме FizzBuzz с использованием R. Понимание каждого из них поможет закрепить правильные методы кодирования и использование уникальных функций R. Это может даже помочь вам получить работу.

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

1. Наивное решение

Самый очевидный способ решения FizzBuzz - перебрать набор целых чисел. В этом цикле мы используем условные операторы, чтобы проверить, делится ли каждое целое число на 3 и / или 5.

В приведенном выше коде используется этот подход. Сначала мы сохраняем целые числа 1–50 в векторе fbnums. Также определяется пустой вектор с именем output для хранения результатов процедуры. Затем мы проверяем, делится ли каждое целое число в fbnums без остатка на 3 и / или 5, используя условные операторы внутри цикла for. В этих операторах используется оператор по модулю %%, который возвращает остаток от операции деления. Например, выражение if (i %% 3 == 0) означает «если мы разделим это целое число на 3, останется ли нулевой остаток?».

Когда одно из условных операторов возвращает истину, соответствующий ответ сохраняется в i -м индексе вектора output. После завершения цикла мы печатаем output, чтобы отобразить результаты процесса.

Хотя этот код работает, у него есть несколько ограничений:

  • Он использует множество запутанных условных операторов, которые сложно поддерживать.
  • Операторы i %% 3 == 0 и i %% 5 == 0 повторяются дважды, что делает код неэффективным и многословным.
  • Цикл for относительно медленный и неэффективный в R, особенно в случаях, когда много итераций.

К счастью, есть несколько способов улучшить эту отправную точку.

2. Лучшее решение с использованием цикла For

Можно уменьшить количество условных операторов в нашем цикле for, как показано ниже. Это было вдохновлено примером Тома Скотта из его видео о задаче FizzBuzz, которое я очень рекомендую посмотреть.

Здесь fbnums и output определены так же, как и раньше. Но в цикле for все работает немного иначе. Сначала мы определяем i -й индекс в нашем output векторе как пустую строку. Затем мы проверяем, делится ли наше целое число на 3, как в последнем примере. Если это возвращает истину, мы вставляем «Fizz» в конец пустой строки в output[i]. Эквивалентный оператор для 5 и «Жужжание» оценивается в следующей строке. Наконец, мы проверяем, пуст ли output[i]. Если это так, мы сохраняем целое число в этом элементе, чтобы не возвращать пустую строку. По окончании цикла печать output дает тот же результат, что и в предыдущем примере.

Эта реализация лучше предыдущей по нескольким причинам:

  • Вставка «Fizz» и «Buzz» в пустую строку таким образом устраняет необходимость в выражении, оценивающем «FizzBuzz». Это избавляет от повторов и выглядит аккуратнее.
  • Формат упрощает добавление дополнительных условий, сохраняя при этом читабельность кода.

Это хороший прогресс, но использование цикла for по-прежнему не позволяет использовать некоторые уникальные возможности R. Следующие три примера используют их в полной мере и отлично подходят для изучения некоторых передовых методов R.

3. Пакетное решение с использованием FizzBuzzR

Одна из сильных сторон R - разнообразие удобных для пользователя пакетов. FizzBuzzR является одним из них и содержит единственную функцию, предназначенную для решения проблемы FizzBuzz. Вызов fizzbuzz позволяет пользователю указать диапазон целых чисел для оценки, интервал, через который следует проходить через эти целые числа, и делители для Fizz и Buzz. Затем результат выводится на консоль построчно.

Эта реализация имеет очевидное преимущество - она ​​невероятно лаконична. Но есть компромисс: ограниченные возможности настройки. Допустим, интервьюер просит вас заменить «Fizz» на «Biff» или сохранить результат прямо в списке. В таких случаях вам нужно будет подумать о совершенно новом подходе к этим вещам.

4. Функциональное решение с использованием map ()

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

В приведенном выше коде мы создаем функцию с именем fbmap, которая вычисляет заданное целое число в соответствии с нашими правилами FizzBuzz. Наша функция делает это, используя те же условные операторы, что и в примере 2. Однако на этот раз мы заменяем определенные значения аргументами, которые мы можем передать функции. Наши делители 3 и 5 заменяются аргументами mod1 и mod2, а наши операторы печати «Fizz» и «Buzz» заменяются на exp1 и exp2. Это означает, что когда мы вызываем функцию, мы можем определять эти значения как угодно без необходимости переписывать много кода.

Чтобы получить результат, мы применяем нашу функцию к каждому элементу вектора fbnums, используя map_chr. Для этого мы используем выражение ~ fbmap(.x, 3, 5, "Fizz", "Buzz") внутри map_chr. Это указывает fbmap на оценку каждого элемента вектора fbnums (обозначенного .x) с делителями 3 и 5 и ответами «Fizz» и «Buzz». Результаты этих последовательных вызовов функций сохраняются как отдельные элементы в output, векторе символов. Затем при печати output в консоли отображаются правильные результаты.

Эта реализация хороша по нескольким причинам:

  • Он ясен, краток и избегает повторений.
  • Указание аргументов для fbmap обеспечивает гибкость. Можно также добавить новые условия, не слишком усложняя кодовую базу.
  • Он использует map_chr для применения функции к вектору, что более эффективно, чем использование цикла for в R.

На мой взгляд, это довольно сильное решение FizzBuzz. Он выглядит хорошо, максимально использует R и не длиннее, чем наивная реализация в моем первом примере. Но что, если вы хотите, чтобы ваше решение FizzBuzz было оптимально совместимо с рабочим процессом tidyverse?

5. Векторизованное решение с использованием case_when ()

Для большинства задач в R есть предопределенная функция tidyverse, которая поможет вам. Хотите применить операторы if к элементам вектора? Используйте case_when из пакета dplyr.

Приведенный выше код модифицирован из примера в документации case_when, написанной Хэдли Уикхэмом. Проще говоря, case_when применяет операторы if к вектору и возвращает вывод, основанный на результате этих операторов. Этот пример также гарантирует возвращение рассматриваемого целого числа, если оно не делится без остатка, с выражением TRUE ~ as.character(fbnums). За исключением некоторого другого синтаксиса, это именно то, что мы делали в предыдущих примерах. Разница в том, что case_when полностью использует возможности векторизации R, что делает его более эффективным, чем циклы for в решениях 1 и 2.

Кроме того, мы по-прежнему не повторяем никаких условных утверждений. Хотя мы оцениваем «FizzBuzz» как отдельное условие, мы можем использовать простую математику, чтобы сделать вывод, что любой экземпляр «FizzBuzz» делится без остатка на 15, наименьший общий множитель 3 и 5. Это позволяет избежать повторения операторов, содержащих %% 3 == 0 и %% 5 == 0. Кроме того, код по-прежнему легко читать и при необходимости добавлять. Это может быть достигнуто путем добавления дополнительных условий в case_when или подключения к другой функции после case_when для выполнения более сложных требований. В общем, case_when - отличная функция для решения проблемы FizzBuzz в R.

Заключение

Решение проблемы FizzBuzz демонстрирует ценность обучения написанию аккуратного кода. Хотя все вышеперечисленные решения работают, некоторые из них гораздо проще поддерживать в большой реальной кодовой базе. Почему эти имплантации лучше? Они придерживаются хороших принципов программирования и максимально используют возможности R.

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