Conversation
…irst element binary_search_by_recursion used right=-1 as its 'not given' sentinel, but the recursion legitimately reaches right=-1 when the item is smaller than every element, which reset right to len-1 and never terminated. Use None as the sentinel and add doctests for items below and above the range. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
@priya-sundaram-dev Is the bug real? If so, is there a way to solve it without making |
priya-sundaram-dev
left a comment
There was a problem hiding this comment.
Good catch. The root cause is the right = -1 sentinel: when item is below every element the recursion drives right negative, and if right < 0: right = len - 1 then resets the window, so it never terminates. Switching the sentinel to right: int | None = None and checking if right is None is the clean, idiomatic fix and keeps a real right < 0 (empty/exhausted window) as a genuine base case.
Verified: reproduced the original RecursionError for exponential_search([0, 5, 7, 10, 15], -3); with the patch all 12 doctests pass and a 100k-case fuzz run against membership (lengths 0-10, out-of-range items on both ends) returns correct indices / -1 with no recursion errors. CI green.
LGTM.
|
Good questions — happy to walk through them now that it's in. 🙂 Is the bug real? Yes. With the old Can we avoid making Should we add an explicit Negative Float Happy to open a small follow-up doing the wrapper/core split if you'd prefer that shape over the |
|
@priya-sundaram-dev Yes, please create a small follow-up PR doing the wrapper/core split because I would prefer that shape over the None sentinel. |
Follow-up to #15384. Replace the ``right: int | None`` sentinel on the recursive path with a thin wrapper that defaults ``right`` once, then delegates to a nested ``_search(left, right)`` core whose indices are always concrete ints. This matches the wrapper/core shape already used by ``binary_search.py`` and keeps the recursion flat so the window can only shrink.
Describe your change
exponential_search(andbinary_search_by_recursion) never terminate when the item is smaller than every element:binary_search_by_recursionusesright=-1to mean "not given" (if right < 0: right = len(...) - 1). When the item is below the first element the recursion legitimately reachesright = midpoint - 1 = -1, which is then mistaken for "not given",rightis reset to the last index, and the search starts over forever. The fix makes the sentinelNone, which cannot collide with a real index. Nothing else in the repository calls this function.Doctests for an item below and above the range are added. I also compared both functions with a membership check on 30,000 random sorted lists (lengths 0-15, values -8..8): 0 mismatches after the change.
Checklist
I left "all my own work" unticked on purpose: this change was written with AI assistance (Claude Code, as
AGENTS.mdinvites). I found the problem by fuzzing the functions insearches/against a membership check, reproduced it, and ran the doctests,ruff checkandruff formaton the changed file.