UFO演算子をメソッドとして定義する

### 概要
最近、Rubyについてもっと理解を深めたくて、Ruby技術者認定試験合格教本を読んでいます。
以下のようなUFO演算子(<=>)をメソッドとして定義している部分が分からず、まとめてみました。
（宇宙船演算子ともいうらしいですが、UFO演算子と呼ばせていただきます）

```ruby
  def <=> other
    return self.id <=> other.id
  end
```

### ざっくり結論
sortメソッド等に利用する為に、<=> を~~再~~定義している記述だった。

↑ @scivola さんより以下ご指摘いただき、「再」を削除しました。
> Object には <=> は定義されていないので，Employee クラスで 再定義 しているわけではないんですよね。

ですので、単純に「定義」している記述という表現が正しいです！


どうしてこの結論に至ったか、以下経緯をまとめました。

### 経緯
下記のような記述が登場。
パッとみて、上記のメソッド部分が全然分からなかったので調べました。

```ruby

class Employee
  attr_reader :id
  attr_accessor :name

  def initialize id, name
    @id = id
    @name = name
  end

  def to_s
    return "#{@id}: #{@name}"
  end

  def <=> other
    return self.id <=> other.id
  end

end

employees = []
employees << Employee.new("3","Tanaka")
employees << Employee.new("1","Suzuki")
employees << Employee.new("2","Sato")

employees.sort!
employees.each do |employee| puts employee end
```

実行結果

```
1: Suzuki
2: Sato
3: Tanaka
```

### 分からなかったこと
employees.sort! の所で呼ばれてるメソッドっぽいけど、
どうやって呼ばれているの？ 役割は？

### 調べてみたこと

#### <=> ってなんだっけ？
> UFO演算子と呼ばれる比較演算子で、これは比較の結果を数値で返します。左辺の値が右辺の値より大きければ、1、等しければ0、左辺の値が小さければ-1を返します。ソート処理などで順番の決定などに使用します。

```
100 <=> 10 #=> 1
100 <=> 100 #=> 0
10 <=> 100 #=> -1
```

> Ruby技術者認定試験合格教本より

ふむふむ。

#### sortメソッドについても改めて調べてみる
> sortメソッドは、配列の要素をソートした新しい配列を返します。要素の順序の比較には<=>演算子が使われ、「要素1 <=> 要素2」の結果が-1なら要素1が先、0なら同じ、1なら要素2が先となります。
https://ref.xaio.jp/ruby/classes/array/sort

ほうほう。sort って、<=> を使っているか！
じゃあ、そのタイミングで呼ばれているっぽいな・・・

#### 挙動確認してみた

ダメ元で、UFO演算子メソッド部分をコメントアウトして、`employees.sort!`してみる。

```ruby
    23: employees = []
    24: employees << Employee.new("3","Tanaka")
    25: employees << Employee.new("1","Suzuki")
    26: employees << Employee.new("2","Sato")
    27: binding.pry
 => 28: employees.sort!
    29: employees.each do |employee| puts employee end

[1] pry(main)> employees
=> [#<Employee:0x00007ff24eb8d9c0 @id="3", @name="Tanaka">,
 #<Employee:0x00007ff24eb8d948 @id="1", @name="Suzuki">,
 #<Employee:0x00007ff24eb8d8d0 @id="2", @name="Sato">]
[2] pry(main)> employees.sort!
ArgumentError: comparison of Employee with Employee failed
from (pry):3:in `sort!'
```
やっぱり出来ない！
比較する要素が複数あるからかな。
コメントアウトを外して、実行してみると上手くいく↓

```ruby
    22: employees = []
    23: employees << Employee.new("3","Tanaka")
    24: employees << Employee.new("1","Suzuki")
    25: employees << Employee.new("2","Sato")
    26: binding.pry
 => 27: employees.sort!
    28: employees.each do |employee| puts employee end

[1] pry(main)> employees.sort!
=> [#<Employee:0x00007fdb013d13d0 @id="1", @name="Suzuki">,
 #<Employee:0x00007fdb013d1358 @id="2", @name="Sato">,
 #<Employee:0x00007fdb013d1448 @id="3", @name="Tanaka">]
```

次に、UFO演算子のメソッド部分で`binding.pry`して、
`self.id`と `other.id` の値をチェック、どんな値が返されるかを考えてみる。

```ruby

    16: def <=> other
    17:   binding.pry
 => 18:   return self.id <=> other.id
    19: end

[1] pry(#<Employee>)> self.id
=> "3"
[2] pry(#<Employee>)> other.id
=> "1"
```
3 <=> 1 だから、1が返る。
結果、 1 => 3 => 2 の順番になるかな。

exit してみると止まるので、次の`self.id`と `other.id` をチェック。

```ruby
    16: def <=> other
    17:   binding.pry
 => 18:   return self.id <=> other.id
    19: end

[1] pry(#<Employee>)> self.id
=> "3"
[2] pry(#<Employee>)> other.id
=> "2"
```
3 <=> 2 だから、1が返る。
結果、 1 => 2 => 3 の順番かな。

exit して、`self.id`と `other.id` をチェック。

```ruby
    16: def <=> other
    17:   binding.pry
 => 18:   return self.id <=> other.id
    19: end

[1] pry(#<Employee>)> self.id
=> "1"
[2] pry(#<Employee>)> other.id
=> "2"
```
1 <=> 2 だから、-1が返る。
結果変わらず、 1 => 2 => 3 の順番かな。

ここでexitしたら、最後まで実行された！
id 同士だけを比較しているので、エラーにならなかった！！

#### まとめ
やはり、sort! のタイミングで、UFO演算子のメソッドが呼ばれていた。

UFO演算子のメソッド内で、<=> の定義を、id同士を比較するものに~~再~~定義していた為、
エラーにならずsortメソッドが最後まで実行された！

sortメソッドは、1が返ると右辺と左辺の順序が入れ替わり、
-1が返ると順序が入れ替わらないので、1=>2=>3 の順に並び替わる。

### 最後に
分かってしまえばそんなに難しくないですが、最初に`def <=> other`を見た時は、正直混乱しました。
「演算子を再定義することが出来る」という概念がなかったので、勉強になりました！
sort の挙動についてもよく分かってよかった。

Qiita 初投稿です。見辛い&レベル低いかもですが、お許しください・・
「違うよ！」「ここの説明の方が分かりやすいよ！」などのアドバイスありましたら、
コメントいただけると喜びます。

### 補足
@scivola さんより、 <=> を定義せず idの値を sort_by を使って比較する方法も教えていただきました！
コメント欄にて詳しく解説してくださいましたので、チェックしてみてください！
@scivola さん、ありがとうございます！！

### 参考にさせていただいたサイト様
https://www.uosansatox.biz/entry/2018/01/25/112048
https://codeday.me/jp/qa/20190301/344992.html
https://www.buildinsider.net/language/rubytips/0018

最後まで読んでくださり、ありがとうございました！