我需要遍历一个嵌套列表,用str()
处理每个非列表项,并返回类似的列表以保持结构。使用递归将非常容易,但是我需要采用这种迭代方式。以下是我尝试的while
循环:
def myiter(e):
a = [e] # initial list
c = [[]] # final result
get_last = lambda x: x[len(x)-1] # get ref to the final sublist
l = get_last(c)
while a:
b = a.pop(0)
if isinstance(b, list):
# if there are more items to process in the original list
if a:
a = b + a
# else extend original list to process sublists
else:
a.extend(b)
# make a new sublist ref
l = get_last(c)
c.append([])
else:
# walk and process every item in the nested list
l.append(str(b))
return c
这有几个问题,因为输出将显示:
myiter([1, [2, [3, 4], 5]]) # [['1'], ['2'], ['3', '4', '5'], []]
期望的结果是:
['1', ['2', ['3', '4'], '5']]
在Python中有简单的迭代方法来完成任务吗?
答案 0 :(得分:3)
这似乎可行:
def stringify(a):
a = a[:] # Make copy of what was passed in.
res = [] # Initialize result list.
my_stack = [] # Initialize our own LIFO stack.
while (a or my_stack): # While a or my_stack is non-empty
if (a):
elem = a.pop(0)
if (not isinstance(elem, list)): # If popped elem is not a list
res.append(str(elem)) # Append stringified elem to res
else:
my_stack.append((a, res)) # Push some stuff, to resume working upon later.
a = elem # Let's start iterating on this inner list
res = [] # This inner list needs a clean res, to start with.
else: # my_stack is non-empty
a, res_prev = my_stack.pop() # Pop some stuff, to resume, work on outer list
res_prev.append(res) # First, append our just-completed inner list.
res = res_prev
return res
输出:
a = [1, [2, [3, 4], 5]]
stringify(a)
['1', ['2', ['3', '4'], '5']]
通过了以下测试案例:
a = [1, [[[2]]]]
a = [[[1]], 2]
a = [1, [[2]]]
a = [1, [2, [3, 4], 5], [6, [7, [8]]], 9]
a = [1, [2, [3, 4], 5]]
a = [1, 2, 3, 4, 5]
有关其工作原理的一些说明:
pop
上的a
产生一个整数,我们只需将字符串化的整数附加到res
pop
上的a
产生了一个内部列表,则在处理该内部列表之后出现的元素之前,我们需要开始处理该内部列表。处理完内部列表之后,我们将不得不回到a
的其余未弹出元素。a
为空,我们的res
就会指向等效的字符串化列表,现在是时候将res
附加到任何内容了可能是其(字符串化的)外部列表a
(分配a = elem
),并将一个空列表用作新的res
({{1} }。在此之前,我们需要将当前res = []
和当前a
res
上的任何内容都代表我们当前正在处理的任何列表(my_stack
)的直接外部列表。答案 1 :(得分:2)
为什么不递归?使用递归过程处理递归数据结构是自然而直接的。将递归过程转换为迭代过程不必涉及克隆输入,创建stack
或其他中间值。您的大脑可以摆脱如此严重的复杂性-
def first (a = []):
return a[0]
def rest (a = []):
return a[1:]
def myiter (a = []):
# base: empty a
if not a:
return []
# inductive: non-empty a, first elem is list
elif isinstance(first(a), list):
return [ myiter(first(a)) ] + myiter(rest(a))
# inductive: non-empty a, first elem is non-list
else:
return [ str(first(a)) ] + myiter(rest(a))
print(myiter([1, [2, [3, 4], 5]]))
当然,将str
设为函数f
的参数-
def myiter (f, a = []):
# base: empty a
if not a:
return []
# inductive: non-empty a, first elem is list
elif isinstance(first(a), list):
return [ myiter(f, first(a)) ] + myiter(f, rest(a))
# inductive: non-empty a, first elem is non-list
else:
return [ f(first(a)) ] + myiter(f, rest(a))
现在,您可以根据需要深度映射str
-
print(myiter(str, [1, [2, [3, 4], 5]]))
# ['1', ['2', ['3', '4'], '5']]
或使用您选择的任何功能-
def square (x):
return x * x
print(myiter(square, [1, [2, [3, 4], 5]]))
# [1, [4, [9, 16], 25]]
您是否由于堆栈限制而试图避免递归?如果您将其设为尾递归-
def identity (x):
return x
def myiter (f, init = []):
def run (a = init, then = identity):
if not a:
return \
then([])
# inductive: non-empty a, first elem is list
elif isinstance(first(a), list):
return \
recur(first(a), lambda l: \
recur(rest(a), lambda r: \
then([ l ] + r)))
# inductive: non-empty a, first elem is non-list
else:
return \
recur(rest(a), lambda r: \
then([ f(first(a)) ] + r))
# loop inner function
return loop (run)
然后实现一个通用的loop
,它将递归调用栈转换为迭代序列-
def recur (*values):
return (recur, values)
def loop (f):
acc = f ()
while type(acc) is tuple and acc[0] is recur:
acc = f(*acc[1])
return acc
输出是相同的,但是现在myiter
可以接受 any 嵌套限制的数组。递归无限制;多么美丽的东西-
print(myiter(str, [1, [2, [3, 4], 5]]))
# ['1', ['2', ['3', '4'], '5']]
print(myiter(square, [1, [2, [3, 4], 5]]))
# [1, [4, [9, 16], 25]]
查看此程序并使用repl.it在您自己的浏览器中验证结果。
为什么不能使用递归? -戳刺
@Jab,三个原因:第一个最大递归限制经常在我的一个应用程序上遇到,第二个性能问题虽然映射可能与迭代方法并驾齐驱,其次是为了训练和研究这两种不同的编码风格。 – MarkokraM
因此您尚未达到递归限制,但您担心程序会这样做吗?最好了解实际的限制,而不是围绕幽灵编写程序。在使用生成器的简化实现中,请注意,递归仅在遇到嵌套级别时发生。即使您直接保留此实现,它也可以支持任何长度和嵌套级别的列表,直到堆栈限制为止,默认情况下,该列表可能约为1,000。这意味着唯一会破坏堆栈的数据输入是嵌套1000次或更多的数据输入。在达到 actual 限制之前,可以将该程序保留为安全状态。
def square (x):
return x * x
def myiter (f, init = []):
def gen (a):
for x in a:
if isinstance(x, list):
yield list(gen(x)) # recursion
else:
yield f(x)
return list(gen(init))
print(myiter(str, [1, [2, [3, 4], 5]]))
# ['1', ['2', ['3', '4'], '5']]
print(myiter(square, [1, [2, [3, 4], 5]]))
# [1, [4, [9, 16], 25]]