在 Ruby 中为 Enumerable mixin 实现 `each`
implement `each` for Enumerable mixin in Ruby
我正在 Ruby 学习 Enumerable 的魔力。我听说只需要包含 Enumerable
并实现 each
方法,就可以拥有 Enumerable 的功能 class.
所以,我考虑实现自己的自定义 class 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
这个 class 接受一个数组,它的 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]?
除最后一条声明外,一切都如我所料。我知道它的 return 值,但不应该是 [2, 3, 4]
。有没有办法做到 [2, 3, 4]
.
也请评论我实现的方式each
。如果有更好的方法请告诉我。起初在我的实现中我没有这条线 return enum_for(:each) unless block_given?
然后当没有提供块时它不工作。我从某处借用了这条线,还请告诉我这是否是正确的处理方式。
each
不修改数组。如果要return修改数组,使用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,因为您希望将它用于所有计算。此外,您可以通过在块中传递 block_given?
来修改 each
方法以避免使用 enum_for
。最后您的代码将如下所示:
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]
您需要使用 map
而不是 each
。
f.map { |i| p i }
#=> [2,3,4]
Foo
包含 Enumerable
这一事实意味着 Enumerable
的所有方法都可以在 Foo
的实例上调用
This class takes an array and its each works almost similar to Array#each
.
I know its the return value but shouldn't it be [2, 3, 4].
A def returns 最后一条语句的执行结果。
Array#each
returns原数组
将这些规则应用到您的 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]
each
的 return 值应该是接收者,即 self
。但是您正在 return 调用 @num.each
的结果。现在,正如我刚才所说,each
returns self
,因此 @num.each
returns @num
.
修复很简单:只需 return 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
也应该 return 一个 Enumerator
当调用时没有块,但你已经正确处理。提示:如果你想通过覆盖它们来实现其他一些 Enumerable
方法的优化版本,或者想添加你自己的 Enumerable
-like 方法,具有与原始方法相似的行为,你将要去一遍又一遍地剪切和粘贴完全相同的代码行,有一次,您会不小心忘记更改方法的名称。如果用 return enum_for(__callee__) unless block_given?
替换该行,则不必记住更改名称。]
我正在 Ruby 学习 Enumerable 的魔力。我听说只需要包含 Enumerable
并实现 each
方法,就可以拥有 Enumerable 的功能 class.
所以,我考虑实现自己的自定义 class 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
这个 class 接受一个数组,它的 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]?
除最后一条声明外,一切都如我所料。我知道它的 return 值,但不应该是 [2, 3, 4]
。有没有办法做到 [2, 3, 4]
.
也请评论我实现的方式each
。如果有更好的方法请告诉我。起初在我的实现中我没有这条线 return enum_for(:each) unless block_given?
然后当没有提供块时它不工作。我从某处借用了这条线,还请告诉我这是否是正确的处理方式。
each
不修改数组。如果要return修改数组,使用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,因为您希望将它用于所有计算。此外,您可以通过在块中传递 block_given?
来修改 each
方法以避免使用 enum_for
。最后您的代码将如下所示:
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]
您需要使用 map
而不是 each
。
f.map { |i| p i }
#=> [2,3,4]
Foo
包含 Enumerable
这一事实意味着 Enumerable
的所有方法都可以在 Foo
This class takes an array and its each works almost similar to
Array#each
.I know its the return value but shouldn't it be [2, 3, 4].
A def returns 最后一条语句的执行结果。
Array#each
returns原数组
将这些规则应用到您的 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]
each
的 return 值应该是接收者,即 self
。但是您正在 return 调用 @num.each
的结果。现在,正如我刚才所说,each
returns self
,因此 @num.each
returns @num
.
修复很简单:只需 return 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
也应该 return 一个 Enumerator
当调用时没有块,但你已经正确处理。提示:如果你想通过覆盖它们来实现其他一些 Enumerable
方法的优化版本,或者想添加你自己的 Enumerable
-like 方法,具有与原始方法相似的行为,你将要去一遍又一遍地剪切和粘贴完全相同的代码行,有一次,您会不小心忘记更改方法的名称。如果用 return enum_for(__callee__) unless block_given?
替换该行,则不必记住更改名称。]