Ruby | Range#include? ( member? / === ) と Range#cover? の違いを Ruby のパパの発言から理解する #ruby

# Ruby | Range#include? ( member? / === ) と Range#cover? の違いを Ruby のパパの発言から理解する #ruby
## 概要
Range#include? ( member / === ) と Range#cover? の違い について
Ruby のパパの発言から理解します。

## 連続か離散か
[Ruby メーリングリスト Rangeクラスに関する質問に対する Matz さんの回答 20 Apr 2014](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/49797)

~~~
Rangeの奇妙な振る舞いを理解するためには、Rangeのメソッドには、
Enumerableとして振る舞うものと、Intervalとして振る舞うものの
の両方があることに注意しなければなりません。

Enumerableとして振る舞うものは離散的で、eachメソッドが生成す
る要素を使います。Rangeの場合、eachは始点から1.0ずつ離れた値
が生成されます(数値の場合)。

Intervalとして振る舞うものは連続的で始点(begin)と終点(end)の
大きさで決まります。

たとえば、include?はeachを使い、cover?は始点と終点の間の比較
で判定します。
~~~

## 数値は例外
[Ruby メーリングリスト Rangeクラスに関する質問に対する Matz さんの回答 21 Apr 2014](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/49799)

数値は例外的に include? も連続値を扱う

~~~
内部的には数値、
特に整数と浮動小数点数は特別扱いしており、その辺に不徹底があ
るようです。たとえば、ご指摘のRationalとFloatの挙動の違いはそ
れが原因です。これはRational対応を行うべきですね。
~~~

## 名前決定の瞬間。名前重要
[Matzにっき 2005-12-10 名前重要](http://www.rubyist.net/~matz/20051210.html)

* Matz さん

~~~
先日来、ruby-talkでRangeについていろいろと議論されていたことを解消できるめどが立った。
しかし、その完全解決にはひとつ、もしくはふたつメソッドを追加する必要があるのだが、 良い名前が思いつかないのだ。困った。

ひとつめは大小比較で包含関係を判定するRangeクラスのメソッド。 1.8でいうとinclude?に相当するのだが、1.9ではこれは基本的に、 要素として含むかどうかという離散的な包含関係を意味することにした。 ただし、include?とmember?は数だけは例外として大小関係で判定する。

名前の候補としては「encompass?」と「span?」があがっているが、 いまいちピンとこない。便利なメソッドなのでできるだけ短い名前にしたいのだが、

beg <= x <= end
という関係が明確に分かるとありがたい。
~~~

* takano32 さん

~~~
最初のものはcover?というのが琴線に響いたので採用しちゃいました。変えるかもしれないけど。
引き続きご意見募集中です。
~~~

* Matz さん

~~~
最初のものはcover?というのが琴線に響いたので採用しちゃいました。変えるかもしれないけど。
引き続きご意見募集中です。
~~~

## サンプルコード
数値に関しては例外的に include も連続値を扱っている。

~~~
require 'tbpgr_utils'
require 'date'
require 'time'

def call_method_with_begin_rescue
  yield
rescue => e
  e.message
end

bulk_puts_eval binding, <<-EOS
(1.1..2.3).include?(1.1)
(1.1..2.3).include?(1.555)
(1.1..2.3).cover?(1.1)
(1.1..2.3).cover?(1.555)
('b'..'d').include?('d')
('b'..'d').include?('ba')
('b'..'d').cover?('d')
('b'..'d').cover?('ba')
(Date.new(2014,1,3)..Date.new(2014,1,5)).include?(Date.new(2014,1,5))
call_method_with_begin_rescue { (Time.new(2014,1,3)..Time.new(2014,1,5)).include?(Time.new(2014,1,4,10,10,10)) }
(Date.new(2014,1,3)..Date.new(2014,1,5)).cover?(Date.new(2014,1,5))
(Time.new(2014,1,3)..Time.new(2014,1,5)).cover?(Time.new(2014,1,4,10,10,10))
EOS
~~~

## 出力

~~~bash
(1.1..2.3).include?(1.1)                                                        # => true
(1.1..2.3).include?(1.555)                                                      # => true
(1.1..2.3).cover?(1.1)                                                          # => true
(1.1..2.3).cover?(1.555)                                                        # => true
('b'..'d').include?('d')                                                        # => true
('b'..'d').include?('ba')                                                       # => false
('b'..'d').cover?('d')                                                          # => true
('b'..'d').cover?('ba')                                                         # => true
(Date.new(2014,1,3)..Date.new(2014,1,5)).include?(Date.new(2014,1,5))           # => true
call_method_with_begin_rescue { (Time.new(2014,1,3)..Time.new(2014,1,5)).include?(Time.new(2014,1,4,10,10,10)) }
# => "can't iterate from Time"
(Date.new(2014,1,3)..Date.new(2014,1,5)).cover?(Date.new(2014,1,5))             # => true
(Time.new(2014,1,3)..Time.new(2014,1,5)).cover?(Time.new(2014,1,4,10,10,10))    # => true
~~~

## 独り言
ドキュメント化されていない、メソッドの細かな仕様・用途を調べるとき
結果的に Ruby のメーリングリストにたどりつくことが大い。
そしてメーリングリストのやりとりを読むと、必要になった経緯も含めて理解できる。

## るりまの Range#cover? にプルリク
るりまに記載があれば、 Ruby コミッターさんの貴重な時間（ 特に Matz さんの ）を節約できるかな、
と思うので プルリクを送りました。

https://github.com/rurema/doctree/pull/140

仮に質問があっても、 「るりまのここみてください」 で回答できると早いですよね。