Я пишу сценарий в Python, который позволит пользователю вводить строку, которая будет командой, которая дает сценарию команду выполнять определенное действие. Ради аргумента я скажу, что мой список команд:
lock
read
write
request
log
Теперь, я хочу, чтобы пользователь был в состоянии ввести слово "журнал", и это выполнит определенное действие, которое очень просто. Однако я хотел бы распознать частичные слова. Так, например, если пользователь вводит "lo", он должен соответствовать "блокировке", поскольку это выше в списке. Я попытался использовать strncmp от libc, использующего ctypes, чтобы выполнить это, но иметь все же для понимания его.
Если вы принимаете вклад от пользователя, то почему вы беспокоитесь о скорости сравнения? Даже самая медленная техника будет гораздо быстрее, чем пользователь может воспринимать. Используйте самый простой наиболее понятный код, который вы можете, и оставить эффективность концерн для жестких внутренних петлей.
cmds = [
"lock",
"read",
"write",
"request",
"log",
]
def match_cmd(s):
matched = [c for c in cmds if c.startswith(s)]
if matched:
return matched[0]
Это сделает то, что вы хотите:
def select_command(commands, user_input):
user_input = user_input.strip().lower()
for command in commands:
if command.startswith(user_input):
return command
return None
Тем не менее:
Вы кажусь оборажены по неправильной вещи. Так что 50 пользователей означают 50 миллисекунд - вы не будете уходить из города за такую «отставание». Беспокойство о неэффективном доступе к базе данных или проблемы, вызванные пользователями, набрав «R» и получают «чтение», когда они думали, что они получили «запрос». Минимизация нажатий пользователей на риск ошибок настолько 1960-х годов, что это не смешно. Что они используют? ASR33 Телетапы? По крайней мере, вы могли настаивать на уникальном матче - «REA» для чтения и «REQ» для запроса.
Jaro_Winkler ()
в Python-levenshtein может быть то, что вы ищете.
Это оптимизировано при выполнении времени, как вы запрашивали ... (хотя, скорее всего, не нужно)
Вот простой бит кода, который примет входной словарь команд Для функционирования и результаты в результате выходного словаря всех не дублирующихся командных команд сопоставлены с той же функцией.
Итак, вы запускаете это, когда вы начнете свой сервис, а затем у вас на 100% оптимизированные поиски. Я уверен, что есть более умный способ сделать это, так что не стесняйтесь редактировать.
commands = {
'log': log_function,
'exit': exit_function,
'foo': foo_function,
'line': line_function,
}
cmap = {}
kill = set()
for command in commands:
for pos in range(len(1,command)):
subcommand = command[0:pos]
if subcommand in cmap:
kill.add(subcommand)
del(cmap[subcommand])
if subcommand not in kill:
cmap[subcommand] = commands[command]
#cmap now is the following - notice the duplicate prefixes removed?
{
'lo': log_function,
'log': log_function,
'e': exit_function,
'ex': exit_function,
'exi': exit_function,
'exit': exit_function,
'f' : foo_function,
'fo' : foo_function,
'foo' : foo_function,
'li' : line_function,
'lin' : line_function,
'line' : line_function,
}
Это адаптировано из реализации TRIE в Python в Python , что вы могли бы сравнить и / или повторно адаптироваться любыми дополнительными функциями, которые вам нужны. Смотрите также в вход в Википедию на попытках .
class Trie:
def __init__(self):
self.root = [None, {}]
def add(self, key):
curr_node = self.root
for ch in key:
curr_node = curr_node[1].setdefault(ch, [key, {}])
curr_node[0] = key
def find(self, key):
curr_node = self.root
for ch in key:
try:
curr_node = curr_node[1][ch]
except KeyError:
return None
return curr_node[0]
Настройка (порядок добавления вопросов!):
t = Trie()
for word in [
'lock',
'read',
'write',
'request',
'log']:
t.add(word)
Тогда звоните следующим образом:
>>> t.find('lo')
'lock'
>>> t.find('log')
'log'
>>> t.find('req')
'request'
>>> t.find('requiem')
>>>
import timeit
cmds = []
for i in range(1,10000):
cmds.append("test")
def get_cmds(user_input):
return [c for c in cmds if c.startswith(user_input)]
if __name__=='__main__':
t = timeit.Timer("get_cmds('te')", "from __main__ import get_cmds")
print "%0.3f seconds" % (t.timeit(number=1))
#>>> 0.008 seconds
Итак, в основном, согласно моему комментарию, вы спрашиваете, как оптимизировать операцию, не требующую измеряемого времени или процессора. Я использовал здесь 10,000 команд, и тестовая строка соответствует каждой только для того, чтобы показать, что даже в экстремальных условиях это могут делать сотни пользователей, и они никогда не увидят никакого запаздывания.
Я предлагаю вам взглянуть на использование библиотеки питонов для чтения, а не изобретать колесо заново. Пользователю придется нажимать клавишу табуляции для завершения слова, но вы можете настроить readline таким образом, чтобы табуляция по возможности совпадала или циклически перебирала все слова, начинающиеся с текущего корешка.
Похоже, это довольно приличное введение в readline на питонском http://www.doughellmann.com/PyMOTW/readline/index.html
Замените на вашу любимую функцию сравнения строк. Довольно быстро, и к делу.
matches = ( x for x in list if x[:len(stringToSearchFor)] == stringToSearchFor )
print matches[0]
можно использовать startwith
eg
myword = "lock"
if myword.startswith("lo"):
print "ok"
или если Вы хотите найти "lo" в слове, независимо от позиции, просто используйте оператор "in"
if "lo" in myword
поэтому, один из способов сделать это:
for cmd in ["lock","read","write","request","log"]:
if cmd.startswith(userinput):
print cmd
break
Если я правильно понимаю ваш Q, вы хотите, чтобы фрагмент, который вернет ответ, как только он имеет его, не пройдя дальше через вашу «Список команд». Это должно делать то, что вы хотите:
from itertools import ifilter
def check_input(some_string, code_book) :
for q in ifilter(code_book.__contains__, some_string) :
return True
return False