【问题标题】:List Comprehension Append Odds Twice Evens Once列表理解追加赔率两次偶数一次
【发布时间】:2019-08-20 13:16:39
【问题描述】:

我在学习列表理解的过程中,偶然发现了一种我无法找到充分理解的资源的问题。

问题源于以下问题: 我们有一个数组 [1,2,3,8,9] 并希望创建一个表达式,它将每个奇数返回两次,而偶数只返回一次。

注意:还有一个提示是我可以创建嵌套列表,但到目前为止还没有帮助我确定这将如何为我服务。

相应算法的输出应该是:[1,1,2,3,3,8,9,9]

使用循环,我可以像这样做我想做的事:

OtherNumList = [1, 2, 3, 8, 9]
OtherNumList2 = []
for i in OtherNumList:
    if i%2==1:
        OtherNumList2.append(i)
        OtherNumList2.append(i)
    else:
        OtherNumList2.append(i)
print(OtherNumList2)

我想只使用一个表达式来做到这一点,或者使用列表推导“单行”它。

我正在努力理解如何将理解设置为如果 X 则追加两次,而如果 Y 则追加一次。

感谢您对理解的帮助,即使只是构建理解的概念;我不期望一个勺子喂养的解决方案,如果你能引导我完成你的思考过程,我更喜欢它,这样我就可以更好地为将来更好的列表理解奠定自己的基础! :)

【问题讨论】:

    标签: python arrays list list-comprehension


    【解决方案1】:

    您可以在没有外部工具的情况下在单个列表理解中执行此操作。您只需要根据从外部序列中提取的值来创建和遍历内部值序列:

    OtherNumList = [1, 2, 3, 8, 9]
    OtherNumList2 = [rep for i in OtherNumList for rep in (i,)*(i%2+1)]
    print(OtherNumList2)
    

    这里的窍门是第二个for。根据i 是偶数(一个副本)还是奇数(两个副本),它会迭代一个或两个i 副本的tuple。方便的是,我们在这里甚至不需要真正的布尔检查; (i%2+1) 总是 1 表示偶数,2 表示奇数,所以我们可以使用它直接乘法。然后直接生成正确次数的结果值,而无需额外的展平。

    【讨论】:

    • 你正在使用两个循环:O
    • @Prerit:是的,虽然内部循环保证精确运行 1 或 2 次,所以它不会影响 big-O 运行时间。列表推导可以在其中包含任意数量的循环,最左边的循环循环最慢,而最右边的循环循环最快。不涉及单独的生成器函数或显式 appends 的任何解决方案都无法避免第二个循环(即使对于那些,它基本上只是一个展开的循环,实际上来说);它可能隐藏在chain 或类似的地方,但它会发生。
    • 感谢您,我从没想过以这种方式使用它。 :)
    • @Prerit:是的。您的解决方案可以使用list(itertools.chain.from_iterable([value]*(value%2+1) for value in x)) 使第二个循环隐含,而chain.from_iterable 为您进行展平,但它仍然是一个“内部循环”,只是在C 级别的chain 内完成一个,而不是用for 完成在 Python 级别,您可以看到它。注意:我选择乘以tuple 而不是list,因为Python 可以对tuple 应用一些不适用于list 的优化。缺点是需要尾随逗号,但在这里速度更快。
    • 我正在努力想出一个更简单的答案。使用模数确定加倍的技巧很巧妙。做得很好。
    【解决方案2】:

    一种方法是生成一个嵌套列表,然后使用例如itertools.chain 将其展平。棘手的部分是立即创建一个平面列表,因为当条件不满足时,您必须一次附加多个元素,因此您需要一些额外的工作来平面化结果列表:

    from itertools import chain
    list(chain.from_iterable([i] if i%2 == 0 else [i]*2 for i in l))
    

    输出

    [1, 1, 2, 3, 3, 8, 9, 9]
    

    虽然在我看来,执行此操作的最佳方法是使用生成器函数,或者非常类似地,您共享的那个,但对于大型列表可能更可取:

    def my_fun(l):
        for i in l:
            if i%2 == 0:
                yield i
            else:
                yield i
                yield i
    
    list(my_fun(l))
    # [1, 1, 2, 3, 3, 8, 9, 9]
    

    【讨论】:

    • 我建议更改为chain.from_iterable([i] if i%2 == 0 else [i]*2 for i in l),这样可以避免同时创建许多临时的lists(将列表理解解压缩为chain需要创建整个l,并且解压到 tuplelists 之前,任何一个都可以链接)。
    • True @ShadowRanger,感谢您的建议,更新了答案
    • 还有 repeat(i, i%2 + 1) 来代替 [i] if i%2 == 0 else [i] *2,但这可能是后天习得的 :) (list(chain.from_iterable(repeat(i, i%2+1) for i in l)))
    • 我有时觉得chainitertools 模块提供的最被低估的东西之一。
    • 感谢您的回答;没有提到我想在没有其他“工具”(如 itertools)的情况下做到这一点是我的错误,但你教会了我一些新东西,所以我只能感激不尽!
    【解决方案3】:
    import numpy as np
    num_list = [1, 2, 3, 8, 9]
    new_list = []
    
    for x in num_list:
        new_list.extend(np.repeat(x, 2, axis=0)) if x%2 == 1 else new_list.append(x)
    

    【讨论】:

    • 感谢 Emma :),尽管正如我对其他(由于我的错误)使用其他库给出答案的人所说的那样,我应该提到我想在没有帮助的情况下实现这一目标其他工具/库。尽管如此,这也是实现我想要的东西的另一种方式,因此在教学时刻受到赞誉:)
    【解决方案4】:

    一个想法是首先给出一个列表列表,就像给你的提示所说的那样。

    nested_list = [[i] if i % 2 == 0 else [i] * 2 for i in NumList] 
    

    这将为您提供以下信息:

    [[1, 1,], [2], [3, 3], [8], [9, 9]]
    

    现在您只需要将这个数组展平在同一行。为此,我参考这里的最佳答案:How to make a flat list out of list of lists?

    【讨论】:

    • 我相信嵌套 lints 的提示点在这里,但我突然想到,如果我们需要用第二个动作展平嵌套列表,它不能是一行(完全)。我想,因为这是一个相当“初级”的练习练习,所以我最好学习多种可能的方法来达到相同的结果。感谢您抽出宝贵时间做出贡献:)
    【解决方案5】:

    我相信作者在提示中使用嵌套列表的意思。

    x = [1, 2, 3, 8, 9]
    [[value]*(value%2+1) for value in x]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-06
      • 1970-01-01
      • 2021-11-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多