Чтобы понять, как потенциальный программист умеет писать код, на собеседованиях любят давать небольшие задачки на программирование. Сегодня разберём одну из них и посмотрим, какие там могут быть подводные камни и на что стоит обратить внимание.
Задача — написать программу для игры FizzBuzz
Есть детская игра FizzBuzz, где нужно называть числа подряд, соблюдая всего три правила:
- Если число делится на 3, вместо него надо сказать «Fizz».
- Если число делится на 5, вместо него надо сказать «Buzz».
- А если число делится одновременно на 3 и на 5, то надо вместо него сказать «FizzBuzz».
Например, в этой игре первые 20 чисел будут выглядеть так:
1
2
Fizz (3 делится на 3)
4
Buzz (5 делится на 5)
Fizz (6 делится на 3)
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz (15 делится одновременно на 3 и 5)
16
17
Fizz
19
Buzz
Нужно написать код на любом языке программирования, который выведет первые 100 чисел в этой игре.
В чём смысл задания
Цель человека, который проводит собеседование, — посмотреть, как вы решаете программистские задачи.
Одни кандидаты начинают сразу писать код, по пути исправляя ошибки, запуская всё заново, исправляя снова, дописывая то, чего не хватает, и так подход за подходом. Другие сначала составляют план решения, схемы и пробуют решить её алгоритмически — сразу нарисовать для себя полную картину, чтобы потом запрограммировать её за один подход. Третьи делают иначе — начинают с самого простого кода, который делает только базовые вещи, а потом начинают её усложнять, пока программа не будет делать то, что нужно.
С другой стороны, сразу будет видно, как именно вы пишете и оформляете код: ставите ли отступы и комментарии, делите ли код на функции и т. д.
А ещё задание достаточно простое, чтобы сделать его за несколько минут, и достаточно сложное, чтобы проверить всё, что нужно на начальном этапе. Дело в том, что для решения нужно предусмотреть ситуации, когда:
- число делится на 3 — и вывести одно слово;
- число делится на 5 — и вывести другое слово;
- число делится на 3 и 5 одновременно — и вывести третье слово;
- число не подходит ни под одно из этих условий — и надо тогда вывести само число.
Сложно прикинуть такое в уме и сразу написать полностью рабочий код с первого раза, поэтому эти задачи и дают на собеседованиях, чтобы проверить вас в деле.
Быстрый подход
Попробуем решить задачу в лоб: сделаем цикл от 1 до 100 и просто проверим все условия по очереди. Используем для этого % — оператор взятия остатка от деления. Если, например, число делится на 3 без остатка, то остаток равен нулю — по этому признаку мы сможем найти все числа, вместо которых надо сказать
«Fizz»:
// перебираем числа от 1 до 100
for (var i = 1; i <= 100; i++) {
// если число делится на три
if (i % 3 == 0) {
console.log('Fizz');
}
// если число делится на 5
if (i % 5 == 0) {
console.log('Buzz');
}
// если не делится ни на 3, ни на 5, то выводим само число
if ((i % 3 != 0) && (i % 5 != 0)) {
console.log(i);
}
}
Программа вроде работает, но вместо одного слова FizzBuzz на числе 15 она вывела два отдельных слова, а это неправильно. Значит, наше решение было слишком быстрым и надо подумать ещё.
Добавляем отдельную проверку для FizzBuzz
Чтобы исправить ситуацию, добавим в код ещё одну проверку, которая выведет одно слово FizzBuzz в нужный момент — когда число делится одновременно на 3 и 5. Но нам тогда придётся поправить и все остальные условия, когда мы просто делили на 3 и 5, чтобы программа не реагировала на них в этом случае:
// перебираем числа от 1 до 100
for (var i = 1; i <= 100; i++) {
// если число делится только на три
if ((i % 3 == 0) && (i % 5 != 0)) {
console.log('Fizz');
}
// если число делится только на 5
if ((i % 5 == 0) && (i % 3 != 0)) {
console.log('Buzz');
}
// если делится одновременно на 3 и 5
if ((i % 3 == 0) && (i % 5 == 0)) {
console.log('FizzBuzz');
}
// если не делится ни на 3, ни на 5, то выводим само число
if ((i % 3 != 0) && (i % 5 != 0)) {
console.log(i);
}
}
Делаем красивый код
Формально мы решили задачу — программа делает всё, что требовалось по условиям. Но наш код выглядит очень неопрятно: в нём сложно разбираться, он громоздкий и в нём слишком много условий и проверок. Если мы хотим на собеседовании показать себя с лучшей стороны, его нужно оптимизировать — убрать все повторы и сделать проще его поддержку в будущем.
Для начала уберём огромные скобки с множеством сравнений, используя конструкцию else-if:
// перебираем числа от 1 до 100
for (var i = 1; i <= 100; i++) {
// если делится одновременно на 3 и 5
if ((i % 3 == 0) && (i % 5 == 0)) {
console.log('FizzBuzz');
}
// иначе, если число делится только на три
else
if (i % 3 == 0) {
console.log('Fizz');
}
// если число делится только на 5
else
if (i % 5 == 0) {
console.log('Buzz');
}
// иначе, если всё предыдущее не подходит, то выводим само число
else
console.log(i);
}
Код стал выглядеть проще, но его всё ещё сложно поддерживать. Что, если нам в будущем понадобится поменять правила и вместо чисел 3 и 5 нужно будет проверять 4 и 7? В этом случае нам нужно будет пройти по всему коду и заменить одно число на другое, и так до тех пор, пока не будут сделаны все замены. Гораздо проще сделать так, чтобы нам было достаточно поменять значение только в одном месте и не выискивать в программе остальные значения. Обычно для этого используют переменные, но в нашем случае можно сделать изящнее:
// перебираем числа от 1 до 100
for (var i = 1; i <= 100; i++) {
// здесь будем хранить то, что надо вывести на экран
var output = '';
// если делится на 3 — добавляем Fizz
if (i % 3 == 0) { output += 'Fizz' }
// если делится на 5 — добавляем Buzz
if (i % 5 == 0) { output += 'Buzz' }
// если ничего не добавили — добавляем само число
if (output == '') { output = i}
// выводим результат на экран
console.log(output)
}
Это уже приемлемый код, который можно показывать как результат работы на собеседовании. Вот что мы для этого сделали:
- Все числа у нас используются только один раз — чтобы их поменять на другие, достаточно сделать это в одном месте, и всё будет работать правильно.
- Мы сделали переменную output — в неё мы добавляем текст, если сработало какое-то условие. Так мы получаем то Fizz, то Buzz, то FizzBuzz, когда текст добавляется два раза, по каждому условию.
- В конце мы проверяем, есть ли что-то в этой переменной. Если нет, то, значит, не сработало ни одно правило и нужно вывести само число.
Что дальше
Это не единственная задача по программированию общего назначения. В следующий раз разберём пример посложнее и расскажем, на что обратить внимание при решении.