Подтвердить что ты не робот

Питонический метод определения того, изменяется ли содержимое списка от нечетных до четных значений

Написание некоторых тестовых примеров, и мой ум блуждает, предполагая, что есть лучший способ написать что-то вроде этого. У меня есть список, число его переходов от всех нечетных значений ко всем четным, не имеет значения где. Мне нужно утверждать, что это так, вот что я придумал:

values = [1, 3, 5, 7, 5, 3, 5, 3, 5, 7, 4, 6, 8, 4, 2, 2, 8, 6]

# find all the indexes of odd and even values
odds = [i for (i, v) in enumerate(values) if v % 2 == 1]
evens = [i for (i, v) in enumerate(values) if v % 2 == 0]

# indexes should be a continuous sequence: 0, 1, 2, 3 ... n
assert odds + evens == range(evens[-1] + 1)

Кажется, это долгий путь. Предложения о том, как это можно уменьшить?

4b9b3361

Ответ 1

[x for x in values if x % 2 == 1] + [x for x in values if x % 2 == 0] == values

Это верно, если values начинается со всех его собственных нечетных значений, за которыми следуют все его четные значения.

Ответ 2

Возможное решение состоит в том, чтобы считать, что вы разрешаете только

odd->odd
odd->even
even->even

другими словами, единственным запрещенным переходом является

even->odd

и это означает

(0, 1) not in ((x%2, y%2) for x, y in zip(values, values[1:]))

Ответ 3

Ну, вам не нужно вычислять evens:

assert odds == range(len(odds))

Ответ 4

Вдохновленный пояснениями и решением от @6502, этот подход генератора использует any() для короткого замыкания итерации, как только тест выходит из строя, и только сбой, если обнаружен четный или нечетный переход. Наихудшая производительность - одна полная итерация, если тест проходит:

iter_val = iter(values)
assert not any(next(iter_val)%2 < v%2 for v in values[1:])

или

from itertools import izip
assert not any(i[0]%2 < i[1]%2 for i in izip(vals, vals[1:]))

Ответ 5

(values[0] % 2) and (len(list(itertools.groupby(values, lambda x: x%2))) == 2)

Ответ 6

assert zip(*itertools.groupby(x%2 for x in values))[0] == (1, 0)

Или проще понять двухстрочный:

odds_and_evens = [x%2 for x in values]
assert odds_and_evens.index(0) == odds_and_evens.count(1)

Если values допустимо, то odds_and_evens будет некоторое число 1, за которым следует только 0, поэтому он действителен, если первый 0 появляется после каждого 1.

Оба эти метода предполагают, что вам нужно иметь хотя бы одну нечетную последовательность, за которой следует хотя бы одно четное, что я не думаю, что ОП разъяснил.

Если пустые списки, все нечетные или все равно должны считаться действительными, работает следующий метод:

odds_and_evens = [x%2 for x in values]
assert odds_and_evens == sorted(odds_and_evens, reverse=True)

Ответ 7

Вместо сбора индексов вы можете просто вычислить точку перехода на основе предположения, что все нечетные значения находятся в начале; а затем проверьте, что после этой точки перехода больше нечетных значений.

Случай, когда утверждение верно:

values = [1, 3, 5, 7, 5, 3, 5, 3, 5, 7, 4, 6, 8, 4, 2, 2, 8, 6]
odd_count = len([x for x in values if (x % 2)])
assert (not any(x for x in values[odd_count:] if (x % 2) != 0))

Случай, когда утверждение ложно:

values = [1, 3, 5, 7, 5, 3, 5, 3, 44, 5, 7, 4, 6, 8, 4, 2, 2, 8, 6]
odd_count = len([x for x in values if (x % 2)])
assert (not any(x for x in values[odd_count:] if (x % 2) != 0))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

Ответ 8

Я думаю, что filter читает лучше, чем понимает список здесь, например,

filter(isodd, values) + filter(iseven, values) == values

Ответ 9

Другим вариантом является сортировка values по четности и просмотр изменений:

assert sorted(values, key=lambda x: x % 2, reverse=True) == values

Ответ 10

Несколько дольше, но это, похоже, захватывает все (даже только, нечетные, пустые) в дополнение к требованиям. Это требует только одного по модулю и одного сравнения в полном списке. Не так кратким (или умным), как Андрей сортировал ответ, но быстрее (?) Для длинных списков.

values= [1, 3, 5, 7, 5, 3, 5, 3, 5, 7, 2, 4, 6, 8, 10]
evenOdd = [x%2 for x in values]

try:
    evenLoc=evenOdd.index(0)
    assert evenLoc != 0
except ValueError:
    evenLoc=len(evenOdd)

try:
    badActor=evenOdd[evenLoc:].index(1)
    assert False 
except ValueError:
    pass