今天看到TW发了道FizzBuzzWhizz的面试题。这道题其实就是逢3过的高级版,手痒痒来解一下。。
- 你首先说出三个不同的特殊数,要求必须是个位数,比如3、5、7。
- 让所有学生拍成一队,然后按顺序报数。
- 学生报数时,如果所报数字是第一个特殊数(3)的倍数,那么不能说该数字,而要说Fizz;如果所报数字是第二个特殊数(5)的倍数,那么要说Buzz;如果所报数字是第三个特殊数(7)的倍数,那么要说Whizz。
- 学生报数时,如果所报数字同时是两个特殊数的倍数情况下,也要特殊处理,比如第一个特殊数和第二个特殊数的倍数,那么不能说该数字,而是要说FizzBuzz, 以此类推。如果同时是三个特殊数的倍数,那么要说FizzBuzzWhizz。
- 学生报数时,如果所报数字包含了第一个特殊数,那么也不能说该数字,而是要说相应的单词,比如本例中第一个特殊数是3,那么要报13的同学应该说Fizz。如果数字中包含了第一个特殊数,那么忽略规则3和规则4,比如要报35的同学只报Fizz,不报BuzzWhizz。
说实话,作为程序员,如果解不出这道题,真的该考虑改行了。号称最难面试的TW考察的肯定不是仅仅能正确跑出结果。我觉得这道题的得分项在这里:
强烈建议写单元测试,这个没什么说的;请展示出你超赞的面向对象/函数式编程功底,coding是一种武功,不同的领悟打出不同的招式和威力;建议尽量减少圈复杂度,我想最让人崩溃的就是看一坨充满了for/if/switch的代码;
好了,来看题。。 这道题其实就是把输入(一个数字)经过一系列的规则转换成Fizz/Buzz/Whizz,来看看这些rule:
- 如果这个数包含了第一个特殊数,输出Fizz;
- 如果这个数是某个特殊数的倍数,输出相应单词;
- 直接输出原数;
估计很多人到这就直接开始写分支判断了。其实这三个规则存在这样的优先级:rule1 > rule2 > rule3,而在javascript的||操作符正好适用,这样也就避免了代码里充斥分支语句。基于这样一个规则表,我们就可以写出代码了。高亮版
var _ = require('underscore');
var special_words = ["Fizz", "Buzz", "Whizz"];
var candidates = seq(9); // 1..9
// select 3 random numbers from candidates.
var special = _(3).times(function(i) {
var selected = candidates[_.random(candidates.length-1)];
candidates = _.without(candidates, selected);
return selected;
});
console.log(special);
var input = seq(100); // 1..100
// main
var output = _.map(input, function yamete(n) {
return rule1(n) || rule2(n) || rule3(n);
});
console.log(_.zip(input, output));
function rule1(n) {
return new RegExp(special[0] + '').test(n + '') ? special_words[0]:false;
}
function rule2(n) {
return _.reduce(special, function(memo, s){
this.i++;
return memo + ((n%s) ? '' : special_words[this.i]);
}, '', {i:-1});
}
function rule3(n) { return n; }
function seq(n) { return _(n).times(function(n){return n+1}); }