- 对于第三种情况,为什么找不到
20
?
- 对于第 4 种情况,为什么它也找不到
20
?(第一个小于 50)。
- 对于第 5 种情况,为什么它找到
10
而不是20
?
您的示例 3、4 和 5 违反了该方法的先决条件,因此该方法可以执行任何它想要的操作,包括 return nil
、 return10
或格式化您的硬盘。(尽管最后一个不太可能。)
该文件指出:
该块必须返回真或假,并且必须有一个索引 i (0 <= i <= ary.size) 以便:
- 对于索引小于 i 的任何元素,该块返回 false,并且
- 对于索引大于或等于 i 的任何元素,该块返回 true。
您的块违反了该条件,因此该方法不可能返回有意义的结果。
Array#bsearch
让我们实际逐步执行第三种情况的示例运行:
[10, 20, 50, 80, 110].bsearch{|a| a == 20 }
在第一次迭代中,算法将选择数组的中间 ( 50
) 并针对您的块进行测试:
50 == 20 #=> false
值false
表示我们正在搜索的元素位于当前元素的右侧。因此,我们要搜索的元素必须在子数组中[80, 110]
。所以,我们递归:
[80, 110].bsearch{|a| a == 20 }
同样,我们选择“中间”元素 ( 110
) 并针对您的块进行测试:
110 == 20 #=> false
由于块的返回值为false
,我们知道该元素必须在当前元素的右侧,但右侧不再有元素,因此我们知道我们正在搜索的元素不存在。
现在为您的第四个案例:
[10, 20, 50, 80, 110].bsearch{|a| a < 50 }
在第一次迭代中,算法将选择数组的中间 ( 50
) 并针对您的块进行测试:
50 < 50 #=> false
值false
表示我们正在搜索的元素位于当前元素的右侧。因此,我们要搜索的元素必须在子数组中[80, 110]
。所以,我们递归:
[80, 110].bsearch{|a| a < 50 }
同样,我们选择“中间”元素 ( 110
) 并针对您的块进行测试:
110 < 50 #=> false
由于块的返回值为false
,我们知道该元素必须在当前元素的右侧,但右侧不再有元素,因此我们知道我们正在搜索的元素不存在。
第五种情况:
[10, 20, 50, 80, 110].bsearch{|a| a <= 50 }
在第一次迭代中,算法将选择数组的中间 ( 50
) 并针对您的块进行测试:
50 <= 50 #=> true
值true
表示我们正在搜索的元素位于当前元素的左侧。因此,我们要搜索的元素必须在子数组中[10, 20]
。所以,我们递归:
[10, 20].bsearch{|a| a <= 50 }
同样,我们选择“中间”元素 ( 20
) 并针对您的块进行测试:
20 <= 50 #=> true
所以,元素必须仍然在左子数组中:
[10].bsearch{|a| a <= 50 }
将测试
10 <= 50 #=> true
由于块的返回值为true
,我们知道该元素必须在当前元素的左侧或该元素的左侧,但右侧不再有元素,因此我们知道我们正在搜索的元素,是这个元素。
注意:我假设总是Array#bsearch
会选择一个尽可能靠近中间的元素,并且我假设对于偶数个元素,它总是会选择中间的右边。但是你知道他们对假设的看法:它让你和我变得很糟糕。事实上,文档明确指出:
未定义在每次迭代中实际获取的值。
因此,取决于在每次迭代中实际获取的确切元素,结果实际上可能会有所不同。这也不足为奇,因为该块违反了方法的先决条件,因此无法判断会发生什么。
比如什么是1 - x / 4
什么,它在做什么?
这只是您的搜索标准。它是0
为x == 4
,积极的x < 4
和消极的x > 4
。这正是find-any模式所需要的:positive 告诉算法它必须向左看,negative 告诉算法向右看,零表示“你找到了范围”:
该块必须始终返回一个数字,并且必须有两个索引 i 和 j (0 <= i <= j <= ary.size) 以便:
- 如果 0 <= k < i,则块返回 ary 的正数,
- 如果 i <= k < j,则该块为 ary 返回零,并且
- 如果 j <= k < ary.size,则该块为 ary 返回一个负数。