用 break 列出推导式

list comprehensions with break

我有以下代码,我想用列表理解写在一行中。

list1 = [4, 5, 6, 9, 10, 16, 21, 23, 25, 27]
list2 = [1, 3, 5, 7, 8, 11, 12, 13, 14, 15, 17, 20, 24, 26, 56]

list3 = []
for i in list1:
    for j in list2:
        if j>i:
            # print(i,j)
            list3.append(j)
            break
print(list1)
print(list3)

输出为:

[4, 5, 6, 9, 10, 16, 21, 23, 25, 27]
[5, 7, 7, 11, 11, 17, 24, 24, 26, 56]

是 break 语句让我失望,我不知道该把它放在哪里。

谢谢

要构建表达式,首先忽略 break 条件:

In [32]: [[j for j in list2 if j > i] for i in list1]                                       
Out[32]: 
[[5, 7, 8, 11, 12, 13, 14, 15, 17, 20, 24, 26, 56],
 [7, 8, 11, 12, 13, 14, 15, 17, 20, 24, 26, 56],
 [7, 8, 11, 12, 13, 14, 15, 17, 20, 24, 26, 56],
 [11, 12, 13, 14, 15, 17, 20, 24, 26, 56],
 [11, 12, 13, 14, 15, 17, 20, 24, 26, 56],
 [17, 20, 24, 26, 56],
 [24, 26, 56],
 [24, 26, 56],
 [26, 56],
 [56]]

从那里您可以添加 min 约束:

In [33]: [min([j for j in list2 if j > i]) for i in list1]                                  
Out[33]: [5, 7, 7, 11, 11, 17, 24, 24, 26, 56]

我已经尝试计时 AbbeGijly 发布的答案。

事实证明,它比原来的解决方案慢。看看吧。

import timeit

print(timeit.timeit('''
list1 = [4, 5, 6, 9, 10, 16, 21, 23, 25, 27]
list2 = [1, 3, 5, 7, 8, 11, 12, 13, 14, 15, 17, 20, 24, 26, 40, 56]

list3 = []
for i in list1:
    for j in list2:
        if j>i:
            # print(i,j)
            list3.append(j)
            break
'''))

print(timeit.timeit('''
list1 = [4, 5, 6, 9, 10, 16, 21, 23, 25, 27]
list2 = [1, 3, 5, 7, 8, 11, 12, 13, 14, 15, 17, 20, 24, 26, 40, 56]
list4 = [[j for j in list2 if j > i] for i in list1]
'''))

输出为:

3.6144596
8.731578200000001

您可以将 break 逻辑移动到一个单独的函数中,然后将该函数放入列表理解中:

def smallest_value_larger_than_i(candidate_values, i):
    for value in candidate_values:
        if value > i:
            return value
    return None  # Not sure how you want to handle this case

list3 = [smallest_value_larger_than_i(list2, i) for i in list1]

这比您的原始解决方案运行速度稍慢,但如果使用列表推导式的目标是提高速度,那么您可以通过改进算法来获得更好的结果。例如,如果两个列表都已排序,那么您可以在跳过一次后立即丢弃 list2 中的元素,而不是将它们与 list1 的其余部分进行检查。您也可以对 list2 进行二进制搜索,而不是线性扫描它。

你不能真正打破列表理解的内部 for 循环,你可以做的是通过使用 next 函数找到匹配值的第一次出现来避免必须打破它:

list1 = [4, 5, 6, 9, 10, 16, 21, 23, 25, 27]
list2 = [1, 3, 5, 7, 8, 11, 12, 13, 14, 15, 17, 20, 24, 26, 56]
list3 = [ next(j for j in list2 if j>i) for i in list1 ]

输出:

print(list1)
print(list3)
[4, 5, 6, 9, 10, 16, 21, 23, 25, 27]
[5, 7, 7, 11, 11, 17, 24, 24, 26, 56]

如果您担心性能(因为列表理解会比循环慢),您可以在列表 2 中使用二等分搜索来查找下一个更高的值:

from bisect import bisect_left
list3 = [ list2[bisect_left(list2,i+1)] for i in list1 ]

这假设 list2 按升序排序并且 max(list2) > max(list1)