Чтобы понять, как потенциальный программист умеет писать код, на собеседованиях любят давать небольшие задачки на программирование. Сегодня разберём одну из них и посмотрим, какие там могут быть подводные камни и на что стоит обратить внимание.
Задача — написать программу для игры 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, когда текст добавляется два раза, по каждому условию.
 - В конце мы проверяем, есть ли что-то в этой переменной. Если нет, то, значит, не сработало ни одно правило и нужно вывести само число.
 
Что дальше
Это не единственная задача по программированию общего назначения. В следующий раз разберём пример посложнее и расскажем, на что обратить внимание при решении.
						




