Ruby:如何使用.each遍历数组?

时间:2018-10-14 06:08:31

标签: ruby loops iteration block

在纸牌游戏桥中,给了四张纸牌点值:杰克:1,皇后:2,国王:3,王牌:4。给定与一手纸牌相对应的字符串数组(纸牌表示为:[“ 2”,“ 3”,“ 4”,“ 5”,“ 6”,“ 7”,“ 8”,“ 9”,“ 10”,“ J”,“ Q”,“ K”, “ A”]),返回该手的高牌点总数。

我可以使用while循环解决这个简单的问题,但我想学习如何使用.each遍历数组,这是我的代码不起作用

def high_card_points(hand)
  sum = 0
  hand.each do |i|
    if hand[i] == "J"
      sum += 1
    elsif hand[i] == "Q"
      sum += 2
    elsif hand[i] == "K"
      sum += 3
    elsif hand[i] == "A"
      sum += 4
    end
  end
  sum
end

现在,当我运行它时,出现错误no implicit conversion of String into Integer。我应该如何以正确的方式做到这一点?

2 个答案:

答案 0 :(得分:2)

这里的问题是,当您使用块内的每个变量时,是数组内的对象而不是索引,因此可以按以下方式工作:

def high_card_points(hand)
  sum = 0
  hand.each do |card|
    if card == "J"
      sum += 1
    elsif card == "Q"
      sum += 2
    elsif card == "K"
      sum += 3
    elsif card == "A"
      sum += 4
    end
  end
  sum
end

,如果您执行撬动操作

[5] pry(main)* => :high_card_points
[6] pry(main)> high_card_points(cards)
=> 10

您也可以像使用each_index一样使用索引。但是您也可以采用其他对象功能的方法:

您可以创建您的课程或对课程字符串进行猴子补丁:

  class String
    def card_points
      case self
      when 'J'
        1
      when 'Q'
        2
      when 'K'
        3
      when 'A'
        4
      else
        0
      end
    end
  end

然后按以下步骤操作:

[31] pry(main)> cards.map(&:card_points).inject(0, :+)
=> 10

答案 1 :(得分:1)

错误消息指出“ TypeError(没有将String隐式转换为Integer)”,并且在行hand[i] == "J"中引发了异常。由each传递到块并分配给块变量i的第一个元素是i = hand.first #=> "2"。因此,我们有hand["2"] == "J",或者实际上是hand.[]("2"),但是方法Array#[]要求其参数为整数,并且“没有将String隐式转换为Integer”。

现在让我解决您问题的另一个方面。

arr = ["2","3","4","5","6","7","8","9","10","J","Q","K","A"]

您可以编写以下内容。

arr.reduce(0) do |tot, s|
  tot + 
  case s
  when "J" then 1
  when "Q" then 2
  when "K" then 3
  when "A" then 4
  else 0
  end
end
  #=> 10  

我能听到你的声音。您是在说:“我说过我想使用.each!”。好吧,你有!让我解释一下。

arr是类Array的实例。 ArrayModule#include了模块Enumerable,这就是为什么我们可以在arr上调用实例方法Enumerable#reduce的原因。 (Array.included_modules #=> [Enumerable, Kernel]

Enumerable中的所有其他实例方法一样,Enumerable#reduce(也称为inject)要求接收者是类Enumerator的实例,但是arrArray而不是Enumerator的实例。 Ruby绕过此问题的方法如下。在reduce上调用arr时,她发现arr不是Enumerator的实例,因此她检查arr是否具有方法{{1 }}(即each的类arr是否具有实例方法Array)。确实如此,因此她在each上调用each以获取

arr

我们现在有了可以在其上调用enum = arr.each #=> #<Enumerator: ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", # "Q", "K", "A"]:each> 的枚举器:

reduce

您看不到enum.reduce(0) do |tot, s| tot + case s when "J" then 1 when "Q" then 2 when "K" then 3 when "A" then 4 else 0 end end #=> 10 被调用,但确实可以。我们可以通过在没有方法Array#each的类中包含Enumerable来确认这一点,然后看看会发生什么。

each

这就是为什么每个包含class C include Enumerable end c = C.new #=> #<C:0x0000000002a118a8> c.reduce {} #=> NoMethodError (undefined method `each' for #<C:0x0000000002a118a8>) class C def each end end c.reduce {} #=> nil 的类都必须具有返回枚举器的实例方法Enumerable的原因,并且为什么要在each的实例方法之前在该类的实例上调用each 1}}。