Ruby 分享我对 ThoughtWorks FizzBuzzWhizz Puzzle 的 Ruby 解法和另外一个 Ruby 脚本

匿名 · 2014年05月23日 · 最后由 yanguango 回复于 2014年06月10日 · 3120 次阅读

哈哈,参加了这次活动,然后无比幸运地得到了去 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

作为刚刚注册不久的新人,还希望各位老人多多照顾… 先谢过了~~ ^_^

太丑了,看不懂,懒得看

能把 ruby 代码写成这样,人才阿。。。

比我大学时候写的好一百倍

匿名 #4 2014年05月23日

#3 楼 @hooopo #2 楼 @pynix

谢谢鼓励~ ^_^

比我大学时候写的好五百倍

匿名 #6 2014年05月23日

#5 楼 @lgn21st

T^T 难以置信

谢谢您的回复,心中的激动久久不能平复~

比我現在寫的好好幾倍......

匿名 #8 2014年05月23日

#7 楼 @Juanito

谢谢您的鼓励…

我也需要多多像您学习呢!^_^

欢迎来骚窝,加油!

。。。第一行就错了吧。。。1..10就不是个位数了。。。

而且。。。这样能写单元测试么。。。

#5 楼 @lgn21st 难道不是因为你大学不写代码?😏

#11 楼 @Tony612 我大学时代写了很多 asp 代码,不是 ASP.net,是最老旧的 aps 那种。

#12 楼 @lgn21st 原来如此。。但 ruby 和这个可比性不强吧,虽然代码风格还是可以有所体现的 😄

14 楼 已删除
15 楼 已删除
匿名 #16 2014年05月24日

#10 楼 @Kabie 是的,后来修改的时候粗心了… 现在又修正了,谢谢您的指正~

其实不算是单元测试啦… 用 exec测试的… T^T

匿名 #17 2014年05月24日

#9 楼 @Seabornlee

谢谢,同加油~!^_^

简洁不等于代码行少。

不客气的说,这代码既不简洁、更谈不上可读性。

写程序的目的不是炫耀技巧,而是沟通。和机器沟通,更重要的是和会读你程序的人沟通。

分成几个小方法,是不是更好读一些?

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
匿名 #21 2014年05月24日

#18 楼 @birdfrank

谢谢您的批评 无论如何,代码总是不完美的

我明白,自己所了解的只是冰山的一角 所以我会继续探寻 Ruby 的奥秘

再次谢谢您

匿名 #22 2014年05月24日

#19 楼 @skandhas

是的,当时太过于坚持代码的行数一定要少这个信念,所以写得太紧凑了

这样的可读性高了很多呢!

#20 楼 @luikore 好思路,尤其 [i, s][s.size <=> 0] 这句用的漂亮!

#22 楼 @izzyleung 你太过谦了。我学 Ruby 时,比你差太多了。

匿名 #24 2014年05月24日

#20 楼 @luikore

这样更简洁一些,不过读起来的确稍稍难一点 但是几个 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 去写 如果能完整的进行链式调用 拆开反而降低了可读性

匿名 #27 2014年05月24日

#26 楼 @zj0713001

谢谢您的回复

我知道 @birdfrank 先生经验比我更为丰富。所以即使先生有些言重,我也不会觉得失去对编程的热情。 我知道,自己还有很多的路要走。

谢谢您对我的鼓励,Izzy 感激不尽

既然是 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)

题目细节我有点记不清,以上测试代码可能不符合原题所说的规则,只是说明个意思吧。

tw 是要这样的吗?不是说最好 TDD 吗?我还天真的写成了一个 gem 一样的东西。。。

能先注明一下原意吗,上来就一段代码,不知道解决什么问题的

谢谢 31 楼给的链接。注意这句话:

“鼓励使用单元测试,展示你出色的面向对象能力。”

歪个楼,楼主是喜欢 Izzy 么?GN'R?瞎猜一下。

匿名 #34 2014年05月26日

#33 楼 @bigpig85

Bingo! ^_^

匿名 #35 2014年05月26日

#29 楼 @realwol

也许是我的代码比较符合审核人的口味吧~ 或许只是我的人品稍微好了一点点而已…

不过,一定还有更棒的公司等着您的呢!

#35 楼 @izzyleung 我只是体验一下,不过我真的没看出来这是要比谁短。。。

匿名 #38 2014年06月10日

#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

我写了整整 30 行啊,原来是比短啊

需要 登录 后方可回复, 如果你还没有账号请 注册新账号