【问题标题】:Only using items from one list once in nested list comprehension在嵌套列表理解中仅使用一个列表中的项目一次
【发布时间】:2016-03-03 12:07:34
【问题描述】:

我正在尝试使用列表推导生成一个新列表,该列表由从 list1 中提取的一个字母直接后跟(在冒号之后)后跟 list2 中以该特定字母开头的单词。我设法使用嵌套的 for 循环对其进行编码,如下所示:

list1=["A","B"]
list2=["Apple","Banana","Balloon","Boxer","Crayons","Elephant"]

newlist=[]
for i in list1:
    newlist.append(i+":")
    for j in list2:
        if j[0]==i:
            newlist[-1]+=j+","

产生预期结果:['A:Apple,', 'B:Banana,Balloon,Boxer,']

尝试使用列表推导同样的方法,我想出了以下内容:

list1=["A","B"]
list2=["Apple","Banana","Balloon","Boxer","Crayons","Elephant"]

newlist=[i+":"+j+"," for i in list1 for j in list2 if i==j[0]]

导致:['A:Apple,', 'B:Banana,', 'B:Balloon,', 'B:Boxer,']

每次找到具有该首字母的单词时,都会在newlist 中创建一个新项目,而我的意图是每个字母都有一个项目。

有没有办法编辑列表理解代码以获得与使用嵌套 for 循环相同的结果?

【问题讨论】:

    标签: python list for-loop nested list-comprehension


    【解决方案1】:
    list1=["A","B"]
    list2=["Apple","Banana","Balloon","Boxer","Crayons","Elephant"]
    
    res = [l1 + ':' + ','.join(l2 for l2 in list2 if l2.startswith(l1)) for l1 in list1]
    print(res)
    
    # ['A:Apple', 'B:Banana,Balloon,Boxer']
    

    但它似乎读起来很复杂,所以我建议使用嵌套循环。您可以创建生成器以提高可读性(如果您认为此版本更具可读性):

    def f(list1, list2):
        for l1 in list1:
            val = ','.join(l2 for l2 in list2 if l2.startswith(l1))
            yield l1 + ':' + val
    
    print(list(f(list1, list2)))
    
    # ['A:Apple', 'B:Banana,Balloon,Boxer']
    

    【讨论】:

    • 生成器并没有提高效率;它在执行方面会稍微,而且因为你在生成器上调用list(),你会撤销任何可能存在的内存效率。
    • 此外,您的版本仍然存在 O(NM) 指数复杂度。你的版本有 26 个字母和 1000 个单词,需要 26000 步,我的需要 2026 步。
    • “生成器不会让这个变得更有效率”,以防他需要准备好的列表 - 是的。万一他会遍历值-不。但效率不是重点:我认为这个解决方案比其他解决方案更具可读性。
    • @germn,在生成器上使用 join 意味着无论如何都会创建一个列表,因此没有优势
    • @germn: str.join() 在这里是个例外,使用列表推导比生成器表达式更快:list comprehension without [ ], Python
    【解决方案2】:

    您需要做的就是删除第二个for 循环并将其替换为','.join(matching_words) 调用,您现在在字符串连接中使用j

    newlist = ['{}:{}'.format(l, ','.join([w for w in list2 if w[0] == l])) for l in list1]
    

    这不是很有效;您为每个字母循环 all list2 中的单词。为了有效地做到这一点,您最好将列表预处理成字典:

    list2_map = {}
    for word in list2:
        list2_map.setdefault(word[0], []).append(word)
    
    newlist = ['{}:{}'.format(l, ','.join(list2_map.get(l, []))) for l in list1]
    

    第一个循环构建一个字典,将首字母映射到单词列表,这样您就可以直接使用这些列表,而不是使用嵌套列表推导式。

    演示:

    >>> list1 = ['A', 'B']
    >>> list2 = ['Apple', 'Banana', 'Balloon', 'Boxer', 'Crayons', 'Elephant']
    >>> list2_map = {}
    >>> for word in list2:
    ...     list2_map.setdefault(word[0], []).append(word)
    ...
    >>> ['{}:{}'.format(l, ','.join(list2_map.get(l, []))) for l in list1]
    ['A:Apple', 'B:Banana,Balloon,Boxer']
    

    上述算法循环两次遍历所有list2,一次遍历list1,使其成为O(N) 线性算法(向list2 添加一个单词或向list1 添加一个字母会增加时间量恒定)。您的版本会为 list1 中的每个字母循环一次 list2,使其成为 O(NM) 算法,从而导致每次添加字母或单词所需的时间呈指数增长。

    要将其转换为数字,如果您扩展 list1 以涵盖所有 26 个 ASCII 大写字母并扩展 list2 以包含 1000 个单词,您的方法(扫描所有 list2 以查找具有给定字母的单词)将使26000步。我的版本,包括预建地图,只需要 2026 步。 list2 包含 100 万字,您的版本必须执行 2600 万步,我的 200 万和 26 步。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-21
      • 2016-08-02
      相关资源
      最近更新 更多