Ruby Enumerable с базовым хэшем

У меня есть класс, который использует миксины Enumerable и Forwardable. Дело в том, что несмотря на то, что #each был реализован (или делегирован), #member? (который также поставляется с Enumerable) не работает должным образом.

require "forwardable"
class RestrictedHash
  include Enumerable
  extend Forwardable

  def_delegators :@hash, :[], :[]=, :each

  def initialize
    @hash = {}
  end
end

r_h = RestrictedHash.new
r_h[:a] = []
r_h.member?(:a)       #=> false
r_h.member?(:a, [])   #=> Wrong number of arguments (2 for 1)
r_h.member?([:a, []]) #=> true

h = {}
h[:a] = []
h.member?(:a)         #=> true
h.member?([:a, []])   #=> false

Любые идеи о том, почему я получаю эту разницу в поведении?


person cenouro    schedule 16.11.2014    source источник


Ответы (1)


Причина этого в том, что метод each для Hash дает пары ключа и значения, поэтому для вашего примера @hash:

irb(main):001:0> h = {}
=> {}
irb(main):002:0> h[:a] = []
=> []
irb(main):003:0> h.each { |i| puts i.inspect }
[:a, []]

Это означает, что когда реализация member? в Enumerable использует each для проверки того, является ли указанное значение членом, она успешно найдет ([:a, []], но не сам ключ :a.

В самом классе Hash member? реализован для вызова rb_hash_has_key.

person mikej    schedule 16.11.2014
comment
Таким образом, хотя хэши реализуют интерфейс Enumerable, на самом деле они реализуют методы, а не просто включают Enumerable? - person cenouro; 17.11.2014
comment
Я не уверен в точных деталях этого. Краткий ответ заключается в том, что с MRI (эталонной реализацией Ruby) Hash реализуется на C, а не в виде кода Ruby, и все методы из Enumerable имеют реализацию C в источнике, на который я ссылаюсь. Если вы проверите этот код на GitHub, вы также увидите строку rb_include_module(rb_cHash, rb_mEnumerable), теперь я не уверен, действительно ли это добавляет какую-либо реализацию или просто приводит к тому, что Enumerable перечисляется в Hash.ancestors, так что Hash можно рассматривать как класс, который включает Enumerable. Извините, это не более определенно! - person mikej; 18.11.2014
comment
Я сделал скрипт для анализа того, какие методы были переопределены или нет. Оказывается, у Hash много методов из Enumerable, но он переопределяет некоторые методы, содержащиеся в Enumerable. Скрипт такой: Enumerable.instance_methods.each { |method| puts "#{method} #{Hash.new.method(method.to_sym).owner}" } - person cenouro; 18.11.2014