哈哈,参加了这次活动,然后无比幸运地得到了去 ThoughtWorks 实习的机会(楼主还是大学僧… T^T) 分享一下这次写的代码(当然有改动咯~) 一共 7 行,自以为兼顾了可读性与简洁。大家轻拍呢~(单元测试的代码就省去了… >_<)
raise ArgumentError unless ARGV.map(&:to_i).all?{|x| (1..9) === x} && ARGV.length == 3
(1..100).each do |num|
next puts 'Fizz' if num.to_s.split('').include?(ARGV[0])
*marks, result = 'Fizz', 'Buzz', 'Whizz', ''
(0..2).each{|i| result += marks[i] if num % ARGV[i].to_i == 0}
puts result == '' ? num : result
end
另外分享的一个脚本是用来刷豆瓣 FM 『累计收听』数目的 Ruby 脚本 每十秒钟刷一首您红心歌曲的收听,所以对于您的私人频道的效果是不会有影响的呢!
您可以到 GitHub Gist 上观看与下载(其实上面的代码也在 Gist 上面啦) 代码的地址在这里:https://gist.github.com/izzyleung/3ad880e980ec92c114b3
若需使用代码,您需要安装 rest-client 这个 gem
作为刚刚注册不久的新人,还希望各位老人多多照顾… 先谢过了~~ ^_^
简洁不等于代码行少。
不客气的说,这代码既不简洁、更谈不上可读性。
写程序的目的不是炫耀技巧,而是沟通。和机器沟通,更重要的是和会读你程序的人沟通。
分成几个小方法,是不是更好读一些?
raise ArgumentError unless ARGV.length == 3
hsh = Hash[ARGV.map(&:to_i).zip [:Fizz, :Buzz, :Whizz]]
def rule01(i, h)
:Fizz if i.to_s.include?(h.keys.first.to_s)
end
def rule02(i, h)
ary = h.keys.select {|k| i % k == 0 }
ary.empty? ? i : ary.map{|k| h[k] }.join
end
def filter(i, h)
rule01(i, h) || rule02(i, h)
end
# print
(1..100).each{ |i| puts filter(i, hsh) }
用表计算,是不是更难读一些?
raise "require 3 digits" if ARGV.size != 3 or ARGV.any?{|a| !(('1'..'9').include? a) }
1.upto 100 do |i|
next puts 'Fizz' if i.to_s.index ARGV.first
s = %w[Fizz Buzz Whizz].zip(ARGV).map{|w, n| [w][i % n.to_i] }.join
puts [i, s][s.size <=> 0]
end
#20 楼 @luikore 好思路,尤其 [i, s][s.size <=> 0]
这句用的漂亮!
#22 楼 @izzyleung 你太过谦了。我学 Ruby 时,比你差太多了。
这样更简洁一些,不过读起来的确稍稍难一点 但是几个 Hack 用得让人击节叫好!
网上有一个 5 行用 Scala 写的
object FizzBuzzWhizz {
def main(args:Array[String]) = (1 to 100).map(rule5(args(0),"Fizz")).map(rule3(args(0),"Fizz")).map(rule3(args(1),"Buzz")).map(rule3(args(2),"Whizz")).foreach(x=>println(if (x._2=="") x._1 else x._2))
def rule5(specialnumber:String,specialstring:String) = (input:Int) => if (input.toString.contains(specialnumber)) (0,specialstring) else (input,"")
def rule3(specialnumber:String,specialstring:String) = (input:(Int,String)) => if ((input._1 > 0)&(input._1 % specialnumber.toInt == 0)) (input._1/specialnumber.toInt,input._2+specialstring) else input
}
代码更少,但是看起来太累了… = =
您可以看看这个 Gist: https://gist.github.com/cnxiaoma/1e00690c13f3c45393f9
#24 楼 @izzyleung 比行数少就更难看了...
1.upto(100){|i|puts i.to_s.index(ARGV.first) ? 'Fizz' : ->s{[i, s][s.size <=> 0]}[%w[Fizz Buzz Whizz].zip(ARGV).map{|w, n|[w][i % n.to_i]}.join]}
#18 楼 @birdfrank 其实我感觉楼主的代码还可以 特别是他仅仅是在校生 也许每个人对于可读性的定义会不一样 我认为 ruby 更人性化的地方就在于他可以一句话完成很多事儿 这就像人类的思绪 一条线的思路不分叉的话肯定是最清晰的 代码是不是断行的依据就是一串链式调用是不是中断了 中断的话肯定是要另启一行或者集合到一个 block 去写 如果能完整的进行链式调用 拆开反而降低了可读性
既然是 Thought Works 出的题目,我大胆猜测一下,写的程序不能进行自动化测试,并且测试不能体现 TDD 思想的话,恐怕是不行的。所以这个题目必须有一个方法,输入“位置序数”,输出报数的结果,然后可以集中对这个方法进行测试。
测试程序大概是这个模样:
queue = FizzQueue.new(3, 5, 7)
assert_equal '1', queue.report_on(1)
assert_equal 'fizz', queue.report_on(3)
assert_equal 'FizzBuzz', queue.report_on(15)
题目细节我有点记不清,以上测试代码可能不符合原题所说的规则,只是说明个意思吧。
#23 楼 @skandhas #25 楼 @luikore
这几天我又想了一个行数更少的版本,不知道这样如何
raise ArgumentError unless ARGV.map(&:to_i).all?{|x| (1..9) === x} && ARGV.length == 3
(1..100).each do |num|
next puts 'Fizz' if num.to_s.split('').include?(ARGV[0])
puts ''.tap { |str| ['Fizz', 'Buzz', 'Whizz'].each_with_index { |e, i| str << e if num % ARGV[i].to_i == 0 } }.tap { |str| str == '' ? str << num.to_s : str }
end
Ruby China 的代码把代码的行给折断了,不妨看看这个 GitHub Gist 吧: https://gist.github.com/izzyleung/ad9b052b455e285b3701