Есть ли какой-нибудь «полосовой» метод для списка? - PullRequest
4 голосов
/ 28 марта 2019

Метод buildin strip в python может легко удалить подстроку заполнения, которая удовлетворяет пользовательскому условию. например,

"000011110001111000".strip("0")

обрезает нулевой отступ с обеих сторон строки и возвращает 11110001111.

Я хотел бы найти аналогичную функцию для списка. например, для данного списка

input = ["0", "0", "1", "1", "0", "0", "1", "0", "1", "0", "0", "0"]

ожидаемый результат будет

output = ["1", "1", "0", "0", "1", "0", "1"]

Элементы в примере input слишком упрощены, они могут быть любыми другими объектами python .

list comprehension удалит все элементы вместо дополнительных.

[i for i in input if i != "0"]

Ответы [ 5 ]

5 голосов
/ 28 марта 2019

Используйте itertools.dropwhile с обоих концов:

from itertools import dropwhile

input_data = ["0", "0", "1", "1", "0", "0", "1", "0", "1", "0", "0", "0"]

def predicate(x):
    return x == '0'

result = list(dropwhile(predicate, list(dropwhile(predicate, input_data))[::-1]))[::-1]
result

Выход:

['1', '1', '0', '0', '1', '0', '1']
2 голосов
/ 28 марта 2019

Нет метода списка, но нетрудно реализовать такую ​​функцию: отсканируйте нужные индексы, а затем нарезайте их.

def strip_seq(predicate, xs):
    def scan(xs):
        return next((i for i, x in enumerate(xs) if not predicate(x)), 0)
    return xs[scan(xs) : -scan(reversed(xs)) or None]

xs = ["0", "0", "a", "1", "0", "0", "1", "0", "b", "0", "0", "0"]
print(strip_seq(lambda x: x=='0', xs))  # ['a', '1', '0', '0', '1', '0', 'b']

Это должно работать с любыми типами последовательностей, включая строки и кортежи.

1 голос
/ 28 марта 2019

Вы можете разделить правой кнопкой с помощью while / pop.

input = ["0", "0", "1", "1", "0", "0", "1", "0", "1", "0", "0", "0"]
while input and input[-1] == "0": input.pop()

Вы можете удалить левой кнопкой с itertools.dropwhile, но вам, возможно, придется создать новый список.

from itertools import dropwhile
input = [*dropwhile(lambda x: x=='0', input)]

Или вы можете эффективно использовать / pop с обоих концов, преобразовав его в deque.

from collections import deque
input = ["0", "0", "1", "1", "0", "0", "1", "0", "1", "0", "0", "0"]
input = deque(input)
while input and input[-1] == '0': input.pop()
while input and input[0] == '0': input.popleft()

(Также input() уже является встроенной функцией, поэтому лучше не использовать это имя для переменных.)

0 голосов
/ 28 марта 2019

«Базовое» решение приведено ниже. Решения, использующие прямое переключение, обращают ввод, и в некоторых случаях это может быть дорогостоящим.

Приведенное ниже решение вычисляет индикаторы начала и конца и возвращает срез ввода.

Это решение работает для других типов последовательностей, таких как кортеж, в которых возвращаемое значение того же типа, что и входные. Этот метод работает и для строковых входов, хотя str.split(), вероятно, будет быстрее.

def strip_basic(input_data, predicate):
    N = len(input_data)
    if not N:
        return input_data[0:0]      # empty sequence, new copy for mutables
    i = 0
    while i < N and predicate(input_data[i]):
        i = i + 1
    if i == N:                      # optimization
        return input_data[0:0]      # empty sequence
    j = N - 1
    while j and predicate(input_data[j]):
        j = j - 1
    j = j + 1
    return input_data[i:j]

Для сравнения (в этом решении часть ввода переворачивается дважды):

def strip_dropwhile(input_data, predicate):
    return list(dropwhile(predicate, list(dropwhile(predicate, input_data))[::-1]))[::-1]

Вот некоторые числа, использующие timeit. Обратите внимание, что для патологического случая, когда вход большой и нет элементов для В отличие от базового решения, выпадающее решение примерно в 8 раз медленнее. В случае, когда вход большой и все элементы удалены, основное решение заключается в В 1,9 раза медленнее, чем выпадающее решение.

# input len = 12, output len = 7
# predicate = lambda x: x == '0'; input_data = list('001100101000')

# dropwhile solution:
200000 loops, best of 5: 1.84 usec per loop

# basic solution:
200000 loops, best of 5: 1.51 usec per loop
# input len = 1,000,000, output len = 1,000,000
# predicate = lambda x: x == '0'; input_data = ['1'] * 1000000

# dropwhile solution:
10 loops, best of 5: 29.3 msec per loop

# basic solution:
100 loops, best of 5: 3.58 msec per loop
# input len = 1,000,000, output len = 0
# predicate = lambda x: x == '0'; input_data = ['0'] * 1000000

# dropwhile solution:
5 loops, best of 5: 98 msec per loop

# basic solution:
2 loops, best of 5: 183 msec per loop
0 голосов
/ 28 марта 2019

Нет встроенного метода.Вы можете использовать itertools.dropwhile для удаления влево.Обрезка вправо может быть возможна с использованием функции генератора.

import itertools as it
stripleft = list(it.dropwhile(lambda x: x==myitem, inputlist))
...