首页 > 解决方案 > Python - 嵌套在地图内的过滤器会产生意外的输出

问题描述

我有一个文件名(字符串)列表和一组由浮点数组成的 ls 。最初,我想根据预先确定的表达式过滤与 ls 的每个元素匹配的所有文件:我将所有实际上是整数的浮点数转换为整数并将其输入.format以创建适当的搜索字符串 ( exprs)。这会产生预期的字符串序列。我现在想使用 re.search 过滤“文件”,但据我了解,我需要为 exprs 的每个输出使用不同的过滤器。所以我把它嵌套在一个 map 函数中:

t = 'Matrix'
exprs = map('{}_spike_{}_D1_1'.format , cycle([t]) ,(int(x) if x.is_integer() else x for x in ls))
y = map(lambda f:filter(lambda i : re.search(f,i), files), exprs)

Print(next(exprs))产生预期的输出,即'Matrix_spike_50_D1_1'。如果我“冻结” re.search 中的表达式,即通过做b = next(exprs)re.search(b, [...])我得到预期的输出(即文件名,正确选择)。但是当我尝试使用map消耗所有输出exprs并返回结果时filter([...]),我得到了

  1. 过滤器对象而不是地图对象
  2. 两个相同的过滤器对象,当通过 a 彻底运行它时while True,捕获所有StopIterations并恢复

如何修改它以返回过滤每个 exprs 的返回文件?

标签: pythonfilterfunctional-programmingiterator

解决方案


如果我正确理解了您的问题,您将获得一个文件列表,例如:

files = ['a', 'b', 'Matrix_spike_2_D1_1', 'c', 'Matrix_spike_4_D1_1']

和一个浮点数列表,应该是整数(但可能不是全部都是):

ls = [1.1, 2.0, 3.0, 4.0, 5.0]

ls整数列表中,构造名称“Matrix_spike_2_D1_1”、“Matrix_spike_3_D1_1”等,然后从files列表中选择满足rex.search调用的文件。当然,search不使用^$锚的方法不会完全匹配,所以我想知道你是否真的打算使用该fullmatch方法。

首先,你有:

t = 'Matrix'
exprs = map('{}_spike_{}_D1_1'.format , cycle([t]) ,(int(x) if x.is_integer() else x for x in ls))

我相信这被简化为:

exprs = map('Matrix_spike_{}_D1_1'.format, (int(x) for x in ls if x.is_integer())

请注意,我仅从ls中选择整数值,我相信这是您的意图。为了采用您的方法,我相信最简单的补救措施是定义一个函数filter_func

import re


ls = [1.1, 2.0, 3.0, 4.0, 5.0]
files = ['a', 'b', 'Matrix_spike_2_D1_1', 'c', 'Matrix_spike_4_D1_1']
exprs = list(map(re.compile, map('Matrix_spike_{}_D1_1'.format, (int(x) for x in ls if x.is_integer()))))

def filter_func(f):
    for expr in exprs:
        if expr.search(f):
            return True
    return False

matched_files = list(filter(filter_func, files))
print(matched_files)

印刷:

['Matrix_spike_2_D1_1', 'Matrix_spike_4_D1_1']

或者使用更“实用”但效率可能较低的方法:

import re
import operator
import functools


ls = [1.1, 2.0, 3.0, 4.0, 5.0]
files = ['a', 'b', 'Matrix_spike_2_D1_1', 'c', 'Matrix_spike_4_D1_1']
exprs = list(map(re.compile, map('Matrix_spike_{}_D1_1'.format, (int(x) for x in ls if x.is_integer()))))
filter_func = lambda f: functools.reduce(operator.or_, map(lambda expr: bool(expr.search(f)), exprs), False)
matched_files = list(filter(filter_func, files))
print(matched_files)

印刷:

['Matrix_spike_2_D1_1', 'Matrix_spike_4_D1_1']

但我相信你的方法不是最有效的。您应该对files列表的每个元素进行一次正则表达式搜索。在上面的示例中,该正则表达式将是:

rex = re.compile('Matrix_spike_(?:2|3|4|5)_D1_1')

在上面的正则表达式中,您会将files列表中的每个元素与您正在查找的所有 4 个可能的文件名进行匹配。这将代码简化为:

import re


ls = [1.1, 2.0, 3.0, 4.0, 5.0]
files = ['a', 'b', 'Matrix_spike_2_D1_1', 'c', 'Matrix_spike_4_D1_1']
sub_rex = '|'.join(str(int(x)) for x in ls if x.is_integer())
rex = re.compile('Matrix_spike_(?:' +  sub_rex + ')_D1_1');
matched_files = list(filter(rex.search, files))
print(matched_files)

印刷:

['Matrix_spike_2_D1_1', 'Matrix_spike_4_D1_1']

如果您打算对文件名进行完全匹配(相等),那么以下代码将是最有效的,因为它将您要查找的名称添加到集合中,并且每次比较都将是一个常数时间查找:

ls = [1.1, 2.0, 3.0, 4.0, 5.0]
files = ['a', 'b', 'Matrix_spike_2_D1_1', 'c', 'Matrix_spike_4_D1_1']
sought_files = {f'Matrix_spike_{int(x)}_D1_1' for x in ls if x.is_integer()}
matched_files = list(filter(lambda f: f in sought_files, files))
print(matched_files)

推荐阅读