Ответ 1
[x for x in values if x % 2 == 1] + [x for x in values if x % 2 == 0] == values
Это верно, если values
начинается со всех его собственных нечетных значений, за которыми следуют все его четные значения.
Написание некоторых тестовых примеров, и мой ум блуждает, предполагая, что есть лучший способ написать что-то вроде этого. У меня есть список, число его переходов от всех нечетных значений ко всем четным, не имеет значения где. Мне нужно утверждать, что это так, вот что я придумал:
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)
Кажется, это долгий путь. Предложения о том, как это можно уменьшить?
[x for x in values if x % 2 == 1] + [x for x in values if x % 2 == 0] == values
Это верно, если values
начинается со всех его собственных нечетных значений, за которыми следуют все его четные значения.
Возможное решение состоит в том, чтобы считать, что вы разрешаете только
odd->odd
odd->even
even->even
другими словами, единственным запрещенным переходом является
even->odd
и это означает
(0, 1) not in ((x%2, y%2) for x, y in zip(values, values[1:]))
Ну, вам не нужно вычислять evens
:
assert odds == range(len(odds))
Вдохновленный пояснениями и решением от @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:]))
(values[0] % 2) and (len(list(itertools.groupby(values, lambda x: x%2))) == 2)
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)
Вместо сбора индексов вы можете просто вычислить точку перехода на основе предположения, что все нечетные значения находятся в начале; а затем проверьте, что после этой точки перехода больше нечетных значений.
Случай, когда утверждение верно:
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
Я думаю, что filter
читает лучше, чем понимает список здесь, например,
filter(isodd, values) + filter(iseven, values) == values
Другим вариантом является сортировка values
по четности и просмотр изменений:
assert sorted(values, key=lambda x: x % 2, reverse=True) == values
Несколько дольше, но это, похоже, захватывает все (даже только, нечетные, пустые) в дополнение к требованиям. Это требует только одного по модулю и одного сравнения в полном списке. Не так кратким (или умным), как Андрей сортировал ответ, но быстрее (?) Для длинных списков.
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