每个方法何时以及如何工作?

时间:2018-12-09 04:55:51

标签: ruby

我不明白为什么在此代码中定义了each方法:

class VowelFinder
  include Enumerable

  def initialize(string)
    @string = string
  end

  def each
    @string.scan(/[aeiou]/) do |vowel|
      yield vowel
    end
  end
end

vf = VowelFinder.new("the quick brown fox jumped")
vf.inject(:+)
# => "euiooue"

该代码有效,但不能没有each。另外,如果我使用ArrayRange类,并覆盖其中的each方法,则会发生一些不同的事情。

创建对象并调用inject方法时,each方法何时以及如何工作?

1 个答案:

答案 0 :(得分:1)

TL:DR 可枚举的mixin与您签订了某种合同。您告诉它each的含义,它带来了数十种其他方法。 inject是这些方法之一。

实际讨论

inject如何工作?粗略地说,它遍历了由 elements 组成的对象,并在元素到达时对其应用了功能。字符串"the quick brown fox jumped"的元素是什么?可以说,它们就是它的角色。因此,如果我们将字符串视为字符数组并注入+来连接字符串,那么我们期望得到原始的字符串本身:

arr = "The quick brown fox jumped".scan /./
puts arr.inject(:+) # => the quick brown fox jumped

但是现在让我们更进一步。 Ruby具有mixin,尤其是具有Enumerable mixin。这使我们可以将任何我们喜欢的东西视为“具有元素的东西”。因此,让我们可以在字符串 itself 的元素上插入+而不是数组。为此,我们必须为字符串定义each,以便可以遍历字符串的元素。一旦做到这一点,Enumerable的许多方法(包括inject)就会浮现。

each对一个字符串意味着什么?同样,我们可以简单地使它表示每个字符。我们可以想象通过将each别名为现有each_char来做到这一点:

class String
  include Enumerable
  alias each each_char
end
s = "the quick brown fox jumped"
puts s.inject(:+) # => the quick brown fox jumped

但是我们可以将each自己从头定义,而不是将each_char别名为each。我们已经知道一种使用scan的方法:

class String
  include Enumerable
  def each
    self.scan(/./) do |ch|
      yield ch
    end
  end
end
s = "the quick brown fox jumped"
puts s.inject(:+) # => the quick brown fox jumped

但是each的定义由我决定,因此,我可以扫描某些 other 模式,而不用扫描每个 字符!例如,我可以扫描元音:

class String
  include Enumerable
  def each
    self.scan(/[aeiou]/) do |ch|
      yield ch
    end
  end
end
s = "the quick brown fox jumped"
puts s.inject(:+) # => euiooue

但是,不要以这种奇怪的方式定义each来使String la脚。让我们将整个功能拖到一个自定义类上。我们称之为VowelFinder!因此,我们以您开始的代码结束。