我想写一个Rem(a, b)
,它返回一个类似a
的新元组,并删除元素b
的第一个外观。例如
Rem((0, 1, 9, 1, 4), 1)
将返回(0, 9, 1, 4)
。
我只允许使用更高阶的函数,例如lambda,filter,map和reduce。
我正在考虑使用过滤器,但这会删除所有匹配元素
def myRem(T, E):
return tuple(filter(lambda x: (x!=E), T))
myRem((0, 1, 9, 1, 4), 1)
我将(0,9,4)
答案 0 :(得分:6)
以下作品(警告: hacky代码):
tuple(map(lambda y: y[1], filter(lambda x: (x[0]!=T.index(E)), enumerate(T))))
但除非要求严格,否则我绝不会建议这样做
答案 1 :(得分:3)
使用临时列表进行操作:
def removeFirst(t, v):
tmp_lst = [v]
return tuple(filter(lambda x: (x != v or (not tmp_lst or v != tmp_lst.pop(0))), t))
print(removeFirst((0, 1, 9, 1, 4), 1))
tmp_lst.pop(0)
- 只会被调用一次(因此,排除第一次出现的关键值v
)not tmp_lst
- 由于这种情况,将包括所有剩余/潜在事件输出:
(0, 9, 1, 4)
答案 2 :(得分:1)
为了好玩,使用itertools,你可以排序主要使用更高阶的函数......
>>> from itertools import *
>>> data = (0, 1, 9, 1, 4)
>>> not1 = (1).__ne__
>>> tuple(chain(takewhile(not1, data), islice(dropwhile(not1, data), 1, None)))
(0, 9, 1, 4)
顺便说一下,这里有一些时间比较了在元组中删除特定索引的不同方法:
>>> timeit.timeit("t[:i] + t[i+1:]", "t = tuple(range(100000)); i=50000", number=10000)
10.42419078599778
>>> timeit.timeit("(*t[:i], *t[i+1:])", "t = tuple(range(100000)); i=50000", number=10000)
20.06185237201862
>>> timeit.timeit("(*islice(t,None, i), *islice(t, i+1, None))", "t = tuple(range(100000)); i=50000; from itertools import islice", number=10000)
>>> timeit.timeit("tuple(chain(islice(t,None, i), islice(t, i+1, None)))", "t = tuple(range(100000)); i=50000; from itertools import islice, chain", number=10000)
19.71128663700074
>>> timeit.timeit("it = iter(t); tuple(chain(islice(it,None, i), islice(it, 1, None)))", "t = tuple(range(100000)); i=50000; from itertools import islice, chain", number=10000)
17.6895881179953
看起来很难打败直截了当的t[:i] + t[i+1:]
,这并不奇怪。
请注意,这个效果令人震惊:
>>> timeit.timeit("tuple(j for i, j in enumerate(t) if i != idx)", "t = tuple(range(100000)); idx=50000", number=10000)
111.66658291200292
使用takewhile
,filter
和lambda
所有这些解决方案都会让我感到非常糟糕......
虽然:
>>> timeit.timeit("not1 = (i).__ne__; tuple(chain(takewhile(not1, t), islice(dropwhile(not1, t), 1, None)))", "t = tuple(range(100000)); i=50000; from itertools import chain, takewhile,dropwhile, islice", number=10000)
62.22159145199112
几乎是生成器表达式的两倍,这表明,生成器开销可能非常大。但是,takewhile
和dropwhile
在C中实现,尽管此实现具有冗余(take-while和dropwhile会两次通过dropwhile
区域)。
另一个有趣的观察,如果我们简单地将替换为list-comp包装为生成器表达式,它明显更快尽管list-comprehension + tuple调用迭代结果两次与一次与生成器表达式相比:
>>> timeit.timeit("tuple([j for i, j in enumerate(t) if i != idx])", "t = tuple(range(100000)); idx=50000", number=10000)
82.59887028901721
显示生成器表达价格有多陡峭......
答案 3 :(得分:1)
以下解决方案仅使用lambda
,filter()
,map()
,reduce()
和tuple()
。
def myRem(T, E):
# map the tuple into a list of tuples (value, indicator)
M = map(lambda x: [(x, 1)] if x == E else [(x,0)], T)
# make the indicator 0 once the first instance of E is found
# think of this as a boolean mask of items to remove
# here the second reduce can be changed to the sum function
R = reduce(
lambda x, y: x + (y if reduce(lambda a, b: a+b, map(lambda z: z[1], x)) < 1
else [(y[0][0], 0)]),
M
)
# filter the reduced output based on the indicator
F = filter(lambda x: x[1]==0, R)
# map the output back to the desired format
O = map(lambda x: x[0], F)
return tuple(O)
<强>解释强>
了解正在发生的事情的一个好方法是打印中间步骤的输出。
第1步:第一张地图
对于元组中的每个值,我们返回一个带有值的元组和一个标志,以指示它是否是要删除的值。这些元组被封装在一个列表中,因为它使下一步的组合更容易。
# original example
T = (0, 1, 9, 1, 4)
E = 1
M = map(lambda x: [(x, 1)] if x == E else [(x,0)], T)
print(M)
#[[(0, 0)], [(1, 1)], [(9, 0)], [(1, 1)], [(4, 0)]]
第2步:减少
这将返回与M
内容类似的结构的元组列表,但是1
的第一个实例的标志变量设置为E
,{{1对于所有后续实例。这是通过计算到该点的指标总和(实现为另一个0
)来实现的。
reduce()
现在输出的格式为R = reduce(
lambda x, y: x + (y if reduce(lambda a, b: a+b, map(lambda z: z[1], x)) < 1
else [(y[0][0], 0)]),
M
)
print(R)
#[(0, 0), (1, 1), (9, 0), (1, 0), (4, 0)]
。
第3步:过滤
过滤掉要删除的值。
(value, to_be_removed)
第4步:第二张地图并转换为元组
从筛选列表中提取值,并将其转换为元组。
F = filter(lambda x: x[1]==0, R)
print(F)
#[(0, 0), (9, 0), (1, 0), (4, 0)]
答案 4 :(得分:0)
这违反了您对“仅使用高阶函数”的要求 - 但由于为什么这是一项要求,我不明白这一点,我提供了以下解决方案。
def myRem(tup, n):
idx = tup.index(n)
return tuple(j for i, j in enumerate(tup) if i != idx)
myRem((0, 1, 9, 1, 4), 1)
# (0, 9, 1, 4)
这是一个numpy
解决方案(仍未使用高阶函数):
import numpy as np
def myRem(tup, n):
tup_arr = np.array(tup)
return tuple(np.delete(tup_arr, np.min(np.nonzero(tup_arr == n)[0])))
myRem((0, 1, 9, 1, 4), 1)
# (0, 9, 1, 4)