python元组仅使用更高阶函数删除第一个匹配外观

时间:2018-02-13 08:59:29

标签: python tuples

我想写一个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)

5 个答案:

答案 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

使用takewhilefilterlambda所有这些解决方案都会让我感到非常糟糕......

虽然:

>>> 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

几乎是生成器表达式的两倍,这表明,生成器开销可能非常大。但是,takewhiledropwhile在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)

以下解决方案仅使用lambdafilter()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)