0

I don't quite understand how this works. I guess a large part of it is because I'm used to C and its low-level data structures, and programming at a higher level of abstraction takes some getting used to. Anyway, I was reading The Ruby Programming Language, and I came to the section about ranges and how you can use the <=> operator as sort of a shorthand for what in C you would have to implement as a sequence of if-else statements. It returns either -1, 0, or 1 depending on the results of the comparison. I decided to try it out with the following statement:

range = 1..100
r = (100 <=> range)
print( r )

The result is an empty string. So my question is, how does this operator work; what data type does it return (I assume the return value is an integer type but I can't say for sure); and finally what is the proper way to use it? Thanks for your time, and sorry if this was already answered (I didn't see it in the listing).

Shoblade X
  • 363
  • 2
  • 9

3 Answers3

1

The <=> operator is meant to compare two values that can be compared directly, as in strings to strings or numbers to numbers. It can't magically compare two different things, Ruby doesn't convert for you like that.

As such you need to use it in the right context:

1 <=> 2
# => -1
2 <=> 1
# => 1
1 <=> 1
# => 0

When the two things can't be compared you get nil. Note that this is different from "empty string":

1 <=> '1'
# => nil

That means "invalid comparison". The <=> operator is being nice here because in other situations you get an exception:

1 < '1'
# => ArgumentError: comparison of Integer with String failed

You can also use this operator to make your own Comparable compatible class:

class Ordered
  include Comparable

  attr_reader :sequence

  def initialize(sequence)
    @sequence = sequence
  end

  def <=>(other)
    self.sequence <=> other.sequence
  end
end

Then you can do this:

a = Ordered.new(10)
b = Ordered.new(2)

[ a, b ].sort
# => [#<Ordered:0x00007ff1c6822b60 @sequence=2>, #<Ordered:0x00007ff1c6822b88 @sequence=10>]

Where those come out in order. The <=> implementation handles how these are sorted, and you can finesse that depending on how complex your sorting rules are.

tadman
  • 208,517
  • 23
  • 234
  • 262
0

Using the return values -1, 0, and 1 only as labels describing different states, you can write a condition that depends on the order between two numbers:

case a <=> b
when -1 then # a is smaller than b. Do something accordingly
when 0 then # a equals b. Do something accordingly
when 1 then # a is larger than b. Do something accordingly
end

Or, a use case where you can make use of the values -1, 0, and 1, is when you want to get the (non-negative) difference between two numbers a and b without using the abs method. The following:

(a - b) * (a <=> b)

will give the difference.

sawa
  • 165,429
  • 45
  • 277
  • 381
0

Add to the other answers this snippet: The "spaceship operator" returns -1, 0, or 1 so you can use it when comparing items in a .sort call:

events.sort {|x, y| y.event_datetime <=> x.event_datetime}

0 means the two items are the same, 1 means they are different but in the correct sort order, and -1 means they are out of order. The above example reverses x and y to sort into descending order.

In C, the function strcmp() has roughly the same behavior, to fit with qsort(), with the same semantics.

Phlip
  • 5,253
  • 5
  • 32
  • 48