使用map和filter函数的列表推导式等价方式

4
我想使用map和/或filter函数编写此代码。 它返回列表中项目的索引,前提是它们的总和达到目标值。
我已经使用列表推导式来实现这个功能,但不知道如何将第二个for循环嵌入到map/filter函数中。 如果我为map/filter函数的函数参数定义自己的函数,我不确定要使用哪种语法。
num = [2,5,7,11,6,15,3,4]
tgt= 9
[num.index(x) for x in num for y in num if x + y == tgt]

结果:

[0, 1, 2, 4, 6, 7]
4个回答

5

由于filtermap都作用于序列中的单个项目,因此您必须从列表中每个项目的角度来查看逻辑,而不是项目组合的角度。这意味着您需要将列表推导中使用的表达式重新构造为每个单独项目的函数。因此,过滤条件x + y == tgt可以视为x == tgt - y,其中y还必须是num列表中的项目之一,以便您的列表推导可以重写为:

[num.index(x) for x in num if x in {tgt - y for y in num}]

使用同等的列表推导式,很明显要实现筛选条件,需要通过将num中的每个项目映射到其与tgt之间的差异来创建一个集合,这可以使用tgt.__sub__方法完成,然后测试num中的每个项目x是否是该集合的成员,这可以使用集合的__contains__方法完成,最后将过滤后的序列映射到num.index以输出每个匹配项的索引:
list(map(num.index, filter(set(map(tgt.__sub__, num)).__contains__, num)))

这将返回:
[0, 1, 2, 4, 6, 7]

1
使用 set 替代 list(map(tgt.__sub__, num)) 可以获得更好的查找效果。 - Netwave
1
@blhsing,你能解释一下你的解决方案的内部工作原理吗? - Don Cheeto

0
双重循环可以使用 itertools.product 函数编写:
>>> list(map(lambda x: num.index(x[0]), filter(lambda x: sum(x) == tgt, itertools.product(num, num))))
[0, 1, 2, 4, 6, 7]

让我们来剖析代码:

filter(lambda x: sum(x) == tgt, itertools.product(num, num))

itertools.product 返回一个元组迭代器,其中的元素在 num 中,类似于您使用的嵌套循环中的 (x, y)

然后我们过滤那些元组求和等于 tgt 的元素,使用 sum 是更好的选择,但请注意它与 x[0] + x[1] 相同(记住我们将像 (2, 2) 这样的元组传递给该函数)。

一旦我们过滤出剩下的元组,我们对每个元组应用函数 num.index,因为我们有元组,所以我们只需要使用其中一个值,记住第一个值匹配嵌套循环中的 x,因此是 num.index(x[0])


谢谢,你能解释一下内部工作原理吗?为什么要使用缩减函数,比如(sum()和product()),以及num.index(x[0])? - Don Cheeto

0

试试这个!你可以使用 itertools.product 来获取每个组合。然后过滤组合列表,找到其总和为 tgt 的项。接着对这些结果映射一个 lambda 函数,以获取这些组合中第一项的索引。

list(map(lambda x: num.index(x[0]), (filter(lambda x: sum(x) == tgt, itertools.product(num, repeat=2)))))

0

反复调用num.index非常低效。每次找到满足条件的数字时,对index的调用都需要对列表进行顺序扫描。

相反,将循环转换为列表中的索引。通过索引数组进行比较(随机访问),这将更加高效。

正如其他人指出的那样,您可以使用itertools.product,但是不是num与自身的乘积,而是range(len(num))的自我乘积。

使用mapfilter

from operator import itemgetter
from itertools import product

res = map(
    itemgetter(0), 
    filter(
        lambda c: num[c[0]]+num[c[1]] == tgt, 
        product(range(len(num)),range(len(num)))
    )
)
print(list(res))
#[0, 1, 2, 4, 6, 7]

内部的filter正在过滤0到num长度减1之间的所有数字对,其中在相应索引处的num的值等于目标。由于product返回一对索引,您只对第一个值感兴趣,因此使用itemgetter(0)filter的结果与map映射以获取第一个元素。

更紧凑的写法是列表推导式:

[i for i, j in product(range(len(num)), range(len(num))) if num[i] + num[j] == tgt]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接