在Ruby中为可枚举mixin实现'each'

在Ruby中为可枚举mixin实现'each',ruby,module,block,enumerable,Ruby,Module,Block,Enumerable,我正在学习Ruby中可枚举的魔力。我听说一个类只需包含Enumerable并实现每个方法,就可以拥有该类的Enumerable功能 因此,我考虑实现自己的自定义类Foo以供实践。如下所示: class Foo include Enumerable def initialize numbers @num = numbers end def each return enum_for(:each) unless block_given? @num.each

我正在学习Ruby中可枚举的魔力。我听说一个类只需包含Enumerable并实现每个方法,就可以拥有该类的Enumerable功能

因此,我考虑实现自己的自定义类Foo以供实践。如下所示:

class Foo
  include Enumerable

  def initialize numbers
    @num = numbers
  end

  def each
    return enum_for(:each) unless block_given?
    @num.each { |i| yield i + 1 }
  end
end
这个类接受一个数组,每个数组的工作原理几乎与Arrayeach类似。区别在于:

>> f = Foo.new [1, 2, 3]
=> #<Foo:0x00000001632e40 @num=[1, 2, 3]>
>> f.each { |i| p i }
2
3
4
=> [1, 2, 3]   # Why this? Why not [2, 3, 4]?
除了最后一句话之外,一切都像我期望的那样。我知道它是返回值,但它不应该是[2,3,4]。有没有办法做到这一点[2,3,4]

另外,请评论一下我实施每一项的方式。如果有更好的办法,请告诉我。起初在我的实现中,我没有让这一行返回enum_for:each,除非给出了block_?然后,当没有提供区块时,它就不工作了。我从某处借用了这行代码,还请告诉我这是否是处理这种情况的正确方法。

每一行都不会修改数组。如果要返回修改后的数组,请使用映射:

但我建议在自定义方法中使用每种方法。您可以在initialize方法中将数组的每个元素递增1,因为您希望将其用于所有计算。此外,您还可以通过传递给定的块来修改每个方法,以避免使用枚举?在一个街区内。最后,您的代码将如下所示:

class Foo
  include Enumerable

  def initialize(numbers)
    @num = numbers.map {|n| n + 1 }
  end

  def each
    @num.each { |i| yield i if block_given? }
  end
end

f = Foo.new [1, 2, 3]
=> #<Foo:0x00000000f8e0d0 @num=[2, 3, 4]>
f.each { |i| p i }
2
3
4
=> [2, 3, 4]
每个都不修改数组。如果要返回修改后的数组,请使用映射:

但我建议在自定义方法中使用每种方法。您可以在initialize方法中将数组的每个元素递增1,因为您希望将其用于所有计算。此外,您还可以通过传递给定的块来修改每个方法,以避免使用枚举?在一个街区内。最后,您的代码将如下所示:

class Foo
  include Enumerable

  def initialize(numbers)
    @num = numbers.map {|n| n + 1 }
  end

  def each
    @num.each { |i| yield i if block_given? }
  end
end

f = Foo.new [1, 2, 3]
=> #<Foo:0x00000000f8e0d0 @num=[2, 3, 4]>
f.each { |i| p i }
2
3
4
=> [2, 3, 4]
你需要使用地图而不是每一个

Foo包含Enumerable这一事实意味着可以在Foo的实例上调用Enumerable的所有方法。您需要使用map而不是每个方法

Foo包含Enumerable这一事实意味着所有Enumerable方法都可以在Foo实例上调用

这个类接受一个数组,每个数组的工作原理几乎与Arrayeach类似

我知道它是返回值,但它不应该是[2,3,4]

def返回最后执行的语句的结果

Arrayeach返回原始数组

将这些规则应用于def:

def each
  return enum_for(:each) unless block_given?
  @num.each { |i| yield i + 1 }  #Array#each returns @num
end  #When a block is given, the result of the last statement that was executed is @num
你可以一直这样做:

class Foo
  include Enumerable

  def initialize numbers
    @num = numbers
    @enum_vals = []
  end

  def each
    if block_given?
      @num.each do |i| 
        yield i + 1
        @enum_vals << i + 1
      end

      @enum_vals
    else
      enum_for
    end
  end
end

result = Foo.new([1, 2, 3, ]).each {|i| p i}
p result

--output:--
2
3
4
[2, 3, 4]
这个类接受一个数组,每个数组的工作原理几乎与Arrayeach类似

我知道它是返回值,但它不应该是[2,3,4]

def返回最后执行的语句的结果

Arrayeach返回原始数组

将这些规则应用于def:

def each
  return enum_for(:each) unless block_given?
  @num.each { |i| yield i + 1 }  #Array#each returns @num
end  #When a block is given, the result of the last statement that was executed is @num
你可以一直这样做:

class Foo
  include Enumerable

  def initialize numbers
    @num = numbers
    @enum_vals = []
  end

  def each
    if block_given?
      @num.each do |i| 
        yield i + 1
        @enum_vals << i + 1
      end

      @enum_vals
    else
      enum_for
    end
  end
end

result = Foo.new([1, 2, 3, ]).each {|i| p i}
p result

--output:--
2
3
4
[2, 3, 4]
每个的返回值应该是接收者,即self。但是您正在返回调用@num.each的结果。现在,正如我刚才所说的,每个都返回self,ergo@num.each返回@num

解决方法很简单:只需返回self:

或者,可能更红一点:

def each
  return enum_for(:each) unless block_given?
  tap { @num.each { |i| yield i + 1 }}
end
[事实上,从Ruby 1.8.7+开始,在没有块的情况下调用时,每个都应该返回一个枚举数,但是您已经正确地处理了。提示:如果您希望通过重写其他一些可枚举方法来实现其优化版本,或者希望添加与原始方法具有类似行为的类似可枚举方法s、 您将一次又一次地剪切和粘贴完全相同的代码行,并且在某一点上,您将意外地忘记更改方法的名称。如果您将该行替换为返回enum_for_ucallee_uuu,除非给出了block_?,则您不必记着更改名称。]

每个的返回值应该是接收者,即self。但是您正在返回调用@num.each的结果。现在,正如我刚才所说,每个返回self,ergo@num。每个返回@num

解决方法很简单:只需返回self:

或者,可能更红一点:

def each
  return enum_for(:each) unless block_given?
  tap { @num.each { |i| yield i + 1 }}
end

[事实上,从Ruby 1.8.7+开始,在没有块的情况下调用时,每个都应该返回一个枚举数,但是您已经正确地处理了。提示:如果您希望通过重写其他一些可枚举方法来实现其优化版本,或者希望添加与原始方法具有类似行为的类似可枚举方法s、 您将一次又一次地剪切和粘贴完全相同的代码行,并且在某一点上,您将意外地忘记更改方法的名称。如果您将该行替换为返回enum_for_ucallee_uuu,除非给出了block_?,则您不必记着更改名称。]

如果给定块,则yieldi+1?对于f,我得到了错误的结果。each@Lokesh,可能您遗漏了一些代码。请参阅当前版本的答案。我非常喜欢您的第一个解决方案。您建议的第二个解决方案是更改初始化,但它不适合我的需要,因为我使用的不是i+1,而是类似于“abc”*i的内容,它占用了
空格,所以在手之前更改@num会占用更多的空间。@Lokesh,没问题,把它放到每一个:@num.map{| i | i+1}中。每个{| i |如果给定块|}就产生i。这只是一个例子,方向,你是一个工程师,所以结合解决方案!如果给定块_,产量为+1?我得到了f的错误结果。each@Lokesh,也许你错过了一些代码。请看当前版本的answerI,我非常喜欢您的第一个解决方案。您建议的第二个解决方案是更改initialize,它不适合我的需要,因为我没有使用i+1,而是使用了类似于“abc”*i的东西来占用空间,所以在手之前更改@num会占用更多的空间。@Lokesh,没问题,把它放到每个:@num.map{| i | i+1}。如果给定块|,每个{i |都会产生i。这只是一个例子,方向,你是一个工程师,所以结合解决方案!你好问题是我希望自定义的每个都像正常的一样。@Lokesh它确实像正常的每个一样。也就是说,数组的行为相同。我使方法链接成为可能。地图就是要走的路。问题是我希望自定义的每个都像正常的一样。@Lokesh它确实像正常的每个一样。也就是说,数组的行为相同。我使方法链接成为可能。地图是前进的道路。