2014-11-16 2 views
0

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

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 

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

ответ

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? является implemented to call rb_hash_has_key.

+0

Так что, хотя хэши реализуют «интерфейс» Перечислимый, он фактически реализует методы, а не просто включает Enumerable? – cenouro

+0

Я не уверен в деталях этого. Короткий ответ заключается в том, что с помощью MRI (эталонная реализация Ruby) 'Hash' реализуется в C, а не как Ruby-код, и все методы из' Enumerable' имеют реализации C в источнике, с которым я связан. Если вы проверите этот код на GitHub, вы также увидите строку 'rb_include_module (rb_cHash, rb_mEnumerable)' теперь я не уверен, добавляет ли она какую-либо реализацию или просто вызывает перечисление «Enumerable» в 'Hash.ancestors', поэтому что 'Hash' можно рассматривать как класс, который включает' Enumerable'. Извините, это еще не окончательно! – mikej

+1

Я сделал сценарий для анализа того, какие методы были переопределены или нет. Оказывается, у Hash есть много методов из 'Enumerable', но он переопределяет некоторые методы, содержащиеся в' Enumerable'. Сценарий таков: 'Enumerable.instance_methods.each {| method | puts "# {method} # {Hash.new.method (method.to_sym) .owner}"} ' – cenouro