我正在学习Ruby中Enumerable的神奇之处。我听说只需要包含each
并实现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
方法,就可以拥有该类的Enumerable功能。
所以,我考虑过实现自己的自定义类Foo。它看起来如下:
each
此类采用数组,其Array#each
的工作方式与>> 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]
。有没有办法让它each
。
另外请评论我实施return enum_for(:each) unless block_given?
的方式。如果有更好的方法请告诉我。在我的实现中,我没有使用这一行F = @(x, y, z)[x+y+z; x*y*z];
funcc = @(x)F(x(1), x(2), 0);
,然后在没有提供块的情况下它不起作用。我从某个地方借了这条线,也请告诉我这是否是处理这种情况的正确方法。
答案 0 :(得分:2)
each
不会修改数组。如果要返回修改后的数组,请使用map
:
def each
return enum_for(:each) unless block_given?
@num.map { |i| yield i + 1 }
end
f.each { |i| p i }
2
3
4
=> [2, 3, 4]
但我建议在自定义方法中使用每个方法。您可以在initialize
方法中将数组的每个元素递增1,因为您希望将其用于所有计算。此外,您可以修改each
方法,以避免在块中传递enum_for
来使用block_given?
。最后,您的代码将如下所示:
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]
答案 1 :(得分:2)
each
的返回值应该是接收者,即self
。但是你要回复调用@num.each
的结果。现在,正如我刚才所说,each
返回self
,而错误@num.each
会返回@num
。
修复很简单:只需返回self
:
def each
return enum_for(:each) unless block_given?
@num.each { |i| yield i + 1 }
self
end
或者,可能还有一点Rubyish:
def each
return enum_for(:each) unless block_given?
tap { @num.each { |i| yield i + 1 }}
end
[实际上,从Ruby 1.8.7+开始,each
在没有块的情况下调用时也应该返回Enumerator
,但是你已经正确地处理了它。提示:如果您想通过覆盖它们来实现某些其他Enumerable
方法的优化版本,或者想要添加自己的Enumerable
类似行为的方法,那么就像原始方法一样一遍又一遍地剪切和粘贴完全相同的代码行,并且在某一时刻,您将意外忘记更改方法的名称。如果您使用return enum_for(__callee__) unless block_given?
替换该行,则无需记住更改名称。]
答案 2 :(得分:1)
您需要使用map
代替each
。
f.map { |i| p i }
#=> [2,3,4]
Foo
包含Enumerable
这一事实意味着可以在Enumerable
Foo
的所有方法
答案 3 :(得分:0)
这个类采用一个数组,每个数组的工作方式与
Array#each
几乎相似。我知道它的返回值,但不应该是[2,3,4]。
def返回最后执行的语句的结果。
Array#each
返回原始数组。
将这些规则应用于您的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]