(1..100).each do |n|
i_3=(n%3==0)
i_5=(n%5==0)
case
when i_3&&i_5
puts 'fizzbuzz'
when i_3
puts 'fizz'
when i_5
puts 'buzz'
else
puts n
end
end
它可以打印出我希望的数字和单词。
有没有办法使它更好地遵循Ruby最佳实践?
#1 楼
您已经内联了整个程序。将FizzBuzz分离为自己的接受
n
作为参数的方法将是有益的。 说到
n
,我不鼓励使用一个和两个字母变量名。 number
会更好。 我喜欢在输入select语句之前预先计算布尔值,但是这两个需要更好的名称。
divisibleBy3
和divisibleBy5
会更好。 我也喜欢您正确地解释了需求,并且没有将结果串联在一起以试图过早优化。有关更多信息,请参见此内容。
您的某些语句可能会占用一些呼吸空间。用
(n % 3)
代替(n%3)
等。在Ruby中,括号不是必需的,但是它们有助于弄清楚。
正如@rofl在聊天中指出的那样,您正在打印“ fizz”,“ buzz”和“ fizzbuzz”,而不是“ Fizz”,“ Buzz”和“ FizzBuzz”。
执行我的建议,您会得到类似的结果。
def fizzbuzz(number)
divisibleBy3 = (number % 3 == 0)
divisibleBy5 = (number % 5 == 0)
case
when divisibleBy3 && divisibleBy5
puts "FizzBuzz"
when divisibleBy3
puts "Fizz"
when divisibleBy5
puts "Buzz"
else
puts number
end
end
(1..100).each {|n| fizzbuzz n}
评论
\ $ \ begingroup \ $
该程序最初是为代码高尔夫构建的,因此是奇数变量名称。但是,我今天仍然学到了一些东西,谢谢!
\ $ \ endgroup \ $
–撤消
14年7月13日在16:05
\ $ \ begingroup \ $
PascalCased方法名称?我的Ruby感觉有点刺痛...
\ $ \ endgroup \ $
– Flaambino
14年7月13日在16:50
\ $ \ begingroup \ $
扩展Integer来容纳divisibleBy3会很有趣吗?和divisbleBy5?方法。
\ $ \ endgroup \ $
–分裂
2014年7月13日在18:07
\ $ \ begingroup \ $
啊,是的,我们走了,就在我发表我的最后评论的那一刻:)
\ $ \ endgroup \ $
– Flaambino
14年7月13日在19:03
\ $ \ begingroup \ $
我会将看跌期权移动到每个区块。模块化程度更高,重复次数更少。
\ $ \ endgroup \ $
–托克兰
14年7月15日在8:40
#2 楼
正如ckuhn203所指出的,变量名不是很好。在这种情况下,我考虑将它们称为fizz
和buzz
。否则就可以了。有很多不同的方法可以做到这一点。
case
语句是此处用法的不错选择,但您也可以这样做:(1..100).each do |number|
fizz = number % 3 == 0
buzz = number % 5 == 0
print "Fizz" if fizz
print "Buzz" if buzz
print number if !fizz && !buzz
print "\n"
end
您也可以使用
print number unless fizz || buzz
,但在复合条件下使用unless
会很快变得令人困惑进行读取,所以当涉及的内容不止单个布尔表达式时,我宁愿使用优质的if
。或使用字符串连接
(1..100).each do |number|
line = ""
line << "Fizz" if number % 3 == 0
line << "Buzz" if number % 5 == 0
puts line.empty? ? number : line
end
或者,如果您想要一种更灵活的方法,则可以执行以下操作:
不一定能在那里正常工作,可能会打印“ BuzzFizz”。但是,您可以只使用嵌套数组来确保排序:
[["Fizz", 3], ["Buzz", 5]]
当然,如ckuhn所建议的那样,这些都可以包装为方法。
更新:显然,(请参阅注释)FizzBuzz任务可以解释为将“ Fizz”,“ Buzz”或“ FizzBuzz”打印为单独的,不同的字符串,而无需使用串联。在我看来,最初的问题陈述没有说明这一点。它只是给出预期的输出。从那里开始,这取决于您(这实际上就是任务的重点)
不过,如果实际要打印3个不同的字符串,那么您可以执行类似
denominators = { "Fizz" => 3, "Buzz" => 5 } # or more
(1..100).each do |number|
matches = denominators.map { |name, divisor| name if number % divisor == 0 }
puts matches.any? ? matches.join : number
end
但是再次,我认为原始规范不需要任何此类东西。
这只是为了好玩,因为Ruby让您猴子修补任何东西。当然,您不应该像在“现实生活中”那样胡闹修补-这是我刚刚提出来的一种超级讨厌的“解决方案”。
denominators = { "Whatever" => 15, "Fizz" => 3, "Buzz" => 5 }
# since ordering matters, you could just sort the hash (making it an array in process) to
# have the highest denominators first, like so:
#
# denominators.sort_by(&:last).reverse
(1..100).each do |number|
match = denominators.detect { |name, divisor| number % divisor == 0 }
puts match ? match.first : number
end
如果执行以下操作,则无法正常打印整数:-P
评论
\ $ \ begingroup \ $
@ Vogel612我没有意识到这是必需的。你在哪里看到的?
\ $ \ endgroup \ $
– Flaambino
2014年7月13日在23:22
\ $ \ begingroup \ $
来自ckuhn203s答案的链接:codereview.stackexchange.com/questions/33717/…。您将在此处找到更详细的说明。.我目前在移动设备上,这里是1:30,所以请接受我的歉意,因为只发布了一个链接..
\ $ \ endgroup \ $
–Vogel612♦
2014年7月13日23:25
\ $ \ begingroup \ $
@ Vogel612嗯...我认为当时尚不清楚原始(Atwood)规格。它只是说“对于三到五的倍数,打印'FizzBuzz'”-仅此而已。使用字符串连接可以很好地实现这一点。您所链接的答案实际上是“如果规格不同怎么办?” -或无论如何“如果我们在规范中读到了什么?”。所以这不是同一个问题。尽管如此,这很容易做到,我将添加一行,但我称其为恶作剧。
\ $ \ endgroup \ $
– Flaambino
14年7月13日在23:39
\ $ \ begingroup \ $
@ ckuhn203同意,我确实认为这是故意含糊的,因为解释也取决于编码人员。譬如说,在一次面试的情况下,当您阅读该书时,我发现可以很好地解决问题,然后告诉他“好吧,但是如果...?”。对更改的需求(在编码人员或代码方面)的适应性是一件好事。但是,仅仅给出模糊的要求,然后对有效的解决方案进行指责就是恶作剧。如果不使用串联(或不使用串联),将其转过来并对其进行故障处理非常容易。如果该死,该死,如果不死,该死。
\ $ \ endgroup \ $
– Flaambino
14年7月14日在1:27
\ $ \ begingroup \ $
嗡嗡声的重点是看应聘者是否可以编写简单的程序。没有必要尝试对其进行优化或使其具有更大的可扩展性。遗漏了重点。如果您可以获得一个fizzbuzz程序来编译,运行和打印正确的输出,则可以赢得fizzbuzz。现在去写一个真实的程序。
\ $ \ endgroup \ $
–伯格胡子出局了
2014年7月14日下午3:35
#3 楼
我更希望看到计数循环写为1.upto(100) do … end
。Ruby
case
块是表达式。可以排除puts
:1.upto(100) do |n|
i_3 = (n % 3 == 0)
i_5 = (n % 5 == 0)
puts case
when i_3 && i_5
'fizzbuzz'
when i_3
'fizz'
when i_5
'buzz'
else
n
end
end
我个人更进一步:不要将
i_3
和i_5
视为布尔值,而是给它们分配噪声。1.upto(100) do |n|
fizz = (n % 3 == 0) ? 'Fizz' : nil
buzz = (n % 5 == 0) ? 'Buzz' : nil
puts case
when fizz || buzz
"#{fizz}#{buzz}"
else
n
end
end
或者用三元表达式替换
case
:1.upto(100) do |n|
fizz = (n % 3 == 0) ? 'Fizz' : nil
buzz = (n % 5 == 0) ? 'Buzz' : nil
puts (fizz || buzz) ? "#{fizz}#{buzz}" : n
end
为了增强代码的可重用性,建议将代码放入函数,然后
yield
结果而不是直接打印结果。def fizzbuzz(max=100)
1.upto(max) do |n|
fizz = (n % 3 == 0) ? 'Fizz' : nil
buzz = (n % 5 == 0) ? 'Buzz' : nil
yield (fizz || buzz) ? "#{fizz}#{buzz}" : n
end
end
fizzbuzz { |fb| puts fb }
请注意Ruby空白约定:两个缩进空格(您使用了四个),还有一些空格在二元运算符的每一侧(您没有使用过)。
评论
\ $ \ begingroup \ $
@ Vogel612不知道您的反对意见是什么,还是您是认真的。
\ $ \ endgroup \ $
– 200_success
14年7月13日在23:44
\ $ \ begingroup \ $
有点晚了,但是去了。关键是,“ FizzBuzz”部分应为单独的if条件。在此答案中,ChrisWue详细说明了,一旦您更改FizzBuzz条件的输出,就需要重构整个程序。实际要求是:对于可被3和5整除的数字,请打印“ FizzBuzz”。您要做的是:对于可被3和5整除的数字,请依次单击“嘶嘶响”和“嗡嗡声”。那是一匹不同颜色的马。
\ $ \ endgroup \ $
–Vogel612♦
14年7月14日在8:55
\ $ \ begingroup \ $
@ Vogel612我想,您的关注点放错了位置。输出相同且正确。如果需求发生变化,则相应地修改代码将是微不足道的。
\ $ \ endgroup \ $
– 200_success
14年7月14日在10:10
\ $ \ begingroup \ $
@ Vogel612:我已经看过了。我觉得一个众所周知的问题突然要从根本上改变它的规范是偏执狂。 YAGNI发生了什么事?
\ $ \ endgroup \ $
– cHao
2014年7月14日在13:06
\ $ \ begingroup \ $
@ Vogel612:除了符合接受标准。嘶嘶='嘶嘶'; buzz ='Buzz'; puts(“#{fizz}#{buzz}” ==“ FizzBuzz”)输出“ true”。
\ $ \ endgroup \ $
– cHao
2014年7月14日14:22
#4 楼
我无法在其他用户对您的代码的评论中添加任何新内容。但是努力争取我能想到的最具说明性的代码,我会写:class Integer
def divisible_by?(n)
(self % n).zero?
end
end
1.upto(100) do |n|
string = case
when n.divisible_by?(3) && n.divisible_by?(5) then 'fizzbuzz'
when n.divisible_by?(3) then 'fizz'
when n.divisible_by?(5) then 'buzz'
else n.to_s
end
puts(string)
end
这种样式在Ruby中还不是惯用的。)
评论
\ $ \ begingroup \ $
+1代表divisible_by?我也有办法在考虑同一件事(实际上,我前一段时间一直在寻找这样的方法,因为它可能已经内置了,但是没有)。不知道相对于一个简单的.lazy.map构造,它是否更具声明性。
\ $ \ endgroup \ $
– Flaambino
14年7月14日在11:55
\ $ \ begingroup \ $
@Flambino。是的,尚不清楚会更好。但我认为,至少((和功能更多))它将更具模块化。首先,您得到结果(行)。现在你想要什么?打印它们?好的,然后是print_lines(lines)或其他。
\ $ \ endgroup \ $
–托克兰
2014年7月14日在11:57
\ $ \ begingroup \ $
self是divisible_by的定义中必需的。一个更简单的例子是Integer类。 def mod(n); %n;结束;结束。 4.mod(3)=>“ n \ n”。您知道这里要求自我明确的规则吗? (例如,其他规则是写访问器必须包含self。,并且self.class是获得self的类所必需的。)
\ $ \ endgroup \ $
–卡里·斯沃夫兰
2014-12-27 20:22
#5 楼
这是我如何在红宝石中实现此功能的方法,而不是作为打高尔夫球或模糊路线的方法,而是利用许多最佳实践的红宝石习语:
class FizzBuzz
def initialize(start = 1, last = 100)
@start = start
@last = last
end
def each
range.each do |n|
yield fizzbuzz(n)
end
end
private
attr_reader :start, :last
def range
start..last
end
def divisible_by_3(n)
n % 3 == 0
end
def divisible_by_5(n)
n % 5 == 0
end
def divisible_by_15(n)
n % 15 == 0
end
def fizzbuzz(n)
case
when divisible_by_15(n)
'fizzbuzz'
when divisible_by_3(n)
'fizz'
when divisible_by_5(n)
'buzz'
else
n
end
end
end
FizzBuzz.new(1,50).each {|f| puts f}
这里的惯用法?
从一个类开始。初始化程序中的默认值产生了最基本的示例。
小型公共API。其余方法是私有的,以指示它们不应直接使用。
私有attr_reader的读取要比直接使用实例变量更好,
将输出的责任与数据的生成分开。 FizzBuzz类仅在范围上进行迭代并返回结果。该类的用户负责输出,因此每个块都位于底部。
小方法。每种方法都做一件事情,并且其命名方式使您可以轻松分辨出它的作用。阅读简短的方法很容易确认该方法的作用。
请注意,被3整除和被5整除意味着可以被15整除。有人可以提出使该方法仅使用先前方法的论点:
divisible_by_3 && divisible_by_5
但我认为是的,这比您的示例更长。但是在逻辑更复杂的大型项目中,遵循这些惯用法将大大提高代码的可读性,从而维护您的代码库。
fizzbuzz方法的另一个版本可能是:
def fizzbuzz(n)
divisible_by_15(n) and return 'fizzbuzz'
divisible_by_3(n) and return 'fizz'
divisible_by_5(n) and return 'buzz'
n
end
这消除了case语句的假定代码味道,但是是否更好是值得商bat的。有些人反对一个以上的回报声明;其他人反对案例陈述...我可以选择两种方式。
评论
\ $ \ begingroup \ $
我喜欢您对类的使用,但是我可以说一两个关于您以自己的方式使用15的事情。可能希望自己对此进行审查。 =;)-
\ $ \ endgroup \ $
–RubberDuck
2014年12月27日在3:03
\ $ \ begingroup \ $
我指出了一个要点-在这个简单的示例中,很容易说被3和5整除的东西也可以被15整除。但是,如果这些策略做了更复杂的事情,则需要重新使用现有的3和5方法。这仅取决于您的问题域是否允许进行语义优化。
\ $ \ endgroup \ $
– DGM
2014-12-27 5:24
评论
您可能可以从该问题的四个Ruby回答之一中学到一两个东西。免责声明:该站点上没有任何代码遵循最佳实践(故意)。