使用列表理解最佳表达式删除前导零的问题

Posted

技术标签:

【中文标题】使用列表理解最佳表达式删除前导零的问题【英文标题】:Problem removing leading zeros using a list comprehension best expression 【发布时间】:2018-10-11 16:11:13 【问题描述】:

result 是一个数字列表:

result = ['0','5','0','1']

我想删除前导零,使用以下表达式:

result = result[next((i for i, x in enumerate(result)
                if x != 0), len(result)):] or [0]

我的问题有两个:

1.) 我不理解这个表达方式 2.) 表达式本身不起作用:它不会删除任何零(前导或其他)

我希望列表推导能够删除每个零,而不仅仅是前导的。 我也对 len(result)): 表达式的一部分在做什么感到困惑?

【问题讨论】:

next 的第二个参数是可迭代对象耗尽时的默认值。从外观上看,这应该是找到第一个 0 的索引(如果存在),并相应地对列表进行切片。 这不起作用,因为您的项目是字符串,并且条件检查和整数。将其更改为result[next((i for i, x in enumerate(result) if x != '0'), len(result)):] or ['0'],它应该可以工作。 你在哪里找到这个代码? 【参考方案1】:

表达式在列表中查找第一个非零值,返回剩余的列表。底部有更多细节。

分析

但是,您有字符:您的列表中没有整数 0。因此,表达式仅返回原始列表。

解决方案

如果您希望它适用于字符,只需包含所需的引号即可:

result[next((i for i, x in enumerate(result) if x != '0'), len(result)):] or ['0']

这给你:

原码:

>>> result = ['0','5','0','1']
>>> result[next((i for i, x in enumerate(result) if x != 0), len(result)):] or [0]
['0', '5', '0', '1']

适应字符串/字符:

>>> result[next((i for i, x in enumerate(result) if x != '0'), len(result)):] or ['0']
['5', '0', '1']
>>> result = ['0','0','5','0','1']
>>> result[next((i for i, x in enumerate(result) if x != '0'), len(result)):] or ['0']
['5', '0', '1']
>>> result = ['7', '0','0','5','0','1']
>>> result[next((i for i, x in enumerate(result) if x != '0'), len(result)):] or ['0']
['7', '0', '0', '5', '0', '1']

表达逻辑

关键从句位于理解的最深处:

(i for
    i, x in enumerate(result)
        if x != '0')

enumerate 子句中,i, x 遍历列表索引及其内容:(0, '0')(1, '5') 等。

if 检查零个字符;它过滤掉所有零。在内部子句找到非零值之前,i for 部分不会获得任何 i 值。

那个点,外部next 运算符接管:它采用非零的第一个索引并将其返回给切片运算符(注意末尾的冒号),这返回列表的其余部分。

正如pault 已经提到的,末尾的len 是默认值:如果您的零过滤器取消了整个列表的资格(全为零),那么理解将len(result) 返回到next。这意味着 next 将返回列表长度,而您的 result 切片返回一个空列表。 现在你的 or 表达式在第一个操作数中有一个“Falsy”值;它返回第二个操作数,列表['0']

【讨论】:

【参考方案2】:

这是你用过的!

>>> result = ['0','5','0','1']
>>> result[next((i for i, x in enumerate(result) if x != 0), len(result)):] or [0]
['0', '5', '0', '1']

让我们分解你的列表理解!

[i for i,x in enumarate(result)]enumerate(result) 解包为 i 和 x。也就是说,

>>> enumerate(result)
<enumerate object at 0x7fc81d12bdc0>
>>> list(enumerate(result))
[(0, '0'), (1, '5'), (2, '0'), (3, '1')]
>>> for i,x in enumerate(result):
...     print i,x
...
0 0
1 5
2 0
3 1

用非零值过滤结果,

>>> [i for i, x in enumerate(result) if x != 0]
[0, 1, 2, 3]

现在看看用x比较字符串和整数的区别

>>> [i for i, x in enumerate(result) if x != '0'] #list comprehension 
[1, 3]

>>> len(result)
4

下一个(迭代器[,默认])

从迭代器返回下一项。如果给出默认值并且迭代器 用尽,它被返回而不是引发 StopIteration。

也就是说,

>>> next([i for i, x in enumerate(result) if x != 0], len(result)) # next takes first argument as iterator, in this example we've given a list, so exception is raised
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list object is not an iterator
>>> next((i for i, x in enumerate(result) if x != '0'), len(result)) #comparing with string '0' 
1

现在,我们得到了列表中第一个具有非零值的索引(我们将其设为 nzindex)。

有了这个索引,我们就用string slicing 来获取结果之后的所有元素[nzindex:]

这将返回索引之后直到结束的所有元素。

>>> result[next((i for i, x in enumerate(result) if x != '0'), len(result)):]
['5', '0', '1']

如果字符串切片返回空列表(falsey结果),则返回默认值result[0]!

>>> result = ['0']
>>> result[next((i for i, x in enumerate(result) if x != '0'), len(result)):] or result[0]
['0']

话虽如此,如果您想删除列表中的所有零

>>> result = ['0','5','0','1']
>>> [x for i,x in enumerate(result) if x!='0']
['5', '1']

如果您想只删除前导零,一种更有效的方法是,

>>> next(i for i,x in enumerate(result) if x!='0')
1
>>> result[next(i for i,x in enumerate(result) if x!='0'):]
['5', '0', '1']

【讨论】:

以上是关于使用列表理解最佳表达式删除前导零的问题的主要内容,如果未能解决你的问题,请参考以下文章

从 QString 中删除前导零的最佳方法

获取具有非前导零的数字(包括“ - ”)

删除 perl 字符串中所有前导零的最优雅和最快的方法

使用 C# 从 IP 地址中删除前导零

不带前导零的日期时间对象

java_二进制的前导的零