2010-01-13 11 views
8

Estoy escribiendo una secuencia de comandos en Python que permitirá al usuario ingresar una cadena, que será un comando que indica al script que realice una acción específica. Por el bien del argumento, voy a decir mi lista de comandos es:manera más rápida de comparar cadenas en python

lock 
read 
write 
request 
log 

Ahora, quiero que el usuario sea capaz de introducir la palabra "log" y se peform una acción específica, que es muy simple . Sin embargo, me gustaría hacer coincidir palabras parciales. Entonces, por ejemplo, si un usuario ingresa "lo", debe coincidir con "bloquear", ya que está más arriba en la lista. He intentado usar strncmp desde libc usando ctypes para lograr esto, pero aún tengo que hacer cara o cruz.

+1

¿Cuánto realmente importa la velocidad? Suponiendo que esto se ejecuta una sola vez cuando el usuario ingresa un comando y ejecuta un pequeño conjunto de comandos (menos de 1000), incluso la implementación más ineficiente (práctica) regresará en menos de un milisegundo, lo que aparecerá de manera instantánea al usuario. –

+0

Esta es una aplicación de red que se ejecuta en el marco Twisted, y puede tener hasta 50 usuarios que ingresan comandos al mismo tiempo, por lo que podría haber un retraso potencial si los 50 están ingresando comandos y estoy analizando ineficazmente. –

+2

trenzado es roscado. todavía no notarás ningún impacto. la mayoría de las computadoras pueden comparar 10,000 o más cuerdas en el tiempo que le lleva a su dedo presionar una tecla. Esto se llama optimización prematura, estás perdiendo el tiempo con trivialidades. – SpliFF

Respuesta

16

Si está aceptando comentarios de un usuario, ¿por qué le preocupa la velocidad de comparación? Incluso la técnica más lenta será mucho más rápida de lo que el usuario puede percibir. Use el código más simple y comprensible que pueda, y deje preocupaciones sobre la eficiencia para los circuitos internos ajustados.

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] 
+1

+1 buen punto sobre el uso del código más comprensible más simple – Lukman

+1

desearía poder votar 10 veces más. Es por eso que me encanta el desarrollo de UI: las escalas de tiempo son absolutamente (bueno, relativamente) enormes.Puedes tomar 100 minutos para hacer algo y nadie lo nota. –

2

puede utilizar startswith

por ejemplo

myword = "lock" 
if myword.startswith("lo"): 
    print "ok" 

o si usted quiere encontrar "lo" en la palabra, independientemente de la posición, sólo tiene que utilizar el "en" operador

if "lo" in myword 

por lo tanto, una forma de hacerlo puede ser:

for cmd in ["lock","read","write","request","log"]: 
    if cmd.startswith(userinput): 
     print cmd 
     break 
+2

@ ghostdog74, lea con más detalle: "Me gustaría hacer coincidir palabras parciales. Entonces, por ejemplo, si un usuario ingresa" lo ", debe coincidir con" bloquear ", ya que es superior en la lista." –

+0

Peter Hansen es correcto. Las palabras parciales deberán coincidir para que el sistema sea más fácil de usar. Tendré (eventualmente) algunos comandos complejos, y ser capaz de abreviarlos en una sola letra es muy conveniente. –

5

Esto va a hacer lo que quiere:

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 

Sin embargo:

Parece overworried sobre lo que no debía. Entonces, 50 usuarios significa 50 milisegundos; no te van a quedar fuera de la ciudad por ese tipo de "retraso". Preocuparse por el acceso ineficiente a la base de datos o los problemas causados ​​por los usuarios que escriben "r" y obtienen "lectura" cuando piensan que obtendrán "solicitud". Minimizar las pulsaciones de teclas del usuario a riesgo de errores es tan de los años 60 que no es divertido. ¿Qué están usando? Teletipos ASR33? Por lo menos, puede insistir en una coincidencia única: "rea" para lectura y "req" para solicitud.

0

Reemplace con su función favorita de comparación de cadenas. Bastante rápido y al grano.

matches = (x for x in list if x[:len(stringToSearchFor)] == stringToSearchFor) 
print matches[0] 
+1

(1) consulte 'http: //docs.python.org/ library/stdtypes.html # str.startswith' (2) no use' list'; sombreará la 'lista()' incorporada. –

2

Le sugiero que consulte el uso de la biblioteca readline python, en lugar de reinventar la rueda. El usuario tendrá que presionar la tecla tab para completar la palabra, pero puede configurar la línea de lectura para que esa pestaña coincida lo más posible o recorre todas las palabras comenzando con el stub actual.

Ésta parece ser una introducción bastante decente a readline en Python http://www.doughellmann.com/PyMOTW/readline/index.html

0

Esta es una adaptación de J.Tauber's Trie implementation in Python, que se podría comparar y/o re-adaptarse con cualesquiera características adicionales que necesita. Vea también el Wikipedia entry on tries.

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] 

configuración (orden de asuntos adición!):

t = Trie() 
for word in [ 
    'lock', 
    'read', 
    'write', 
    'request', 
    'log']: 
    t.add(word) 

Entonces llaman así:

>>> t.find('lo') 
'lock' 
>>> t.find('log') 
'log' 
>>> t.find('req') 
'request' 
>>> t.find('requiem') 
>>> 
+0

Holy overkill, Batperson! –

+0

No es broma, pero otra "iterar sobre la lista con startswith()" parecía * realmente * aburrida. ;-) –

+0

Al menos mis esfuerzos con el esfuerzo abordaron la preocupación de eficiencia del OP (inútil) por la primera coincidencia :-) –

0

si entiendo su Q correctamente, usted quiere un fragmento que devolverá el responda tan pronto como lo tenga, sin atravesar más su 'lista de comando'. Esto debería hacer lo que quiera:

from itertools import ifilter 

def check_input(some_string, code_book) : 
    for q in ifilter(code_book.__contains__, some_string) : 
     return True 
    return False 
3

Esto se ha optimizado en tiempo de ejecución como el que ha solicitado ... (aunque la mayoría no necesita probable)

Aquí es un poco de código simple que tendrá una entrada diccionario de comando asignado a la función, y da como resultado un diccionario de salida de todos los subcomandos no duplicados asignados a la misma función.

Por lo tanto, ejecute esto cuando comience su servicio, y luego tenga 100% de búsquedas optimizadas. Estoy seguro de que hay una forma más inteligente de hacerlo, así que siéntete libre de editar.

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, 
} 
0
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 

Así que, básicamente, por mi comentario, usted está preguntando cómo optimizar una operación que no requiere tiempo medible o CPU. Usé 10.000 comandos aquí y la cadena de prueba coincide con cada uno solo para mostrar que, incluso en circunstancias extremas, aún podría tener cientos de usuarios haciendo esto y nunca verían ningún retraso.

Cuestiones relacionadas