【问题标题】:python--recursive list comprehension to apply lstrip on a listpython--递归列表推导在列表上应用 lstrip
【发布时间】:2016-05-17 17:39:03
【问题描述】:

我有两个列表如下

f = ['sum_','count_','per_']
d = ['fav_genre','sum_fav_event','count_fav_type','per_fav_movie']

所以我想将 f 中每个字符串的 lstrip 应用于列表 d 的所有项目,以便我可以得到

d = ['fav_genre','fav_event','fav_type','fav_movie']

我想使用列表理解来做到这一点。 但我知道我也可以通过其他方式做到这一点,比如使用 re.sub,每次对 d 的列表项应用替换

 #example
 d = [re.sub(r'.*fav', 'fav', x) for x in d] #####gives what i want
 ## but if fav (which in this case a matching pattern) is not there in d then this solution won't work
 ## d = ['fav_genre','sum_any_event','count_some_type','per_all_movie']
 #re.sub can't be applied on this d(as before) as no matching char like 'fav' found 

所以列表压缩是我选择做的..

到目前为止我已经尝试过..

d_one = [x.lstrip('count_') for x in d]   ###only count_ is stripped
# o/p- d-one = ['fav_genre', 'sum_fav_event', 'fav_type', 'per_fav_movie']
# so i c_n apply lstrip of each string from f on items of d
## why not apply all items lstrip in one go ### so tried
d_new = [x.lstrip(y) for y in f for x in d]
###['fav_genre', 'fav_event', 'count_fav_type', 'per_fav_movie', 'fav_genre', 'sum_fav_event', 'fav_type', 'per_fav_movie', 'fav_genre', 'sum_fav_event', 'count_fav_type', 'fav_movie']

所以它给了我每次应用 lstrip 迭代的结果

请建议我如何在列表理解中一次性应用所有 lstrip(递归)。提前致谢。

【问题讨论】:

  • 我不认为你真的想在这里使用lstriplstrip 不会像您想要的那样删除字符串前缀,而是从参数中删除由任何字符组成的前缀。因此,如果您尝试从"notable" 中删除'count_',您将得到"able"。我不认为你想要那个。
  • 你能解释一下为什么它必须是递归的吗?这是一些扭曲的任务吗?如果它必须是递归的,那么你可以期望的最好的就是一个只有一行的函数。
  • @timgeb-yes,这里是递归,我的意思是,当我得到 lstrip 的每个迭代结果应用(结果)时,为什么不在第一个 lstrip 结果上应用第二个 lstrip,直到 f 的最后一项...因此递归调用列表 f 以获取项目并将该项目的 lstrip 应用于列表 d 每次。
  • @TomKarzes-你能解释一下我是否在 'count_notable_authors' 上应用 lstrip('count_') .. 为什么之前不能也被替换。但在其他情况下,'count_xyz_author' 它只会删除count_'并保持'xyz-author'..刚开始研究python,所以没有深入的概念想法..请回答为什么会这样??
  • 因为lstrip 不会做你认为的事情。如果你给它一个前缀'count_',它只是一组字符,和'ntc_uo'没有区别。它是无序的。它将继续从字符串的前面删除任何这些字符,直到遇到不在集合中的字符。

标签: python regex list recursion list-comprehension


【解决方案1】:

试试这个:

>>> f = ['sum_','count_','per_']
>>> d = ['fav_genre','sum_fav_event','count_fav_type','per_fav_movie']
>>> [s[len(([p for p in f if s.startswith(p)]+[""])[0]):] for s in d]
['fav_genre', 'fav_event', 'fav_type', 'fav_movie']

我相信这可以按预期处理所有情况。

【讨论】:

  • -非常感谢!!!你让我很开心(正如我与某人争辩说这可以通过列表理解来完成,你应该完全值得称赞让我能够信守诺言)......
【解决方案2】:

可以使用以下方法,根据f创建一个合适的正则表达式:

import re

f = ['sum_','count_','per_']
d = ['fav_genre','sum_fav_event','count_fav_type','per_fav_movie']

re_prefix = re.compile(r'^({})'.format('|'.join(f)))
print [re_prefix.sub('', entry) for entry in d]

或作为单线(效率不高):

print [re.sub(r'^({})'.format('|'.join(f)), '', entry) for entry in d]

给你以下输出:

['fav_genre', 'fav_event', 'fav_type', 'fav_movie']

【讨论】:

  • @Martian Evans-确实是一个更好的人,但我必须接受汤姆的回答,因为我不想侮辱我的问题标题......
【解决方案3】:

这就是你要找的东西?

>>> f = ['sum_','count_','per_']
>>> d = ['fav_genre','sum_fav_event','count_fav_type','per_fav_movie']
>>> [x[len(y):] for x in d for y in f if x.startswith(y)]
['fav_event', 'fav_type', 'fav_movie']

编辑: 我对此戳得越多,我就越发现列表理解是不可能的。问题似乎包括不匹配的条件,但是一个简单的 'else' 会导致在迭代 f 中的其他项目时包含 d 中的每个项目。

例如

>>> [x[len(y):] if x.startswith(y) else x for x in d for y in f
['fav_genre', 'fav_genre', 'fav_genre', 'fav_event', 'sum_fav_event', 'sum_fav_event', 'count_fav_type', 'fav_type', 'count_fav_type', 'per_fav_movie', 'per_fav_movie', 'fav_movie']

这会创建一个包含太多项目的新列表。

向列表 comp 添加另一个条件会产生语法错误:

[x[len(y):] if x.startswith(y) else x if x[len(y):] not in f for x in d for y in f]
File "<stdin>", line 1
  [x[len(y):] if x.startswith(y) else x if x[len(y):] not in f for x in d for y in f]
                                                               ^
SyntaxError: invalid syntax

即使我们可以通过列表推导得到它,函数的可读性也会更高:

def strip_prefixes(prefixes, mylist):
    for element in mylist:
        for x in prefixes:
            if element.startswith(x):
                element = element[len(x):]
    return mylist

【讨论】:

  • @Willnx-您在输出中也缺少 fav_genre...这不是开头(来自 f 的项目)...
  • 好点 Satya,当我第一次看到这个解决方案时我没有注意到,但你当然是对的。
  • 您可以将列表推导的结果存储在一个集合中,但据您了解,列表推导不适合这项工作。至少不适合“修改”列出条目的输出。为此,您应该使用一个非常适合的函数来转换输出。理解的 for 部分用于合并,if 部分用于归约。
【解决方案4】:

我要睡觉了,但正在处理这个问题。我认为这样做可能不是最好的主意,因为它有很多循环并且不那么可读。也不太对。

d_new = set([(x,y) for x in [x.split(y)[1] for y in f for x in d if x.startswith(y)] for y in [x for x in d if x.startswith('fav')]])

它目前将它们放入元组中,您可以在集合中为 x 添加另一个 x 以提取不同的元组对。在这一点上,虽然我什至不认为使用列表解析有用或不值得,但如果你真的想使用它,这可能会给你一个开始。

编辑:

代码如下所示:

[('fav_movie', 'fav_genre'), ('fav_event', 'fav_genre'), ('fav_type', 'fav_genre')]

【讨论】:

    【解决方案5】:

    不要用列表理解来做这件事。如果很像 map/reduce 语法糖,那么列表理解。通过使用简单的函数,您将获得更易于阅读的解决方案。

    import re
    
    f = ['sum_','count_','per_']
    d = ['fav_genre','sum_fav_event','count_fav_type','per_fav_movie']
    def makeTrimmer(patterns):
        regex = re.compile("^(%s)" % "|".join(patterns))
    
        def trimmer(string):
    
            old_string = string             
            new_string = re.sub(regex, "", old_string)
    
            while len(old_string) != len(new_string):
                old_string = new_string
                new_string = re.sub(regex, "", old_string)
    
            return new_string
    
        return trimmer
    
    trimmer = makeTrimmer(f)
    vals = [trimmer(x) for x in d]
    print vals
    

    如您所见,trimmer 函数的可读性很强,您可以在列表推导中做到这一点,但没有简单的方法可以做到这一点。因为列表推导的 if 部分的工作方式非常类似于列表中要输出的内容的过滤器。 for 部分是组合条目,第一部分是构建条目输出。在您的情况下,您只需要基于多个前缀构建正确的输出......换句话说,您不会尝试将所有前缀与所有值组合到多个输出中,也不会过滤任何结果。

    我的方法可能可以用 lambdas 实现,但那很可能是丑陋的。

    没有 lambda 的非递归方法:

    vals = [
        re.sub(re.compile("^(%s)" % "|".join(f)), "", x)
        for x in d
    ]                      
    print vals
    

    这是使用匿名 lambda 的完整递归:

    # -*- coding: utf-8 -*-
    import re
    
    f = ['sum_','count_','per_']
    d = ['fav_genre','sum_fav_event','count_fav_type','per_fav_movie']
    
    vals = [
        (lambda a, *b: a(a, *b))(
            (lambda loop, newstring, oldstring:
                newstring
                if len(newstring) == len(oldstring) else
                    loop(
                        loop,
                        newstring,
                        re.sub(re.compile("^(%s)" % "|".join(f)), "", x)
                    )
            ),
            re.sub(re.compile("^(%s)" % "|".join(f)), "", x),
            x
        )
        for x in d
    ]
    
    print vals
    

    这与上述方法几乎相同,只是我们使用递归方法来调用进一步过滤,因此该方法清除了 sum_count_per_fun_avgfun_avg 之类的内容。

    另外,不要使用 lambda 方法,效率低。

    但这里有一个更高效的 lambda 版本:

    vals = [
        (lambda regex:
            (lambda a, *b: a(a, *b))(
                (lambda loop, newstring, oldstring:
                    newstring
                    if len(newstring) == len(oldstring) else
                        loop(                                                                                                                   
                            loop,
                            newstring,
                            re.sub(regex, "", x)
                        )
                ),
                re.sub(regex, "", x),
                x
            )
        )(re.compile("^(%s)" % "|".join(f)))
        for x in d
    ]
    

    我们只编译一次正则表达式。但是python中的递归仍然是一个问题,所以你不应该过多地使用递归。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-02-13
      • 1970-01-01
      • 2010-11-07
      • 1970-01-01
      • 2020-02-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多