#python #recursion #nested-loops #nested-lists
Вопрос:
Мне нужно перебирать вложенные списки в python, где элементы списка являются вызовами функций (первый элемент в списке-функция, все остальные элементы являются аргументами этой функции). Эти аргументы также сами по себе могут быть функциями с аргументами, отсюда и вложенность. Список также может содержать одну функцию без аргументов. Вот так: ['foo','arg1','arg2']
Или: [['foo','arg1','arg2'],['bar'],['baz','arg1']]
Или: ['foo','arg1',['baz','arg1']]
И т. Д.
Вот пример использования фиктивных функций суммы и продукта… Сумма складывается, произведение умножается; Если бы мы оценили функции, то получили бы 10.
[«сумма»,[«продукт»,2,3],4]
Я могу просто передать функцию и аргументы другой функции, которая выполняет фактический «вызов» моих безопасных функций, но у меня возникают проблемы с рекурсивным повторением списка. Вот печальная попытка:
def process_list(l):
if isinstance(l,list):
results = []
for item in l:
if isinstance(item,list):
func = item[0]
args = item[1:]
for idx, arg in enumerate(args):
if isinstance(arg,list):
args[idx] = process_list(arg)
results.append(function_caller_result(func,args)) #what if that's a list?
else:
results.append(item)
return results
return l
Любая помощь здесь будет очень признательна! Заранее спасибо! 🙂
Комментарии:
1.
[['foo','arg1','arg2'],['bar'],['baz','arg1']]
каким будет результат?foo
вернулся бы (предположим)1
,bar
вернулся2
baz
бы , вернулся3
бы , так что вы ожидали[1, 2, 3]
бы ? второй и третий аргумент теперь не имеет функции, в которую они могут быть переданы, в любом случае, если первый элемент в списке или вложенном списке не является функцией, это приведет к случаям, подобным тому, что я объяснил, надеюсь, я ясно дал понять, например :[['foo', 1, 2], [['foo' ,1, 2], 1, 2], [['foo' ,1, 2], 1, 2]]
Ответ №1:
Вы можете попробовать эту рекурсивную функцию, которая работает для некоторых тестов, в которых решение Ajax1234 не работает.
Я заметил, что их ввод имеет форму [['sum',['product',2,3],4]]
, а ['sum',['product',2,3],4]
не показан в вашем вопросе.
Это решение использует ваш список входных ['sum',['product',2,3],4]
данных вместе с другими тестами.
from operator import add, sub, mul, truediv
funcs = {'sum': add, 'minus': sub, 'product': mul, 'divide': truediv} # you can use your functions instead of this
def process(seq):
if isinstance(seq, list):
return funcs[seq[0]](*(process(i) for i in seq[1:]))
return seq
print(process(['sum',['product',2,3],4]))
print(process(['sum',['product',['minus', 2, 3],3],4]))
print(process(['sum',['product',['minus', 2, 3],3],['divide', 3, 4]]))
Выход
10
1
-2.25
Если вы хотите один лайнер, то
def process(seq):
return seq if not isinstance(seq, list) else funcs[seq[0]](*map(process, seq[1:]))
Редактировать:
Это не сработает в случае, который я объяснил в комментарии, решение Ajax1234 решает эту проблему и объясняет причину изменения списка ввода и логики.
Ответ №2:
Вы пытаетесь создать своего рода рекурсивную функцию, но вручную. Это не сработает, если вложенность больше, чем вы закодировали.
Вместо этого напишите рекурсивную функцию:
my_list = ['sum',['product',2,3],4]
def process(l):
l = l.copy()
for item in l:
if isinstance(item,list):
l.replace(item, process(item))
return function_caller_result(*l)
# suppose l = [1, [2], '3']
# func(*l) => func(1, [2], '3')
result = process(my_list)
Ответ №3:
Вы можете использовать рекурсию при распаковке:
eval_d = {'sum':lambda x, y:x y, 'product':lambda x, y:x*y} #dict to store function names with an associated callable
data = [['sum',['product',2,3],4]]
def eval_f(d):
r = [eval_d[a](*[i if not isinstance(i, list) else eval_f([i]) for i in b])
for a, *b in d]
#r now stores the result from the function calls
return sum(r) #for this example to work, a cumulate sum is returned, but you can also just return the list
print(eval_f(data))
Выход:
10
Цель этого решения-работать в сценариях, в которых eval_f
передается список списков с сигнатурами функций [['foo','arg1','arg2'],['bar'],['baz','arg1']]
, т. е. Таким образом, отдельные вызовы функций должны быть заключены в список, чтобы гарантировать, что шаблон будет сохранен: ['foo','arg1','arg2']
будет передан eval_f
и [['foo','arg1','arg2']]
.
Комментарии:
1. ваше объяснение также заставило меня осознать некоторую ошибку в моем решении, так что спасибо за это, у меня есть вопрос, поскольку
[['sum', 1, 2], ['sum', 1, 2], ['sum', 1, 2]]
ваше решение дает9
, но как было принято решение поsum
всем промежуточным результатам?[3, 3, 3]
? Это то, что объясняет последний комментарий? Английский не является моим основным языком, поэтому я, возможно, неправильно истолковал2. @python_user Я включил только окончательное суммирование
return sum(r)
, чтобы функцияeval_f
дала разумный результат. Основываясь на коде операции,r
сам по себе, вероятно, должен быть возвращен на практике , но , поскольку у нас нет реализацийfoo
,bar
, иbaz
, имеет больше смысла предоставить решение, которое выполняет базовую оценку выражений, в стиле общего ввода операции.