【算法题】不定列表求和

问题

在一个嵌套了多个元素为数字或列表 (列表中也是数字或列表) 的列表中,求出所有数字的和。
例如:[1, [2, [3, [4, 5]]]]的和为 15,[1, 2, 3, 4, 5]的和也为 15。

分析

通过遍历每一个元素,如果发现这个元素不是列表,就把它累加,否则继续遍历这个元素直到找到数字。可以采用递归的方式来求解。

def sum_ulist(exp):
    sum = 0
    for i in exp:
        sum += i if type(i) != list else sum_ulist(i)
    return sum

可以看到最后的程序非常短小,运行如下:

>>> sum_ulist([1, [2, [3, [4, 5]]]])
15
>>> sum_ulist([1, 2, 3, 4, 5])
15

思考

还是一个不定嵌套层数的列表,这次不进行求和,而是把其中的所有数字都取出来,最后放在一个列表中返回。


与上面类似,不过上面的求和在每一次函数调用中只关注当前层级的总和即可,而这边的递归需要把数字都放在同一个列表中,可以利用默认参数的一个特性来解决。

我们之前提到,默认参数必须设置为不可变,否则会带来一些问题:当你调用此函数却不指定默认参数的值时,它会自动使用函数第一次调用中默认参数的对象,如果这个对象发生了改变,可能达不到我们预期的效果。

举个例子:

def a(li=[]):
    li.append(1)
    return li
>>> a()
[1]
>>> a()
[1, 1]
>>> a()
[1, 1, 1]

这种情况在很多时候都不是我们想要的,这边却可以利用这个特性:

def add_ulist(exp, tem_li=[]):
    for i in exp:
        tem_li.append(i) if type(i) != list else add_ulist(i)
    return sum

通过在 else 语句后面的递归中不指定默认参数,我们就让后面的函数调用都只操作第一次函数调用时初始化的 tem_li 对象了,少写了几个字母 :)

运行如下:

>>> add_ulist([1, [2, [3, [4, 5]]], [6, [7, 8]]])
[1, 2, 3, 4, 5, 6, 7, 8]

当然,在 else 语句后面的递归中将这次函数获得的 tem_li 列表传给默认参数也可以:

def add_ulist(exp, tem_li=[]):
    for i in exp:
        tem_li.append(i) if type(i) != list else add_ulist(i, tem_li)
    return sum

最后,如果弄不清楚默认参数的奇妙性质的话,还是老老实实将默认参数设置为不可变对象吧:

def add_ulist(exp, tem_li=None):
    if tem_li is None:
        tem_li = []
    for i in exp:
        tem_li.append(i) if type(i) != list else add_ulist(i, tem_li)
    return sum