我对此很陌生,所以请保持友好。我试图解决克里斯·派恩(Chris Pine)的挑战,将整个歌词打印到墙上的99瓶啤酒上。这似乎可行,但是是否有更好/更有效的方法呢?如果可以的话,只想学习如何改进它。

var1 = 99
loop do
    puts (var1.to_s) + ' bottles of beer on the wall, ' + (var1.to_s) + ' bottles of beer, ' 
    puts 'take one down, pass it around, ' + ((var1 - 1).to_s) + ' bottles of beer on the wall.' 
    var1 = (var1 - 1)
break if var1 == 1
end


评论

欢迎使用代码审查!很高兴您在学习编程的早期阶段就找到了这个网站,我相信您会在这里获得评论家的宝贵见解!

我可以建议将文本更改为:代码中的99个小错误删除代码中的99个小错误,将其修补为代码中的117个小错误;)

“代码中有127个小错误,代码中有127个小错误。功能请求又增加了一个,代码中有-127个小错误”

只有一个? @Flambino一定很好,真的很好。

@CarySwoveland好吧,由于意外的翻转,至少有两个错误。但也许错误计数是一个普通的32位int,在这种情况下,功能请求实际上添加了4,294,967,042个新错误。真相可能介于这两个极端之间。

#1 楼

在任何编程语言中,都应始终为变量,函数,方法以及分配名称所需的其他任何名称使用有意义的名称。
对于您而言,var1完全是非描述性的。更好的名称是bottlesbottle_countbottles_remaining甚至更具体。
其次,在Ruby中,您很少需要使用普通的loop。在您的情况下,您基本上想重复进行大约99次相同的操作(实际上,不,您不完全是这样,但是我们稍后再讲)。
在Ruby中执行此操作的一种简单方法是要说的是:
99.downto(1) do |remaining|
  # stuff to do 99 times
end

remaining变量将从99开始并递减至1,因此我们可以用它来打印经文。
说到打印,您正在使事情变得困难
如果您使用双引号而不是单引号,则可以使用#{...}将内容插入字符串。像这样:
99.downto(1) do |remaining|
  puts "#{remaining} bottles of beer on the wall, #{remaining} bottles of beer."
  puts "Take one down and pass it around, #{remaining-1} bottles of beer on the wall."
  puts # print a blank line between verses.
end

请注意,#{...}可以包含任何Ruby代码(但为您自己而保持简单!),因此减去1的一些算法也是可行的。而且不需要在任何地方使用to_s,因为使用字符串插值时会自动发生。
上面的代码与您当前的代码相当,除了它在经文之间打印出空白行。而且它会打印99个诗句的事实-您实际上只在您的版本中打印98个诗句。
...但是,它打印的歌词错误。无论如何语法上的歌词都是错误的。
大多数经文的确是这样的:

X瓶啤酒在墙上,X瓶啤酒。周围有(X-1)瓶啤酒。

X是我们剩下的瓶子数量。 (顺便说一下,这看起来像上面的Ruby代码多少;该代码几乎很容易阅读。)
但是请注意最后三节的变化:

2瓶墙上的啤酒,2瓶啤酒。
放下一瓶并通过,在墙上放一瓶啤酒。
墙上放一瓶啤酒,一瓶在啤酒上。
放下并穿过它,再也不用喝啤酒了
墙上没有啤酒,也没有啤酒。去商店买更多的99瓶啤酒。

这就是为什么这是编程挑战。如果只是编写上面的5行代码,那不会太棘手。
但是有趣的是,当您有规则的例外时,而最后三节经文就是这样。它们甚至不遵循单一,不同的规则集-它们各自以自己的方式不同。
但是,如何处理取决于您自己的方式。毕竟,这是一个挑战。有聪明的方法,简单的方法,复杂的方法,荒谬的方法……您可以选择哪种方法取决于您。这个想法是让您考虑问题及其潜在的解决方案。没有一个正确的答案。

其他小玩意:

Ruby约定是使用2个缩进空间-而不是目的地4个空格,而不是制表符。
break行应缩进。
var1 = (var1 - 1)中的括号完全没有必要。此外,您可以将其编写为var1 -= 1,这是同一件事的简写。


评论


\ $ \ begingroup \ $
我看过(和写过)生产代码,其中实现了类似于后三节经文的特殊情况:单数和复数,有时还有替代短语。但是,必须考虑将这些特殊情况编码为每次迭代的大块的不同方法的复杂性。即使该代码预期可以持续很长时间并且需要维护,我也可能只将前97个经文放在循环中,并在末尾逐字逐行打印后三个经文。
\ $ \ endgroup \ $
– David K
2015年2月1日于13:11

\ $ \ begingroup \ $
@DavidK当然,这是一个有效的解决方案。挑战的真正目的不仅在于找到“唯一正确的解决方案™”,还在于使程序员稍加思考并考虑甚至存在哪些解决方案。硬编码最后三节经文确实是一种解决方案-也是一种很好且简单的解决方案。除非对某件事有其他要求,否则实现完全取决于程序员。
\ $ \ endgroup \ $
– Flaambino
2015年2月1日14:07在

\ $ \ begingroup \ $
我同意。您提出了一个重要的观点,那就是人们通常会忽略那些会稍微改变大多数情况下的模式的情况。有时您会看到等同于打印“ 1瓶”的生产代码。正如您所指出的,有很多方法可以解决此问题。在这种特殊情况下,我只是投票赞成“简单”解决方案。
\ $ \ endgroup \ $
– David K
2015年2月1日15:19

\ $ \ begingroup \ $
@DavidK哦,天哪,这是我的宠儿。当我看到类似“ 1个新邮件”之类的东西而不是适当的复数形式时,我会稍有畏缩。我承认自己自己做这件事是有罪的,但这总是让我觉得有人(像我一样)根本不在乎。不会激发人们对该软件的信心。
\ $ \ endgroup \ $
– Flaambino
2015年2月1日于16:20

\ $ \ begingroup \ $
更好的名字是n
\ $ \ endgroup \ $
–山姆·沃特金斯(Sam Watkins)
2015年2月3日14:09

#2 楼

看起来不错。您可能会考虑使用枚举数和块来避免编写:

var = (var1 - 1)


(通常会写var -= 1)并摆脱循环。例如,使用Integer#downto:

99.downto(1) do |n|
  puts "#{n} bottles of beer on the wall, #{n} bottles of beer, " 
  puts "take one down, pass it around, #{n-1} bottles of beer on the wall." 
end


或者,您可以创建要使用Array#each枚举的数组:

nbr = 99.downto(0).to_a
n = nbr.shift
while nbr.any?
  puts "#{n} bottles of beer on the wall, #{n} bottles of beer, " 
  n = nbr.shift
  puts "take one down, pass it around, #{n} bottles of beer on the wall." 
end


第三种方法是直接使用枚举器,并使用Enumerator#next逐步执行:

enum = Enumerator.new { |y| 100.times { |i| y << 99-i } }
n = enum.next
loop do
   puts "#{n} bottles of beer on the wall, #{n} bottles of beer, " 
   n = enum.next
   puts "take one down, pass it around, #{n} bottles of beer on the wall." 
end


next引发StopIteration异常,当尝试超出迭代器的末尾。内核通过打破循环来处理异常。