我正在尝试创建一对函数,给定一个“起始”数字列表,将递归地添加到每个索引位置,直到达到定义的最大值(就像里程表在汽车中工作的方式一样) - 在重置为1并转移到下一个轮子之前,每个计数轮增加到9。
代码如下所示:
number_list = []
def counter(start, i, max_count):
if start[len(start)-1-i] < max_count:
start[len(start)-1-i] += 1
return(start, i, max_count)
else:
for j in range (len(start)):
if start[len(start)-1-i-j] == max_count:
start[len(start)-1-i-j] = 1
else:
start[len(start)-1-i-j] += 1
return(start, i, max_count)
def all_values(fresh_start, i, max_count):
number_list.append(fresh_start)
new_values = counter(fresh_start,i,max_count)
if new_values != None:
all_values(*new_values)
当我运行 all_values([1,1,1],0,3)并打印 number_list 时,我得到:
[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1],
[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1],
[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1],
[1, 1, 1], [1, 1, 1], [1, 1, 1]]
哪个是不幸的。如果我用
替换 all_values 的第一行,我们就知道了print(fresh_start)
我得到了我正在追求的东西:
[1, 1, 1]
[1, 1, 2]
[1, 1, 3]
[1, 2, 1]
[1, 2, 2]
[1, 2, 3]
[1, 3, 1]
[1, 3, 2]
[1, 3, 3]
[2, 1, 1]
[2, 1, 2]
[2, 1, 3]
[2, 2, 1]
[2, 2, 2]
[2, 2, 3]
[2, 3, 1]
[2, 3, 2]
[2, 3, 3]
[3, 1, 1]
[3, 1, 2]
[3, 1, 3]
[3, 2, 1]
[3, 2, 2]
[3, 2, 3]
[3, 3, 1]
[3, 3, 2]
[3, 3, 3]
我已经尝试制作 fresh_start 的副本(通过 temp = fresh_start )并附加相应的内容,但输出中没有任何变化。
任何人都可以提供任何有关修复代码的方法吗?关于如何简化问题的反馈也将受到欢迎。
非常感谢!
答案 0 :(得分:3)
temp = fresh_start
不会复制。附加不会制作副本,作业不会制作副本,而且几乎任何不说副本的副本都不会制作副本。如果您想要一份副本,请将其剪切:
fresh_start[:]
是副本。
答案 1 :(得分:1)
在Python解释器中尝试以下内容:
>>> a = [1,1,1]
>>> b = []
>>> b.append(a)
>>> b.append(a)
>>> b.append(a)
>>> b
[[1, 1, 1], [1, 1, 1], [1, 1, 1]]
>>> b[2][2] = 2
>>> b
[[1, 1, 2], [1, 1, 2], [1, 1, 2]]
这是代码中发生的事情的简化版本。但为什么会这样呢?
b.append(a)
实际上并未复制a
并将其填入b
的数组中。它正在为a
制作参考。它就像是Web浏览器中的书签:当您使用书签打开网页时,您希望现在看到的网页就像现在一样,而不是当您为其添加书签时。但这也意味着,如果您对同一页面有多个书签,并且该页面发生了变化,无论您关注哪个书签,都会看到更改后的版本。
与temp = a
的故事相同,就此而言a = [1,1,1]
。 temp
和a
是特定数组的“书签”,恰好包含三个。上面示例中的b
是一个数组的书签...其中包含三个包含三个书签的同一个数组。
所以你要做的是创建一个新数组并复制旧数组的元素。最快的方法是采用包含整个数组的数组切片,如user2357112所示:
>>> a = [1,1,1]
>>> b = []
>>> b.append(a[:])
>>> b.append(a[:])
>>> b.append(a[:])
>>> b
[[1, 1, 1], [1, 1, 1], [1, 1, 1]]
>>> b[2][2] = 2
>>> b
[[1, 1, 1], [1, 1, 1], [1, 1, 2]]
好多了。
答案 2 :(得分:0)
当我查看所需的输出时,我不禁想到使用其中一个numpy网格数据生成函数。
import numpy
first_column, second_column, third_column = numpy.mgrid[1:4,1:4,1:4]
numpy.dstack((first_column.flatten(),second_column.flatten(),third_column.flatten()))
Out[23]:
array([[[1, 1, 1],
[1, 1, 2],
[1, 1, 3],
[1, 2, 1],
[1, 2, 2],
[1, 2, 3],
[1, 3, 1],
[1, 3, 2],
[1, 3, 3],
[2, 1, 1],
[2, 1, 2],
[2, 1, 3],
[2, 2, 1],
[2, 2, 2],
[2, 2, 3],
[2, 3, 1],
[2, 3, 2],
[2, 3, 3],
[3, 1, 1],
[3, 1, 2],
[3, 1, 3],
[3, 2, 1],
[3, 2, 2],
[3, 2, 3],
[3, 3, 1],
[3, 3, 2],
[3, 3, 3]]])
当然,这种特殊方法的实用性可能取决于您需要处理的各种输入,但我怀疑这可能是构建数据的有趣方式,而numpy对于这种事情来说非常快。大概如果你的输入列表有更多的元素,你可以有更多的min:max参数输入mgrid []然后以类似的方式解包/堆栈。
答案 3 :(得分:0)
这是您的程序的简化版本,可以使用。评论将随之而来。
number_list = []
def _adjust_counter_value(counter, n, max_count):
"""
We want the counter to go from 1 to max_count, then start over at 1.
This function adds n to the counter and then returns a tuple:
(new_counter_value, carry_to_next_counter)
"""
assert max_count >= 1
assert 1 <= counter <= max_count
# Counter is in closed range: [1, max_count]
# Subtract 1 so expected value is in closed range [0, max_count - 1]
x = counter - 1 + n
carry, x = divmod(x, max_count)
# Add 1 so expected value is in closed range [1, max_count]
counter = x + 1
return (counter, carry)
def increment_counter(start, i, max_count):
last = len(start) - 1 - i
copy = start[:] # make a copy of the start
add = 1 # start by adding 1 to index
for i_cur in range(last, -1, -1):
copy[i_cur], add = _adjust_counter_value(copy[i_cur], add, max_count)
if 0 == add:
return (copy, i, max_count)
else:
# if we have a carry out of the 0th position, we are done with the sequence
return None
def all_values(fresh_start, i, max_count):
number_list.append(fresh_start)
new_values = increment_counter(fresh_start,i,max_count)
if new_values != None:
all_values(*new_values)
all_values([1,1,1],0,3)
import itertools as it
correct = [list(tup) for tup in it.product(range(1,4), range(1,4), range(1,4))]
assert number_list == correct
由于您希望计数器从1到max_count
包含,所以更新每个计数器有点棘手。您的原始解决方案是使用多个if
语句,但在这里我创建了一个使用divmod()
来计算每个新数字的辅助函数。这允许我们向任何数字添加任何增量,并找到正确的数字执行。
您的原始程序从未更改i
的值,因此我修改过的程序也没有。您可以通过删除i
并让increment_counter()
始终转到最后位置来进一步简化程序。
如果您在不调用for
或break
的情况下运行return
循环,则else:
情况会在存在的情况下运行。在这里,我添加了一个else:
案例来处理列表中第0位的执行。如果存在第0位的执行,则表示我们已到达计数器序列的末尾。在这种情况下,我们返回None
。
您的原始程序有点棘手。它在return
中有两个显式counter()
语句,在序列末尾有一个隐式返回。它确实返回None
表示递归可以停止,但它的方式对我来说太棘手了。我推荐使用明确的return None
。
请注意,Python有一个模块itertools
,其中包含一种生成这样的计数器系列的方法。我用它来检查结果是否正确。
我确信你正在写这篇文章来了解递归,但是请注意Python不是像这样的递归解决方案的最佳语言。 Python有一个相对较浅的递归堆栈,并且不会自动将尾递归转换为迭代循环,因此如果递归调用嵌套足够多次,这可能会导致Python内部的堆栈溢出。 Python中最好的解决方案是使用itertools.product()
,就像我直接生成所需的计数器序列一样。
由于生成的序列是列表列表,而itertools.product()
生成元组,我使用列表推导将每个元组转换为列表,因此最终结果是列表列表,我们可以简单地使用用于比较它们的Python ==
运算符。