Ruby's 'is_a?' está devolviendo falso para los tipos integrados, ¿qué está pasando?

I've only been learning the deep parts of Ruby for a few months so apologies if this is a bit of a dumb question. I'm attempting to recursively iterate through an Array that puede contain nested Arrays and as such I need to check the type of the current element. I have the following code as a small test:

arr = [ 1..2, [3..4, [5..6]], [7..8, [9..10]] ]

arr.each do |node|
  p node.class
  p node.instance_of? Array
end

Cuando lo ejecuto, obtengo el siguiente resultado:

Range
false
Array
false
Array
false

I expected the last two to return True, given I have an Array containing a Range and two nested Arrays.

What's even weirder, is if I write the following:

node.class.name == "Array"

It returns True, as it should.

¿Que esta pasando aqui?

Versión Ruby: RM 1.9.3-p194

Nota: I eventually realised that this was occurring due to the way I namespace my code using modules to avoid code-collision, like so, but also verify object identity in a naughty way:

module MyProg
  class MyClass
    attr_reader :my_array

    def initialize(size)
      @my_array = Array.new(size)
    end
  end
end

MyProg::MyClass.new

Doing this isolates your code but has the downfall of causing todos las class lookups to be resolved starting from under your namespace. This means that in the above example, my_array.class would actually resolve to MyProg::Array instead of the global Array class.

If you namespace like this and you still want to use this method, you can remedy it by using the double-colon global identifier before the class to force Ruby to begin lookup from the global namespace:

arr.is_a? ::Array
arr.is_a? ::String

Given Ruby's Duck Typing abilities however (and for better code maintenance later on), you should really be testing the comportamiento of the object as-per Peter's suggestion below. As such I'm marking his answer as correct for some excellent help given to a learner!

preguntado el 03 de julio de 12 a las 23:07

Same Ruby version returns true for me for the last two cases. -

I cannot replicate your results. Using MRI 1.9.3-p194 on Ubuntu, instance_of? devuelve True. -

I couldn't determine exactly what was causing this. After rewriting the function within the unit test, it returned the right results. My theory is that somehow, MiniTest is altering the method in some way. I've resorted to checking behaviour as-per @Peter's suggestion which passes the test and gives the correct result so I'll update my question. -

yo obtengo trues on MRI 1.9.3p125, too. -

3 Respuestas

I wrote another answer, but one major question is - why are you doing this? Why not, instead, just call flatten on the array so you just get the entries? Or, check for the comportamiento of the objects instead. You might need to give more detail about what you require.

Respondido 04 Jul 12, 00:07

I need to retain the structure of the Array as I'm converting it to a DAG from an S-expression (which is just a multi-dimensional array). If I use flatten I'll lose the structure and won't be able to determine if an Array is a child of another. Interestingly, I tried your solution of checking __id__ and the object id was the same for both nodes. I'm guessing because they were nested within the parent Array (which has the same id)? - xiii

El id depends on the instance of the Array class that is available when the objects are created. Still doesn't make a lot of sense that is_a? isn't working properly. But I think you can hopefully provide a solution that only cares about the behavior of the objects. - Peter

Realmente quieres decir is_a?, which is a more general test to see if the node es de tipo Array, rather than a specific instance of the specific Array class that you mention. See aquí for more details. But if you just use is_a? everything will make sense.

contestado el 23 de mayo de 17 a las 11:05

I should mention the same output occurs if I use is_a?. yo suelo instance_of? as I'm not checking inheritance, I only want to test for a specific class - Array in this case. - xiii

I do not understand how you can get the output you report with is_a?. Are you running this in a single Ruby script with no other patches or modifications happening? Doesn't make sense. - Peter

Running the code (both with instance_of? y is_a?) in IRB or MiniTest returns the wrong output. Running it as a standalone script returns the right output. I haven't made any modifications and I don't think MiniTest does? - xiii

The problem will be that the instance of Array will be different - if you compare Array.__id__ y [1,2].class.__id__ you will probably find them different. I'll submit an alternative answer. - Peter

I ran your code and got these results.

Range
false
Array
true
Array
true

I'm running ruby 1.9.3p125

Respondido 03 Jul 12, 23:07

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.