0

我想要一个函数check来检查给定的list减少(“归结”)到给定函数下的一个值reduce_function。(一个常见的例子可能是检查列表列表是否仅包含相等长度的子列表。)

我至少看到了以下三种方法来实现这一点。对于他们每个人,我都看到了一些优点和缺点。在我看来,它们中的任何一个都没有真正的可读性。你能给我一个详细的概述:

哪一个会被认为是最具可读性和最“pythonic”的?

1. 测量一组缩减值的长度。

这似乎最易读,但需要reduce_function返回一个哈希:

def check(lst):
    return len(set(map(reduce_function, lst))) == 1

2.统计组数

def check(lst):
    return len(list(itertools.groupby(lst, key=reduce_function)) == 1

3. 用于all与第一个元素进行比较

这需要一个额外的 -or语句(可以用if-else语句代替)来涵盖lst为空的情况。

def check(lst):
    return not lst or all([reduce_function(el) == reduce_function(lst[0]) for el in lst])
4

3 回答 3

4

我喜欢所有 3 个选项,尽管第三个不需要是列表理解,只需去掉方括号即可。

与您的第二个选项一样,itertools文档有一个名为的配方all_equal ,用于检查迭代中的所有元素是否也相等itertools.groupby,尽管它们没有考虑自定义函数并且在为空时默认为 false,但它可以很容易地实现:

def all_equal(iterable, key=reduce_function):
    g = groupby(iterable, key)
    return next(g, False) and not next(g, False)

itertools.all_equal是检查迭代中所有元素是否相等的“pythonic”方法;我只是修改它以满足您的需求。

于 2019-11-13T12:51:39.713 回答
1

这可能有点基于意见,但也有客观原因支持或反对不同的选择。我将在这里重点介绍第一个和第三个。

第一种方法,转换为 aset并测试它的长度,恕我直言,它是最干净的,但它有 O(n) 额外的空间要求(在所有元素相同的最坏情况下)。它也适用于任何可迭代对象,而第三个仅适用于lst实际是list. 在当前的形式中,第三种方法也具有 O(n) 空间复杂度,(在所有情况下)由于 ; 中的列表[...]理解all。您可以改用生成器表达式。此外,reduce_function(lst[0])为每个其他元素重新计算。最后,not lst or是多余的,因为all空列表是True

另外,请注意,如果您想测试列表是否“归结为”至多一个值,正如 暗示的那样not lst or,您应该检查len(...) <= 1前两种方法。


我没有对此进行测试,但我认为这应该可行,并且 a) 使用 non-hashable reduce_function,b) 是 O(1) 空间复杂度,c) 使用列表或可迭代对象,并且 d) 尊重空列表极端情况:

def check(lst):
    return sum(1 for _ in itertools.groupby(lst, key=reduce_function)) <= 1

尽管如此,这仍然会评估reduce_function整个lst,即使已经很清楚存在不止一个不同的值。

于 2019-11-13T12:52:06.033 回答
0

如果要检查是否所有值都可以减少到相同的值,则无需遍历整个列表。一旦发现不等于列表中第一个元素的值,您就可以停止。它会更有效率:

def check(func, lst):
    it = iter(lst)
    first = func(next(it))
    for i in it:
        if func(i) != first:
            return False
    return True

您也可以for用函数替换循环all。在您的解决方案中,all计算列表len(lst) + 1时间中的第一个元素。

def check(func, lst):
    it = iter(lst)
    first = func(next(it))
    return all(first == func(i) for i in it)

check(sum, [[0, 1], [0, 1], [0, 1]])
# True

check(sum, [[1, 1], [0, 1], [0, 1]])
# False
于 2019-11-13T13:06:10.913 回答