Как понять, что перед тобой циклическая ссылка
требуется написать функцию, которая принимает произвольное количество любых аргументов(аргументами могут быть числа, списки и кортежи, содержащие внутри себя другие числа и кортежи, и т. д.. Функция должна вернуть произведение и сумму всех ненулевых элементов вложенных чисел. с произведением и суммой, вроде бы, справился, но как быть, если вдруг попадется циклическая ссылка, подскажите пожалуйста способ понять, что передо мной именно она.
def task_one(*args, a=0, b=1):
args_id = id(args)
total = a
composition = b
d = list(args)
for i in d:
if isinstance(i, (float, int)):
if i != 0:
total += i
composition *= i
else:
c = task_one(*i, a=total, b=composition)
total = c[0]
composition = c[1]
return [total, composition]
Пример вызова функции:
c = [1, 2, [3, 4, (5, 6, 2, [1, 2, (5, 4, 0)])], 2, [3, 0]]
print(task_one(c))
Вывод
[40, 345600]
При встрече циклической ссылки должно вернуть:
None
Пример передачи циклической ссылки:
a = [1, 2, 3]
b = [1, 2, 4]
a.append(b)
b.append(a)
print(task_one(a))
Вывод:
Traceback (most recent call last):
File "C:/Users/Владимир/PycharmProjects/untitled2/ArtezioHomework/Lesson3/fgdsf.py", line 24, in <module>
print(task_one(a))
File "C:/Users/Владимир/PycharmProjects/untitled2/ArtezioHomework/Lesson3/fgdsf.py", line 14, in task_one
c = task_one(*i, a=total, b=composition)
File "C:/Users/Владимир/PycharmProjects/untitled2/ArtezioHomework/Lesson3/fgdsf.py", line 14, in task_one
c = task_one(*i, a=total, b=composition)
File "C:/Users/Владимир/PycharmProjects/untitled2/ArtezioHomework/Lesson3/fgdsf.py", line 14, in task_one
c = task_one(*i, a=total, b=composition)
[Previous line repeated 994 more times]
File "C:/Users/Владимир/PycharmProjects/untitled2/ArtezioHomework/Lesson3/fgdsf.py", line 9, in task_one
if isinstance(i, (float, int)):
RecursionError: maximum recursion depth exceeded in __instancecheck__
Ответы (2 шт):
Других вариантов не вижу, кроме как хранить set с id всех обработанных не-чисел в цепочке рекурсии и при обработке очередного не-числа проверять, нет ли его id в этом set-е. Можно передавать этот set аргументом в функцию task_one. Примерно так:
def task_one(*args, a=0, b=1, s=None):
args_id = id(args)
total = a
composition = b
d = list(args)
for i in d:
if isinstance(i, (float, int)):
if i != 0:
total += i
composition *= i
else:
if s == None:
s = set()
if id(i) in s:
continue
s.add(id(i))
c = task_one(*i, a=total, b=composition, s=s)
total = c[0]
composition = c[1]
return [total, composition]
Кстати, промежуточные результаты можно не отправлять в функцию, а просто учитывать их на выходе функции в вычислениях:
def task_one(*args, s=None):
args_id = id(args)
total = 0
composition = 1
d = list(args)
for i in d:
if isinstance(i, (float, int)):
if i != 0:
total += i
composition *= i
else:
if s == None:
s = set()
if id(i) in s:
continue
s.add(id(i))
c = task_one(*i, s=s)
total += c[0]
composition *= c[1]
return [total, composition]
Чтобы проверить, имеет ли объект циклическую ссылку, можно использовать встроенный модуль
import pprint
и функцию
pprint.isrecursive(obj)
Пример использования:
FIRST_LIST = [1, 2, 3]
SECOND_LIST = [1, 2, 4]
THIRD_LIST = [1, 2, [3, 4, (5, 6, 2, [1, 2, (5, 4, 0)])], 2, [3, 0]]
FIRST_LIST.append(SECOND_LIST)
SECOND_LIST.append(THIRD_LIST)
THIRD_LIST.append(FIRST_LIST)
print(pprint.isrecursive(FIRST_LIST))
Вывод
True