Ruby中的“棋盘上的小麦粒”

“grains of wheat on a chessboard” in Ruby

There are 64 squares on a chessboard. Every square has the double that
the one before. Write a program that shows:
- how many grains were on each square, and
- the total number of grains

我的代码在第一部分工作,但是我在声明总数时遇到了问题。缺少一些基本类/方法声明。谢谢你的帮助。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Grains

  def square(n)
    array_of_grains = []
    (0..63).each {|x| array_of_grains << 2**x}
     n = n-1
    array_of_grains[n]
  end

  def total
     array_of_grains.each {|x| sum += x }
  end

end


这里有很多问题。

首先,array_of_grainssquare的局部。它不存在于方法之外。如果希望它是Grain实例上的实例变量,则需要是@array_of_grains

其次,您的total方法会导致一个错误,因为没有本地array_of_grains变量可供它在上调用each时使用。即使有,sum也将是未定义的。您需要声明sum,并将其初始值设置为0,或者简单地使用inject(&:+),这是本书中最古老的Ruby技巧之一。

第三,每次调用方法square时,都要构建整个array_of_grains数组。您可以使用||=只计算该值一次。

您的方法应该如下所示:

1
2
3
4
5
6
7
8
def square(n)
  @array_of_grains ||= (0..63).map {|x| 2 ** x }
  @array_of_grains[n - 1]
end

def total
  @array_of_grains.inject(&:+)
end

您可以通过在方法上面简单地添加一个attr_accessor :array_of_grains,使您的代码几乎可以工作,但这仍然不能解决没有定义sum的问题。


这里有一个元答案,结合了这里所有的好主意(周围都是赞成票!)使用您的类生成完整的解决方案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Grains

  attr_accessor :array_of_grains

  def initialize(n = 1)
    @array_of_grains = 63.times.with_object([n]) { |i,a| a << 2 * a.last }
  end

  def square(n)
    array_of_grains[n - 1]
  end

  def total
    array_of_grains.reduce(:+)
  end
end

用途:

1
2
3
board = Grains.new
board.square(3) #=> 4
board.total #=> 18446744073709551615


这是写它的一种方法:

1
2
3
4
5
square = 63.times.with_object([1]) { |i,a| a << 2 * a.last }
  #=> [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192,
  #    16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152,
  #...
  #    2305843009213693952, 4611686018427387904, 9223372036854775808]

为了确定总数(我最初在问题中忽略了这一点):

1
2
3
4
5
6
7
total, square = 63.times.reduce([0,[1]]) do |(t,a),_|
  v = 2 * a.last
  [t + v, a << v]
end

total  #=> 18446744073709551614
square #=> (as above)


1
2
@square_of_sums = (n * (n + 1) / 2)**2
@number_of_squares = n * (n + 1) * (2 * n + 1) / 6

这仍然需要时间,但这是你想要做的事情的数学。


如果希望array_of_grains作为实例变量可用,请使用@array_of_grains

您还可以使用Ruby的访问器:

1
2
3
attr_accessor array_of_grains # getter and setter
attr_reader array_of_grains # getter
attr_writer array_of_grains # setter

在这种情况下,您可以:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Grains

  attr_accessor :array_of_grains

  def set_grains(array)
    self.array_of_grains = array
  end

  def total
    return self.array_of_grains.count
  end

end