эффективно проверить, что строка состоит из одного символа в Python



каков эффективный способ проверить, что строка s в Python состоит всего из одного символа, скажем 'A'? Что-то вроде all_equal(s, 'A') что будет вести себя так:



all_equal("AAAAA", "A") = True

all_equal("AAAAAAAAAAA", "A") = True

all_equal("AAAAAfAAAAA", "A") = False


два, казалось бы, неэффективных способа: во-первых, преобразовать строку в список и проверить каждый элемент, или во-вторых, использовать регулярное выражение. Существуют ли более эффективные способы или это лучшее, что можно сделать в Python? Спасибо.

1043   8  

8 ответов:

это самый быстрый, в несколько раз быстрее, чем даже count(), просто время это с тем, что отлично набор времени мгилсона:

s == len(s) * s[0]

здесь вся проверка выполняется внутри кода Python C, который просто:

  • выделяет символы len(s);
  • заполняет пробел первым символом;
  • сравнивает две строки.

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

как мы видим из результатов синхронизации, как правило, самые быстрые способы решения задачи не выполняют никакого кода Python для каждого символа. Тем не менее,set() решение также выполняет всю работу внутри кода C библиотеки Python, но оно все еще медленное, вероятно, из-за операционной строки через интерфейс объекта Python.

UPD: Относительно пустого строкового случая. Что с ним делать сильно зависит от поставленной задачи. Если задача "проверить, все ли символы в строке одинаковы",s == len(s) * s[0] является допустимым ответом (нет символов означает ошибку, и исключение в порядке). Если задача "проверить, есть ли ровно один уникальный символ", пустая строка должна дать нам False, и ответ s and s == len(s) * s[0] или bool(s) and s == len(s) * s[0] если вы предпочитаете получать логические значения. Наконец, если мы понимаем задачу как "проверить, нет ли разных символов", результат для пустой строки верно, и ответ not s or s == len(s) * s[0].

>>> s = 'AAAAAAAAAAAAAAAAAAA'
>>> s.count(s[0]) == len(s)
True

это не короткое замыкание. Версия, которая делает короткое замыкание будет:

>>> all(x == s[0] for x in s)
True

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


вот простой timeit скрипт для проверки некоторых других параметров, опубликованных:

import timeit
import re

def test_regex(s,regex=re.compile(r'^(.)*$')):
    return bool(regex.match(s))

def test_all(s):
    return all(x == s[0] for x in s)

def test_count(s):
    return s.count(s[0]) == len(s)

def test_set(s):
    return len(set(s)) == 1

def test_replace(s):
    return not s.replace(s[0],'')

def test_translate(s):
    return not s.translate(None,s[0])

def test_strmul(s):
    return s == s[0]*len(s)

tests = ('test_all','test_count','test_set','test_replace','test_translate','test_strmul','test_regex')

print "WITH ALL EQUAL"
for test in tests:
    print test, timeit.timeit('%s(s)'%test,'from __main__ import %s; s="AAAAAAAAAAAAAAAAA"'%test)
    if globals()[test]("AAAAAAAAAAAAAAAAA") != True:
        print globals()[test]("AAAAAAAAAAAAAAAAA")
        raise AssertionError

print
print "WITH FIRST NON-EQUAL"
for test in tests:
    print test, timeit.timeit('%s(s)'%test,'from __main__ import %s; s="FAAAAAAAAAAAAAAAA"'%test)
    if globals()[test]("FAAAAAAAAAAAAAAAA") != False:
        print globals()[test]("FAAAAAAAAAAAAAAAA")
        raise AssertionError

на моей машине (OS-X 10.5.8, core2duo, python2. 7. 3) с этими надуманные (короткие) строки,str.count курит set и all, и бьет str.replace немного, но обрезается str.translate и strmul в настоящее время лидирует с хорошим отрывом:

WITH ALL EQUAL
test_all 5.83863711357
test_count 0.947771072388
test_set 2.01028490067
test_replace 1.24682998657
test_translate 0.941282987595
test_strmul 0.629556179047
test_regex 2.52913498878

WITH FIRST NON-EQUAL
test_all 2.41147494316
test_count 0.942595005035
test_set 2.00480484962
test_replace 0.960338115692
test_translate 0.924381017685
test_strmul 0.622269153595
test_regex 1.36632800102

тайминги могут быть немного (или даже значительно?) разные между разными системами и с разными строками, так что это стоило бы изучить с фактической строкой, которую вы планируете передать.

в конце концов, если вы попали в лучшем случае на all достаточно, и ваши строки достаточно длинные, вы можете рассмотреть этот вопрос. Это лучший алгоритм ... Я бы избегал set решение, хотя, как я не вижу ни одного случая, когда он мог бы выбить count решение.

если память может быть проблемой, вам нужно будет избегать str.translate,str.replace и strmul поскольку они создают вторую строку, но это обычно не вызывает беспокойства в эти дни.

вы можете преобразовать в набор и проверить, что есть только один член:

len(set("AAAAAAAA"))

попробуйте использовать встроенную функцию all:

all(c == 'A' for c in s)

добавление другого решения этой проблемы

>>> not "AAAAAA".translate(None,"A")
True

Если вам нужно проверить, все ли символы в строке одинаковы и равны заданному символу, вам нужно удалить все дубликаты и проверить, равен ли конечный результат одному символу.

>>> set("AAAAA") == set("A")
True

в случае, если вы хотите найти, если есть какие-либо дубликаты, просто проверьте длину

>>> len(set("AAAAA")) == 1
True

интересные ответы до сих пор. Вот еще:

flag = True
for c in 'AAAAAAAfAAAA':
    if not c == 'A': 
        flag = False
        break

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

not len("AAAAAAAAA".replace('A', ''))

Comments

    Ничего не найдено.