Range#cover?がRangeを受け取る際のドキュメントについて
code:text
pocke:thinking_ruby: 1:33 AM
Range#cover? のドキュメントについて質問させてください。
RDocの次の分の意味がちゃんと理解できずにいます。
In the specific case of (a..b).cover?(c...d) with a <= c && b < d, the end of the sequence must be calculated, which may exhibit poor performance if c is non-numeric.
「(a..b).cover?(c...d)かつ a <= c && b < d の時、 c がnumericでないならば終端が計算されていないといけない。じゃないとパフォーマンスが悪くなる」と言う意味だと推測しているんですが、この「終端が計算されていないといけない(the end of the sequence must be calculated)」の意味をはかりかねています。
「be calculated」の意味がよく分かっていません。なんとなく succで要素を列挙していくようなケースでパフォーマンスが悪化するのだろうなと予測していますが、それを表しているんでしょうか? (edited)
pocke:thinking_ruby: 1:41 AM
「(a..b).cover?(c...d)」は「a <= c && b < d」を意味します。 cが数値ではなく終端が計算でもとまらない時、パフォーマンスの問題が起きる可能性があります。
るりまのドキュメントが誤訳っぽいので気になったのでした。
docs.ruby-lang.orgdocs.ruby-lang.org
Range#=== (Ruby 2.7.0 リファレンスマニュアル)
obj が範囲内に含まれている時に真を返します。
mrkn 7:19 AM
訳は正しいと思います。
1 reply
6 days agoView thread
mrkn 7:21 AM
be calculated は「計算で求まる」という意味でしょうね。
7:21
succ で列挙していかないと終端を得られないような場合に遅くなるっていう解釈はあってます。
scivola 8:14 AM
範囲式は条件式として使われる場合を除き,必ず両端を評価しますよね。
「与えられた範囲オブジェクトの終端が計算されていない」というのはどういう状況なのでしょうか。
「the end of the range」ではなく「the end of the sequence」と表現されているところがちょっと引っかかります。引数の終端のことではない,という可能性はないでしょうか。
引数の終端なら d と書けば済むはず(始端は c で表している)。
mame 9:05 AM
よくわかんないけど
「(a..b).cover?(c...d)」は「a <= c && b < d」を意味します。
は完全に間違いだし、その英文の訳だとしたら完全に間違ってると思う
9:05
a <= c && d <= b だよね
9:08
(a..b).cover?(c...d) は a <= c && b < d のとき、列の終端を実際に計算(イテレート)せねばならず、この(イテレートの)処理は c が numeric でないときに遅いことがある
(edited)
:naruhodo:
1
9:08
英文の意味としてはこんなかんじかと
9:12
the end of the sequence はなんか雑な表現ですが、たとえば (1...5) の the end of the range は 5 、the end of the sequence は 4 ということをいいたいんだと思われます
:naruhodo:
1
9:13
Ruby の用語として聞いたことないけど、このドキュメントはパッチが送られてきたのを適当に取り込んだだけっぽいので、適当っぽい
9:14
本当かな
9:16
カッコの補足が正確でないな
mrkn 9:16 AM
「Ruby の用語」の正式な定義集ってあるのかな
9:16
JIS か
mame 9:17 AM
(a..b).cover?(c...d) は a <= c && b < d のとき、列の終端を実際に計算(イテレートや、普通の計算)せねばならず、この処理は c が numeric でないとき(イテレートが必要になるとき)に遅いことがある
9:17
というふうに英語は読めるけれど、このドキュメント自体が正しいのかはわからないな
mrkn 9:17 AM
式で書くと range.last のことですよね。
mame 9:18 AM
.last と .to_a.last は別ですぞ
9:18
なんかそのへんやたらむずかしい
mrkn 9:18 AM
そうだったけ
mame 9:18 AM
irb(main):001:0> (1..5.5).last
=> 5.5
irb(main):002:0> (1..5.5).to_a.last
=> 5
mrkn 9:18 AM
どうしてこうなってるんだろう
9:19
end と同じなのかー
mame 9:32 AM
$ ruby -e 'p ("aaaaa".."zzzzy").cover?("aaaaa"..."zzzzz")'
true
9:32
これが遅いケースか
9:32
これ true であるべきなのかよくわかんないな
9:33
$ ruby -e 'p (1..5).cover?(1..5.5)'
false
pocke:thinking_ruby: 11:07 AM
なるほど、ありがとうございます。 「計算」しなければならない時というのは、 c が numericでないとき、と同義なのでしょうか?
mrkn 11:13 AM
同義ですね。
:pray:
1
11:16
Range#cover? 内で Range#max が呼ばれていて、Range#max は Numeric については列挙を回避しています。
mame 11:41 AM
狭義の「計算」と広義の「計算」がありますがどっちでしょうか
11:42
ぼくは max を呼ぶ行為を「計算」と呼ぶと思っていた(イテレートするかどうかは関係なく)(これはたぶん広義) (edited)
11:43
たぶん mrkn さんは max の中でイテレートすることを「計算」と呼んでいそう
mrkn 11:44 AM
max を求めることが計算なんじゃないですかね
11:44
その際に、numeric じゃなかったら時間かかる
mame 11:44 AM
そうそう
11:45
なので
「計算」しなければならない時というのは、 c が numericでないとき、と同義なのでしょうか?
は同義ではないのでは
11:45
numeric だろうとなんだろうと max は呼ぶ
11:46
こういう内部実装をドキュメントに書くべきではない、という感想しかないな
mrkn 11:46 AM
いま実装を見ているのだけど、右開区間の場合に上界 (end) が integer じゃなかったら max は計算できないってことになってるな。
11:47
ドキュメントに書かないとなると cover? の計算量が知りたい人は実装を読まなければならなくなりますね。
mame 11:50 AM
cover? って比較演算で高速に判定したいために入ったんじゃなかったっけ
mrkn 11:50 AM
追加された経緯はそうだったと思う
mame 11:51 AM
その目的を考えると、イテレートが発生しうるのがそもそもマズい気がするんだよな
mrkn 11:52 AM
Range の境界値の型によって遅くなる場合があることは大事な情報だと思う
pocke:thinking_ruby: 11:52 AM
cover? は <=> を使って include? は succ を使う、という印象があったので、 cover? にRangeを渡したときは succ を使う可能性があるというのには少し違和感を覚えました
:kannzenndoui:
1
mame 11:53 AM
そういうことです
11:53
("aaaaa".."zzzzy").cover?("aaaaa"..."zzzzz") が true を返すのは本当に必要なのか
11:54
どうしてもやりたいなら "zzzzy".succ して "zzzzz" になるか見るだけで十分のような気も
11:54
というのをシュッと変えてやろうと思ったときに、ドキュメントがあると仕様変更になってしまうのです
11:54
書かなければ変えられる
11:54
ということでドキュメントはなければないほどよい
:naruhodo:
2
:goodpoem:
1
:sorena_r:
1
11:54
いらんことを保証しない
mrkn 12:00 PM
("aaaaa".."zzzzy").cover?("aaaaa"..."zzzzz") が true を返すのは本当に必要なのか
12:01
これは必要ないかもしれないけど、範囲の境界値の型をチェックしておかないと、そこでしばらく停止してしまうのは危険なので情報を提示するのは良いと思う。
12:01
ドキュメントに書かないなら、文字列の範囲で cover? は動かないようにしたほうが良い。
mame 12:02 PM
そのケースはイテレートせずに false を返すのでいいような気もするんだよなあ
mrkn 12:03 PM
いいのかな。良いかどうかは良くわからない。
12:03
nil でも良いかもしれない。
12:03
未定義
mame 12:04 PM
irb(main):001:0> (1..4).cover?(1...5)
=> true
irb(main):002:0> (1..4).cover?(1...4.5)
=> false
12:04
難しすぎる
mrkn 12:04 PM
後者は良くないよね
12:04
上で言及したけど
12:04
なぜそうなってるんだろう
mame 12:05 PM
Float の Range は列ではなく数直線上の範囲って気持ちなんじゃないかな
mrkn 12:06 PM
まぁ、max が計算できないのは正しいが
12:07
あ、いいのか。
12:07
混乱したけど (1..4).cover?(1...4.5) は正しいですね。そう、数直線ってことなんだ。
mame 12:08 PM
どっちも false でいいんじゃないのかなあ
mrkn 12:09 PM
なるほど
12:09
たしかに整数の場合も数直線上の区間で比較する方が統一感はありますね。
mame 12:09 PM
irb(main):001:0> (1..4).cover?(1...5)
=> true
irb(main):002:0> (1..4).cover?(1...5.0)
=> false
12:10
こっちのほうが印象的だな
mrkn 12:10 PM
整数範囲で true になるから混乱したんだ
tompng 12:10 PM
(1..4.5).cover?(1...5) #=> true (1...5).cover?(1..4.5) #=> true 12:11
ちょっと:warudakumi:でRangeじゃんけん
グー = '0'..'gu'
チョキ = '0'..'choki'
パー = '0'...'pa'
mame 12:11 PM
1...5 も 1...4.5 も 1, 2, 3, 4 だと考えるので同じということなんだろうけど、むずかしい mrkn 12:11 PM
1...5 が 1..4 と同じ意味になっちゃってるんだよな。
mame 12:11 PM
p グー.cover? パー も true なので偽
:tasikani:
1
scivola 12:17 PM
イテレーションのドメインと区間とを一つのクラスにまとめちゃったのが混乱の元ということはないでしょうか。
mame 12:17 PM
はい
12:18
否定疑問文に「はい」と答えるとわかりにくいな、そうだと思います
12:18
おっしゃるとおりだとおもいます
scivola 12:24 PM
あ,なるほど,こういうチャットツールだと否定疑問文で訊かないほうがいいですね
:tiken:
3
12:24
Range の設計者に失礼な気がして,控えめに書こうとしたら否定疑問に
mrkn 12:26 PM
Ruby は大クラス主義なので、一つのクラスに様々な機能を担当させる傾向があります。
mame 12:34 PM
混乱の元だけど、じゃあ分けるべきだったかっていうと分けないような気はするのでむずかしい (edited)
https://gyazo.com/990df3707171c2dc4034fca5000ace04
https://gyazo.com/66ab2dd3481dbd8d84e643bdd09f486f
https://gyazo.com/2293f8db45bbb652926f5bd25a3669d8