你能解释一下下面这个函数吗?

7
def a(n):
    return max([len(n)] + [a(i) for i in n]) if isinstance(n, list) else 0

这是我最近的一个测试,我就是无法理解列表推导式。所以基本上这个函数应该返回最大列表的长度(根据正确答案的假设)。如果不是因为函数中的这部分代码,我可能已经理解了:

+ [a(i) for i in n])

当我看到那部分时,它似乎增加了正在迭代的列表的长度。有人能解释一下这部分的目的吗?更具体地说,增加的原因是什么?
编辑:经过仔细观察后,看起来函数将第一个列表的长度放入一个列表中,然后放入下一个列表的长度并返回最大值?...是这样工作的吗?

1
那应该是 a(i) - thefourtheye
抱歉,那是一个打错的字,谢谢。 - user3050527
这是故意模糊的。关键在于用你自己的术语重写该函数(编辑:请参见下面的答案)。 - salezica
5个回答

6

这个函数计算树中最大节点的长度(树是通过列表嵌套列表实现的)。也许通过一些重命名和重新排列,它会更加清晰:

def longest_list_in_tree(tree):
    if not isinstance(tree, list):
        return 0 # This is a leaf-value of the tree, it has "length 0"

    own_length = len(tree)
    longest_descendant_of_each_subtree = [
        longest_list_in_tree(subtree) for subtree in tree
    ]

    return max([own_length] + longest_descendant_of_each_subtree)

4

该程序的目标是找出列表中最长列表的长度。假设您将一个列表的列表作为输入。

b = [[1], [1, 2], [3], [[1, 2, 3], [1]]]

现在,要找到内部列表的长度,我们首先必须遍历当前列表。但是,如果最初的输入本身不是一个列表怎么办?我们将该元素的长度返回为0。这个检查是由以下部分完成的:

... if isinstance(n, list) else 0

如果n不是列表的实例,我们返回0。现在我们确定当前项是一个列表,我们获取该列表的长度len(n)。然后,我们像这样迭代列表:

[a(i) for i in n]

我们取得n的每个元素并递归调用a。现在,如果该元素是一个列表,则期望a(i)返回其内最长列表的长度。因此,我们收集每个元素中最长列表的长度,将它们创建成一个列表,并将其与n的长度连接起来。这个连接是通过进行的。

[len(n)] + [a(i) for i in n]

我们通过用方括号将len(n)括起来,使其成为一个列表,然后使用+运算符将我们从所有元素中获取的长度附加到其中。因此,在每次递归函数调用中,我们得到类似于以下内容:

[length of current list,
        max length of sub elements in element 1 of n,
        max length of sub elements in element 2 of n,
        max length of sub elements in element 3 of n,
...
]

然后我们找到所有元素中的最大值,并将其返回给调用者。

为了更好地理解这个程序,我创建了一个装饰器来记录输入和相应的输出。这可能有助于您更好地理解递归。

def logger(f):
    def wrapper(args, level):
        print "\t\t" * level + "Entered with input ", args
        temp = f(args, level)
        print "\t\t" * level + "Leaving with length", temp
        return temp
    return wrapper

@logger
def a(n, lev):
    return max([len(n)] + [a(i, lev+1) for i in n]) if isinstance(n, list) else 0

b = [[1], [1, 2], [3], [[1, 2, 3], [1]]]

print a(b, 0)

输出结果如下:

Entered with input  [[1], [1, 2], [3], [[1, 2, 3], [1]]]
        Entered with input  [1]
                Entered with input  1
                Leaving with length 0
        Leaving with length 1
        Entered with input  [1, 2]
                Entered with input  1
                Leaving with length 0
                Entered with input  2
                Leaving with length 0
        Leaving with length 2
        Entered with input  [3]
                Entered with input  3
                Leaving with length 0
        Leaving with length 1
        Entered with input  [[1, 2, 3], [1]]
                Entered with input  [1, 2, 3]
                        Entered with input  1
                        Leaving with length 0
                        Entered with input  2
                        Leaving with length 0
                        Entered with input  3
                        Leaving with length 0
                Leaving with length 3
                Entered with input  [1]
                        Entered with input  1
                        Leaving with length 0
                Leaving with length 1
        Leaving with length 3
Leaving with length 4
4

2
列表推导式是 Python 和其他一些语言中相当深奥的属性。它可能会使很多人感到困惑。尽管如此,它们所遵循的结构非常简单,基于集合符号表示法:
1. 代表输入列表成员的变量(在这种情况下是递归函数调用)。 2. 要处理的列表(在这种情况下是 n)。 3. 一个可选的谓词表达式。 4. 输出表达式,根据满足谓词的输入列表中的条目产生输出列表的条目。
该函数将返回给定输入 n 的最大大小列表/子列表。max 函数调用中包含的列表推导式使用自身的递归调用以相同的方式遍历子列表,构建所有值的大小列表(如果不是列表,则为 0)。
为了帮助解释代码,你编写另一个函数以返回传递给 max 函数调用的输入数组,以帮助提高理解:
def b(n):
  return [len(n)]+[b(i) for i in n] if isinstance(n,list) else 0

例子:

>>> x = [1,2,3,4,5,[1,2,3,4,5,6,7,8,9],7,[1,2,[1,2,3,4,5,6,7,8,9,10,11,12,13,14],4,5,6,[1,2]]]
>>> a(x)
14
>>> b(x)
[8, 0, 0, 0, 0, 0, [9, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0, [7, 0, 0, [14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0, 0, 0, [2, 0, 0]]]

注意:针对您的编辑——+ 运算符允许您将两个列表合并在一起。例如:
x = [a,b,c]
y = [d,e,f]
z = x + y;

1
让我为您分解一下:

def a(n):
    '''return the largest length of a list or sublists in that list'''
    if isinstance(n, list):
        # if n is a list, return the max of the len of the list
        # for all subitems in the list, perform a(subitem) recursively.
        # so if a sublist is longer than the list, return the largest len.
        return max([len(n)] + [a(i) for i in n]) 
    else:
        # that subitem isn't a list, so return 0, which won't factor into the max calc.
        return 0

我认为我会称呼这样的函数:
def max_len_list_of_lists(a_list):
    '''return the largest length of a list or sublists in that list'''
    ...

1
假设 n 是一个列表,其中的成员也可以是包含其他列表等内容的列表,此函数返回最长列表的长度。例如:
a([2,3,4,5]) = 4   # [2,3,4,5]
a([1, [2,3,4,5], 3]) = 4   # [2,3,4,5]
a([1, [2,3,4,5], [3, [7,8,9,0,1]]]) = 5   # [7,8,9,0,1]

基本上,这是一个尾递归,它表明所有列表的最大长度是作为输入传递的列表及其所有成员列表长度中的最大值。
运行一些小例子,看看为什么它是正确的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接