8

When I call Array#- it doesn't seems to call any comparison method on the strings I'm comparing:

class String
  def <=>(v)
    puts "#{self} <=> #{v}"
    super(v)
  end

  def ==(v)
    puts "#{self} == #{v}"
    super(v)
  end

  def =~(v)
    puts "#{self} =~ #{v}"
    super(v)
  end

  def ===(v)
    puts "#{self} == #{v}"
    super(v)
  end

  def eql?(v)
    puts "#{self}.eql? #{v}"
    super(v)
  end

  def equal?(v)
    puts "#{self}.equal? #{v}"
    super(v)
  end

  def hash()
    puts "#{self}.hash"
    super
  end
end

p %w{one two three} - %w{two}

It just returns:

["one", "three"]

So, what is Array#- doing?

Also, I'm using Ruby 1.9.2p290. In 1.8.7 it seems to cause an infinite loop.

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
Zequez
  • 3,399
  • 2
  • 31
  • 42
  • Try invoking it with `irb` (!). After reading your class definition it goes into an infinite loop. – Josh Lee Sep 19 '11 at 21:43
  • 1
    @JoshLee What do you mean it enters an infinite loop? When I execute the script it exits without any problem – Zequez Sep 19 '11 at 21:49
  • It prints out N == N N == m m == m N == n m == n M == n l == n over and over… – Josh Lee Sep 19 '11 at 21:51
  • 1
    Interesting question. `['one', 'two', 'three'].delete('two')` uses `==`. You forgot `equal?`, but it's not the searched one. – knut Sep 19 '11 at 21:53
  • @JoshLee not in my computer at least, I'm using ruby 1.9.2p290 – Zequez Sep 19 '11 at 21:54
  • @knut Oh it wasn't listed in http://ruby-doc.org/core/classes/String.html is it an alias of .eql? ? – Zequez Sep 19 '11 at 21:57
  • It`s defined in BasicObject (and BasicObject is a ancestor of String). So it is inherited. More details in http://stackoverflow.com/questions/7156955/whats-the-difference-between-equal-eql-and – knut Sep 19 '11 at 22:04
  • See also http://stackoverflow.com/questions/2529873/how-does-rubys-array-compare-elements-for-equality - But you already tested String#hash. – knut Sep 19 '11 at 22:42
  • String#hash has no parameter. I edited your question - please wait for review. – knut Sep 19 '11 at 22:43
  • You'd use `Array.foo` to indicate a method called on the `Array` class itself, whereas you'd use `Array#foo` to indicate a method called on an instance of `Array`. – Andrew Grimm Sep 19 '11 at 23:42
  • @AndrewGrimm You are right, I forgot, sorry, thanks for correcting it ñ.ñ – Zequez Sep 20 '11 at 01:51

1 Answers1

6

source code for Array#-.

It appears that rather than testing for equality, a hash is made from the second array. Anything not contained in that array is pushed into the resultant array.

Array difference in 1.8.7 is implemented this way also. The changes to String only cause problems in irb (not in a plain ruby script).

hsam
  • 5
  • 2
cam
  • 14,192
  • 1
  • 44
  • 29
  • @lucapette: ah ok, probably any repl will have issues. – cam Sep 19 '11 at 22:34
  • But if it's not testing for equality how does it knows if something is contained in the array? I mean, it have to test for equality at some point right? :| – Zequez Sep 19 '11 at 23:26
  • @Zequez: no, it just needs to hash the string (see `"foo".hash`). The hash lookup is how it determines if an item is contained in the array. – cam Sep 19 '11 at 23:49
  • @cam Ohhhhhh I got it now, so, no case insensitive array subtraction this way then haha. Thanks a lot for clearing it out! ñ.ñ Though it's still weird that it does not call the hash method in the string =/ – Zequez Sep 20 '11 at 01:54
  • So if it's hashing the string, why isn't `hash` getting called on `String`? – Joshua Grosso Reinstate CMs Jan 29 '16 at 00:24