我已经在Ruby中实现了FizzBu​​zz挑战的这种实现:

(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最佳实践?

评论

您可能可以从该问题的四个Ruby回答之一中学到一两个东西。免责声明:该站点上没有任何代码遵循最佳实践(故意)。

#1 楼


您已经内联了整个程序。将FizzBu​​zz分离为自己的接受n作为参数的方法将是有益的。
说到n,我不鼓励使用一个和两个字母变量名。 number会更好。
我喜欢在输入select语句之前预先计算布尔值,但是这两个需要更好的名称。 divisibleBy3divisibleBy5会更好。
我也喜欢您正确地解释了需求,并且没有将结果串联在一起以试图过早优化。有关更多信息,请参见此内容。

您的某些语句可能会占用一些呼吸空间。用(n % 3)代替(n%3)等。
在Ruby中,括号不是必需的,但是它们有助于弄清楚。

正如@rofl在聊天中指出的那样,您正在打印“ fizz”,“ buzz”和“ fizzbuzz”,而不是“ Fizz”,“ Buzz”和“ FizzBu​​zz”。

执行我的建议,您会得到类似的结果。

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所指出的,变量名不是很好。在这种情况下,我考虑将它们称为fizzbuzz

否则就可以了。有很多不同的方法可以做到这一点。 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所建议的那样,这些都可以包装为方法。

更新:显然,(请参阅注释)FizzBu​​zz任务可以解释为将“ Fizz”,“ Buzz”或“ FizzBu​​zz”打印为单独的,不同的字符串,而无需使用串联。在我看来,最初的问题陈述没有说明这一点。它只是给出预期的输出。从那里开始,这取决于您(这实际上就是任务的重点)

不过,如果实际要打印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)规格。它只是说“对于三到五的倍数,打印'FizzBu​​zz'”-仅此而已。使用字符串连接可以很好地实现这一点。您所链接的答案实际上是“如果规格不同怎么办?” -或无论如何“如果我们在规范中读到了什么?”。所以这不是同一个问题。尽管如此,这很容易做到,我将添加一行,但我称其为恶作剧。
\ $ \ 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_3i_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 \ $
有点晚了,但是去了。关键是,“ FizzBu​​zz”部分应为单独的if条件。在此答案中,ChrisWue详细说明了,一旦您更改FizzBu​​zz条件的输出,就需要重构整个程序。实际要求是:对于可被3和5整除的数字,请打印“ FizzBu​​zz”。您要做的是:对于可被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}” ==“ FizzBu​​zz”)输出“ 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的读取要比直接使用实例变量更好,
将输出的责任与数据的生成分开。 FizzBu​​zz类仅在范围上进行迭代并返回结果。该类的用户负责输出,因此每个块都位于底部。
小方法。每种方法都做一件事情,并且其命名方式使您可以轻松分辨出它的作用。阅读简短的方法很容易确认该方法的作用。
请注意,被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