为什么Range #select会生成一个Array对象?

时间:2016-10-28 18:21:22

标签: arrays ruby object range ruby-2.3.1

考虑我有一个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生成一个Array对象?

我相信我并不完全理解Ruby对象模型。我目前的理解是factors.class.eql? Range应该返回true,而不是false

factors.class.eql? Array # => true

2 个答案:

答案 0 :(得分:2)

Ruby中的对象模型是简单的单继承,但能够“混合”模块以添加共享行为。在您的情况下,您使用模块select中存在的Enumerable方法。该模块混合到Array,Hash和Range中。这给出了这些类方法的实例,例如select。您可以在此处阅读有关可枚举方法的更多信息:https://ruby-doc.org/core-2.2.3/Enumerable.html#method-i-select

如果你考虑一下,Range#select返回一个数组是有意义的。你不是从范围中选择连续的值吗?您正在选择块返回true的任意值,这使得无法返回范围,因此#select将始终返回一个数组,即使它在Hash或任何其他混合在Enumerable中的类上调用。 / p>

<强>更新

了解Enumerable如何从Range

返回一个数组

要实现Enumerable中混合的任何类,您只需在类上定义#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都知道如何处理它。这是因为在你的新#each方法中,你已经迭代了一个数组! Enumerable使用您的each方法来实现所有其他可枚举方法,例如: any?selectfind

那个#each方法是告诉Ruby如何迭代你的对象集合的地方。一旦Ruby知道如何迭代你的对象,结果就已经是一个数组。

Rubinius实现Range

您可以在此处看到,使用whilefirst值循环直到达到last值并yield到每个块的Range#select来实现Range迭代。该块将结果收集到一个数组中,这就是如何让数组不再调用select,因为each正在使用global word #probably define this after imports. def GameMode(): global word #add this choice = input('Play alone or play with friends? A F : ') choice = choice.upper() if choice == 'A': wordslotmachine = ['stand','emerald','splash'] word = random.choice(wordslotmachine) word = word.upper() Rules() #ignore this elif choice == 'F': word = input('Enter your word for your friends to guess: ') word = word.upper() Rules() #ignore this else: choice = input('Please enter A or F: ') choice = choice.upper() def MainGame(): guesses = '' turns = 10 underscore = 0 seconds = 1 checker = 0 cheaterchance = 5 global word # add this here 。{/ p>

https://github.com/rubinius/rubinius/blob/master/core/range.rb#L118

一些资源:

答案 1 :(得分:1)

检查范围http://ruby-doc.org/core-2.3.1/Range.html

的文档

它包含模块Enumerable。这就是实施mapall?any?findselectinject以及更多方法的地方