1
tmp = [-3,3,5]
p "test: #{tmp.bsearch_index{|j| j == -3}}"

In the above code, I get response as nil. If I compare j against 3, or 5, it works. Why does bsearch_index does not consider very first element?

Mario Galic
  • 47,285
  • 6
  • 56
  • 98
TorukMakto
  • 2,066
  • 2
  • 24
  • 38

1 Answers1

3

You need to write

tmp.bsearch_index{|n| n >= -3}    #=> 0

This uses Array#bsearch_index's find minimum mode, which returns the smallest value in the array that satisfies the expression in the block. For example,

tmp.bsearch { |n| n >= 0 }        #=> 3
tmp.bsearch_index { |n| n >= 0 }  #=> 1

In this mode, to quote the doc for Array#bsearch, "the block must always return true or false, and there must be an index i (0 <= i <= ary.size) so that the block returns false for any element whose index is less than i, and the block returns true for any element whose index is greater than or equal to i. This method returns the i-th element. If i is equal to ary.size, it returns nil."

If the block were { |n| n == -3 } there would be no index i, 0 <= i <= tmp.size #=> 3 that has the property that tmp[j] == -3 is false for all j < i and true for all j >= 1.

If the block calculation were tmp[j] == 5 the requirement would be satisfied (for index 2) so the correct value would be returned. If the block calculation were tmp[j] == 3 the requirement would not be satisfied (tmp[2] == 3 #=> false); the fact that the correct index is returned is only due to the way the method has been implemented. If

tmp = [-3, 3, 5, 6]

then nil is returned for n == 3 as well as for n == -3:

tmp.bsearch_index { |n| n == 3 }  #=> nil

bsearch has a second mode, find any mode. (See the doc for Array#bsearch for details.) In this case we might write one of the following:

tmp.bsearch_index { |n| -3 - n }  #=> 0   
tmp.bsearch_index { |n|  3 - n }  #=> 1
tmp.bsearch_index { |n|  5 - n }  #=> 2
tmp.bsearch_index { |n|  4 - n }  #=> nil

This mode would be useful here if nil were to be returned when no element in the array evaluates to zero in the block. In other contexts it has a multitude of uses.

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
  • I kind of get it but don't get it. I understand the logic how it works after you quoted doc. I don't understand why would some one need to implement it that way? Binary search is a very common problem one could easily write a solution to accomodate == operator. What am I missing here? – TorukMakto Jul 30 '20 at 23:10
  • 1
    Suppose you were to try to guess a number `n` between 0 and 99 and could ask questions of someone who knew the value of `n` who could answer only "yes" or "no". You might first ask if `n < 50`. If the answer were "yes", you might then ask if `n < 25`; else you might ask if `n < 75`, and so on. That's a binary search. But if you asked if `n == 50` and were told "no", you wouldn't know if `n < 50` or `n >50`, so that answer to your question would be insufficient to perform a binary search. – Cary Swoveland Jul 31 '20 at 00:20
  • 2
    Or `tmp.bsearch_index { |n| -3 <=> n }` to find that specific element. – Stefan Jul 31 '20 at 07:26
  • @Stefan, good point. I added a mention but just used `-3 - n`. – Cary Swoveland Jul 31 '20 at 17:56