Smaller does not always mean "one less". You can shrink a list by peeling one element off each end, or by cutting it in the middle and throwing half away. The way you choose decides how many calls are made.
def find(values, key, lo, hi):if lo > hi:return -1mid = (lo + hi) // 2if values[mid] == key:return midif values[mid] < key:return find(values, key, mid + 1, hi)return find(values, key, lo, mid - 1)
lo and hi close in. Each call halves what is left, so a thousand elements take ten calls.Peeling from both ends is the same pattern: if the ends agree, call yourself for what is left inside; if they do not, the answer is known at once. The base case is one element left, or none.