Рекурсия и работа со списками в Python

Моя задача:

Напишите функцию, которая рекурсивно ищет в сложном объекте значение. Сложный объект — это будет список списков списков и т. д. Например, [1, 2, [3, 4, [5, [6, []]]]]. Функция должна рекурсивно заходить внутрь вложенных списков, а если это другой тип данных - игнорировать его.

Сделал функцию searchNum(), мой ввод searchNum([[], 2 ,[[]], 1],1) должен вывести 3

def searchNum(array, value):
    for i in array:
        if isinstance(i, list):
            if len(i) == 0:
                continue
            if isinstance(i, list):
                return (array.index(i), searchNum(i, value))
        if isinstance(i, int):
            if i == value:
                return array.index(i)
    return False


print(searchNum([[], 2 ,[[]], 1],1))

Программа заходит в список под индексом 2 и возвращает из него False и не идет дальше к нужному числу (1, индекс 3). Как идти дальше по списку, используя рекурсию?

введите сюда описание изображения

Мой ответ:

def newSearch(array, value):
for idx, val in enumerate(array):
    if isinstance(val, list):
        if len(val) == 0:
            continue
        if newSearch(val, value) == False:
            continue
        else:
            return f'{idx}, {newSearch(val, value)}'
    if isinstance(val, int):
        if val == value:
            return f'{idx}'

return False

print(newSearch([[], 2 ,[[]], [0,[9, 1]]],1))

Результат: введите сюда описание изображения


Ответы (3 шт):

Автор решения: CrazyElf

У вас есть и ошибка и не оптимальность.

for i in array:
    ...
    array.index(i)

Зачем заново искать индекс элемента в списке, если вы можете узнать его сразу в момент перебора:

for idx, val in enumerate(array):

Также не совсем понятно, что вы собираетесь вернуть из функции:

return (array.index(i), searchNum(i, value))
...
return array.index(i)
...
return False

У вас 3 разных варианта, что может вернуться из этой функции:

  • кортеж из индекса и чего-то ещё
  • просто индекс
  • просто False

Определитесь - что именно вы хотите возвращать и в каких случаях? Сейчас это выглядит какой-то случайной мешаниной

Но главная ошибка тут:

if isinstance(i, list):
    return (array.index(i), searchNum(i, value))

При заходе в рекурсию вы не проверяете, что вернула функция searchNum, а просто пробрасываете её результат выше по стеку вызовов. Таким образом, если попался по дороге список, вы получите результат поиска в этом списке как окончательный результат работы функции, и не важно - был там найден элемент или нет. Нужно проверять результат вызова и делать возврат только если он был успешный, а если нет, то нужно позволить поиску продолжиться на данном уровне рекурсии:

if isinstance(i, list):
    ret = searchNum(i, value)
    if ... : # тут проверка, что ret хороший
        return ... # тут возврат в этом случае
→ Ссылка
Автор решения: greymaster

Я предлагаю решать эту задачу так, он возвращает индекс первого найденного элемента

def searchNum(array, value, answer=""):
    index = 0
    one_iter_ans = ""
    for index, check in enumerate(array):
        one_iter_ans = str(index) + "."
        if isinstance(check, list):
            if len(check) == 0:
                continue
            tmp = searchNum(check, value, answer + one_iter_ans)
            if tmp:
                return tmp
        if isinstance(check, int):
            if check == value:
                return answer + one_iter_ans
    return False


print(searchNum([[2, [1]], 2 ,[[]], 1],1))
→ Ссылка
Автор решения: FatCatStudent
1   def searchNum(array, value):
2       for index, i in enumerate(array):  # проходим по всем элементам
3          if isinstance(i, list):  # элемент список?
4               if len(i) == 0:  # пустой?
5                   continue  # пропускаем, возвращаем -1
6               res = searchNum(i, value)  # если не пустой -> рекурсивным вызовом проверяем внутренний список
7               if isinstance(res, int) and res >= 0:  # результат вызова число больше нуля? (индекс)
8                   return str(index) + '.' + str(res)  # возвращаем индекс внутреннего списка, где мы нашли индекс элемента + dot + индекс искомого элемента
9           if isinstance(i, int):  # элемент число?
10              if i == value:  # которое нам нужно?
11                  return index  # возвращаем его
12  return False  # ничего не нашли? возвращаем False

[0, [0, 5]] ищем 5

При первой итерации цикла (i=0) попадаем в строку 10, результат False, ничего не делаем, берём следующий элемент. Следующий элемент - список i=[0,5], попадаем в строку 3, затем в 6, переходим на следующий уровень рекурсии

Снова проходимся по списку, первая итерация цикла (i=0), снова попадаем в строку 10, результат False, ничего не делаем берём следующий элемент (i=5), это тоже число, попадаем в строку 10, результат True, возвращаем индекс = 1

На предыдущем уровне мы остановились в строке 6 на элементе с индексом 1 (это индекс внутреннего списка), результат = 1, попадаем в строку 7, условие выполняется, выполняем возврат строки = 1(индекс) + . + найденный индекс(1) = 1.1, рекурсия закончена, значение вернулось конструкции, которая впервые вызвала метод, результат = 1.1

Если элемента в списке не окажется, метод всегда будет возвращать False, и результат, соответственно, тоже будет False

→ Ссылка