I'd write it differently, using the result of the recursive calls to decide whether to return immediately or continue searching. My version returns a non-empty list of indices if the item was found, or None
otherwise.
def traverse(S, item): for i, element in enumerate(S): if element == item: return [i] if type(element) in (list, tuple): if indices := traverse(element, item): indices.insert(0, i) return indices
Results for your two tests (Try it online!):
[3, 1, 2, 4][5, 0, 0, 0]
I don't pass in a list, instead build the list backwards only from the location of the item (or if it's nowhere, then I don't build any list at all). This is much less work, although inserting at 0
makes it quadratic, although much faster than copying slices. If you want it linear, you could wrap this recursive function in another function and have the recursive function append the indices and have the wrapper function reverse the list before returning it to the original outside caller. The wrapper function would also let you turn the "non-empty list or None
" result into something else (like a pair with depth), if you want.