为什么 Range#select 会产生一个 Array 对象?
Why does Range#select produce an Array object?
假设我有一个 Range 对象,
(1..30).class # => Range
现在考虑我正在尝试找到 num
、
的因子
num = 30
factors = (1..num).select { |n| num % n == 0 }
factors.class # => Array
对于 Ruby 2.3.1
,Range 对象没有 #select
,但 Array 对象有。调用 Range#select
如何生成数组对象?
我认为我没有完全理解 Ruby 对象模型。我目前的理解是factors.class.eql? Range
应该returntrue
,而不是false
。
factors.class.eql? Array # => true
查看范围文档 http://ruby-doc.org/core-2.3.1/Range.html
它说包含模块 Enumerable
。这就是实现 map
、all?
、any?
、find
、select
、inject
以及更多方法的地方。
Ruby 中的对象模型很简单,单一继承,但能够 "mixin" 模块添加共享行为。在您的情况下,您使用的是模块 Enumerable
中存在的 select
方法。这个模块混合成Array、Hash和Range。这给出了那些 classes 方法的实例,例如 select
。您可以在此处阅读有关可枚举方法的更多信息:https://ruby-doc.org/core-2.2.3/Enumerable.html#method-i-select
如果你仔细想想,Range#select
return 是一个数组是有道理的。您不是从范围中选择连续的值吗?您正在选择块 return 为真的任意值,这使得不可能 return 一个范围因此, #select
将始终 return 一个数组,即使它被调用在 Hash 或混合在 Enumerable 中的任何其他 Class。
更新:
了解 Enumerable 如何return从范围
创建数组
要实现混合在 Enumerable
中的任何 classes,您只需在 class 上定义 #each
方法。假设您重新实现了 Range:
class Range
include Enumerable # mixin
def initialize(first, last)
@number_range = first.upto last # this is an array of ints
end
def each(&block) # this methods returns an enumerable
@number_range.each &block
end
end
通过以上我们可以初始化我们假设的范围实例:
@hypo_range = Range.new 1, 10
并在其上调用可枚举方法:
@hypo_range.any? { |i| i == 5 } # => true
@hypo_range.select &:odd? # => [1,3,5,7,9]
因为你只需要实现 #each
来挂钩 Enumerable API,所以 Ruby 知道如何处理它,无论对象的 class 是什么是。这是因为在新的 #each
方法中,您已经在遍历数组了! Enumerable 在后台使用您的 each
方法在顶部实现所有其他可枚举方法,例如any?
、select
、find
等
#each
方法是告诉 Ruby 如何迭代对象集合的地方。一旦 Ruby 知道如何遍历您的对象,结果就已经是一个数组。
Range 的 Rubinius 实现
这里可以看到Range是通过使用while
从first
值循环直到达到last
值然后yield
ing到block实现的在每次迭代中。该块将结果收集到一个数组中,这就是您如何从调用 Range#select
中获取数组,因为 select
在幕后使用 each
。
https://github.com/rubinius/rubinius/blob/master/core/range.rb#L118
一些资源:
假设我有一个 Range 对象,
(1..30).class # => Range
现在考虑我正在尝试找到 num
、
num = 30
factors = (1..num).select { |n| num % n == 0 }
factors.class # => Array
对于 Ruby 2.3.1
,Range 对象没有 #select
,但 Array 对象有。调用 Range#select
如何生成数组对象?
我认为我没有完全理解 Ruby 对象模型。我目前的理解是factors.class.eql? Range
应该returntrue
,而不是false
。
factors.class.eql? Array # => true
查看范围文档 http://ruby-doc.org/core-2.3.1/Range.html
它说包含模块 Enumerable
。这就是实现 map
、all?
、any?
、find
、select
、inject
以及更多方法的地方。
Ruby 中的对象模型很简单,单一继承,但能够 "mixin" 模块添加共享行为。在您的情况下,您使用的是模块 Enumerable
中存在的 select
方法。这个模块混合成Array、Hash和Range。这给出了那些 classes 方法的实例,例如 select
。您可以在此处阅读有关可枚举方法的更多信息:https://ruby-doc.org/core-2.2.3/Enumerable.html#method-i-select
如果你仔细想想,Range#select
return 是一个数组是有道理的。您不是从范围中选择连续的值吗?您正在选择块 return 为真的任意值,这使得不可能 return 一个范围因此, #select
将始终 return 一个数组,即使它被调用在 Hash 或混合在 Enumerable 中的任何其他 Class。
更新:
了解 Enumerable 如何return从范围
创建数组要实现混合在 Enumerable
中的任何 classes,您只需在 class 上定义 #each
方法。假设您重新实现了 Range:
class Range
include Enumerable # mixin
def initialize(first, last)
@number_range = first.upto last # this is an array of ints
end
def each(&block) # this methods returns an enumerable
@number_range.each &block
end
end
通过以上我们可以初始化我们假设的范围实例:
@hypo_range = Range.new 1, 10
并在其上调用可枚举方法:
@hypo_range.any? { |i| i == 5 } # => true
@hypo_range.select &:odd? # => [1,3,5,7,9]
因为你只需要实现 #each
来挂钩 Enumerable API,所以 Ruby 知道如何处理它,无论对象的 class 是什么是。这是因为在新的 #each
方法中,您已经在遍历数组了! Enumerable 在后台使用您的 each
方法在顶部实现所有其他可枚举方法,例如any?
、select
、find
等
#each
方法是告诉 Ruby 如何迭代对象集合的地方。一旦 Ruby 知道如何遍历您的对象,结果就已经是一个数组。
Range 的 Rubinius 实现
这里可以看到Range是通过使用while
从first
值循环直到达到last
值然后yield
ing到block实现的在每次迭代中。该块将结果收集到一个数组中,这就是您如何从调用 Range#select
中获取数组,因为 select
在幕后使用 each
。
https://github.com/rubinius/rubinius/blob/master/core/range.rb#L118
一些资源: