【问题标题】:Ruby: How to use .each to iteration through an array?Ruby:如何使用 .each 遍历数组?
【发布时间】:2019-03-18 21:23:49
【问题描述】:

在纸牌游戏桥牌中,给四张牌点值:Jack: 1, Queen: 2, King: 3, Ace: 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。我应该如何以正确的方式做到这一点?

【问题讨论】:

  • 它们不是“价值”,它们首先被称为“Milton Work Points”:)
  • 提示:T 通常用于 10,所以它们都是单个字符。
  • #each 方法调用后还有一个下划线,可能是错字但值得一提

标签: ruby loops iteration block


【解决方案1】:

这里的问题是,当你使用块内的每个变量时,是数组内的对象而不是索引,所以你可以如下工作:

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

如果你在 pry 中执行

[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

【讨论】:

  • 您不需要最后一行中的map 步骤:cards.sum(&:card_points) (MRI v2.4+) 或cards.inject(0) { |t,c| t+c.card_points }
  • 感谢您的更新,请考虑用这一点编辑问题
【解决方案2】:

错误消息指出“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'd 模块 Enumerable,这就是为什么我们可以在 arr 上调用实例方法 Enumerable#reduce。 (Array.included_modules #=> [Enumerable, Kernel])。

Enumerable 中的所有其他实例方法一样,Enumerable#reduce(又名inject)需要一个作为Enumerator 类实例的接收器,但arrArray 的实例,而不是@ 987654345@。 Ruby 解决了这个问题,如下所示。当在arr 上调用reduce 时,她看到arr 不是Enumerator 的实例,因此她检查arr 是否有一个方法each(即arr 的方法)类Array 有一个实例方法each)。确实如此,所以她在arr 上调用each 来获取

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 被调用,但它确实如此。我们可以通过在没有方法each 的类中包含Enumerable 来确认这一点,然后看看会发生什么。

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 以及为什么在调用来自Enumerable 的实例方法之前对该类的实例调用each

【讨论】:

    猜你喜欢
    • 2015-03-03
    • 2015-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-21
    • 1970-01-01
    • 2017-01-11
    • 1970-01-01
    相关资源
    最近更新 更多