配列に2つの値があるときに、bsearchメソッドがnilを返す理由がわかりません。
arr = [1, 2]
arr.bsearch { |el| el < 2 }
ドキュメントを検索する
Array#bsearch のドキュメントを注意深く読む必要があります。 bsearch には 2 つの形式またはモードがあると述べられています。「最小値検索モードと任意の検索モード。どちらの場合も、配列の要素はブロックに関して単調 (またはソート) でなければなりません。」 find-minimum モードでは、ブロックは true または false を返します。 find-any モードでは、配列の要素と同じクラスの値がブロックによって返されます。したがって、find-minimum モードを使用していることになります。
また、ドキュメントから、find-minimum モードでは、次のようにインデックス i, 0 <= i <= ary.size が存在する必要があります。
このメソッドは i 番目の要素を返します。 i が ary.size に等しい場合、nil を返します。」
コードを調べる
次に、少し変更したステートメントを調べてみましょう。
arr = [1, 2, 3]
arr.bsearch { |el| el < 2 } #=> nil
bsearch の使用によって課せられる要件を満たすインデックス 0、1、または 2 を見つけたいと考えています。
対象のインデックスが 0 であるとします。ゼロより小さいインデックスはないため、arr[1] < 2 と arr[2] < 2 (2 < 2 および 3 < 2) が両方とも真かどうかをテストします。実際にはどちらも誤りです。したがって、インデックス 0 は考慮から除外されます。
ここで、ドキュメントで参照されているインデックスが 1 だったとします。arr[0] < 2 が false であることが必要ですが、これは true です。したがって、インデックス 1 を除外できます。
インデックスが 2 に等しくなるには、arr[0] < 2 と arr[1] < 2 の両方が false である必要がありますが、実際には両方とも true です。
besearchを使用するための要件を満たすインデックスが存在しないため、nilが返されます。
コードを修正する
arr = [1, 2, 3] の場合、式は次のように記述する必要があることは明らかです。
arr.bsearch { |el| el >= 2 } #=> 2
arr.bsearch { |el| el > 2 } #=> 3
arr.bsearch { |el| el > 0 } #=> 1
arr.bsearch { |el| el > 3 } #=> nil
その他の例
配列に整数が含まれる例をさらにいくつか示します。
ary = [2, 4, 6]
ary.bsearch { |el| el > -99 } #=> 2
ary.bsearch { |el| el > 1 } #=> 2
ary.bsearch { |el| el > 2 } #=> 4
ary.bsearch { |el| el >= 2 } #=> 2
ary.bsearch { |el| el >= 6 } #=> 6
ary.bsearch { |el| el > 6 } #=> nil
配列は増加しないこともできます
配列は非減少である必要はなく、単調である必要があることに注意してください。これは、非減少または非増加を意味します。以下は、配列が増加していない (実際には厳密に減少している) 例です。もちろん、ブロック計算もそれに応じて調整する必要があります。
ary = [6, 4, 2]
ary.bsearch { |el| el <= 7 } #=> 6
ary.bsearch { |el| el <= 99} #=> 6
ary.bsearch { |el| el < 5 } #=> 4
ary.bsearch { |el| el <= 4 } #=> 4
ary.bsearch { |el| el < 4 } #=> 2
ary.bsearch { |el| el <= 2 } #=> 2
ary.bsearch { |el| el < 2 } #=> nil
ary.bsearch { |el| el <= 1 } #=> nil
配列には数値以外の値が含まれる場合があります
唯一の要件は、要素のペアが宇宙船演算子 <=> と比較できることです。 a <=> b は、a < b の場合は -1 を返し、a == b の場合は 0 を返し、a > b の場合は 1 を返します。
たとえば、文字列の場合、bsearch では String#<=> が使用されます。ここではいくつかの例を示します。
ary = ['brush', 'keep', 'task']
ary.bsearch { |s| s >= 'hi' } #=> "keep"
ary.bsearch { |s| s >= 'align' } #=> "brush"
ary.bsearch { |s| s >= 'mama' } #=> "task"
ary.bsearch { |s| s >= 'tar' } #=> "task"
ary.bsearch { |s| s >= 'tax' } #=> "nil"