2012-04-14 13 views
5

Lo siento si esta es una pregunta que se responde en otro lugar. Buscando a través de Google y Stackforum no encontré nada desde el que pudiera extrapolar las respuestas; pero siento que parte de eso soy yo.Python Lambda Count/Loop Function

Estoy tratando de entender lambdas como un concepto, y como parte de eso, estoy buscando formas de usarlo.

SO, si esto es una cosa colosal y estúpida que hacer con lambda desde el punto de vista de una función, no dude en hacérmelo saber y explicarme. Pero de cualquier manera, todavía quiero saber la respuesta/todavía quiero saber cómo hacer esto con el lenguaje python.

lo tanto, para los propósitos de prueba que tengo:

my_test = 'test_name' 
testlist = ['test_name', 'test_name_dup', 'test_name_dup_1', 'test_name_dup_3'] 

Busco a utilizar lambda para crear una función que recorra y devuelve la primera test_name_ # que no está en la LISTAPRUEBA. La funcionalidad eventualmente se aplicará a los nombres de los archivos, pero para fines de prueba tuve que alejarme de leer los nombres de los archivos, me dio muchas más formas de estropear algo.

Pero my_test tiene que poder cambiar y la lista de prueba será una lista de rutas de archivo.

lo tanto, estoy en busca de una función como:

new_name = lambda x: my_test + '_' + str(x) 

Pero el valor inicial debe ser x = 1, y debe continuar hasta que new_name no está en LISTAPRUEBA. Parece que:

bool(new_name not in testlist) 

podría ser algo trabajar con.

Pero no puedo encontrar la manera de configurar la inicial x en 1, y hacer que pase por (x + 1) hasta que el bool sea verdadero.

Sé que esto es posible ya que he encontrado algunos ejemplos CRAZY lambda que están recorriendo líneas en un archivo. Simplemente no podía entenderlos (y no tenía ninguna forma de jugar con ellos, ya que estaban lidiando con cosas fuera de mi nivel de programación.

En una nota relacionada, ¿podría agregar valores al comienzo de este bucle? (es decir, puedo tener que comprobar si hay nombreDePrueba, entonces test_name_dup, entonces test_name_dup_ #)?

Gracias de antemano por la ayuda! Lambdas (aunque muy frío) totalmente lío con la cabeza.

+1

Podría dar más ejemplos de entradas y salidas para esta función objetivo? – bereal

+0

Tenga en cuenta que los ejemplos de 'lambda' pueden ser solo malos ejemplos ya que' lambda's no son para muchos códigos porque hacen que el código sea ilegible y usar una función normal es mucho mejor en la mayoría de las situaciones. – jamylak

+0

Creo que sería útil que nos dijera cuál sería el resultado correcto para el ejemplo my_test/testlist. – thebjorn

Respuesta

2

no hay necesitará utilizar un lambda en este caso, un simple bucle for hará:

my_test = 'test_name_dup' 
testlist = ['test_name', 'test_name_dup','test_name_dup_1', 'test_name_dup_3'] 

for i in xrange(1, len(testlist)): 
    if my_test + '_' + str(i) not in testlist: 
     break 

print my_test + '_' + str(i) 
> test_name_dup_2 

Si realmente quieres usar lambda para este problema, también tendrás que aprender sobre itertools, iterators, filtros, etc. Voy a basarme en la respuesta de thg435, escribiéndola de una manera más idiomática y explicando que:

import itertools as it 

iterator = it.dropwhile(
    lambda n: '{0}_{1}'.format(my_test, n) in testlist, 
    it.count(1)) 

print my_test + '_' + str(iterator.next()) 
> test_name_dup_2 

la clave para entender la solución anterior radica en el procedimiento dropwhile(). Toma dos parámetros: un predicado y un iterable, y devuelve un iterador que suelta elementos del iterable siempre que el predicado sea verdadero; luego, devuelve cada elemento.Para los iterables, estoy pasando count(1), un iterador que produce un número infinito de enteros a partir de 1.

Luego dropwhile() comienza a consumir los números enteros hasta que el predicado sea falso; esta es una buena oportunidad para pasar una función definida en línea, y aquí está nuestro lambda. Recibe cada entero generado por turno, verificando si la cadena test_name_dup_ # está presente en la lista.

Cuando el predicado devuelve false, dropwhile() regresa y podemos recuperar el valor que hizo que se detenga llamando al next() en él.

+0

'xrange (1, len (testlist))' no es necesariamente suficiente, ¿verdad? –

+0

Sí, pero como dije, estoy buscando entender cómo hacer esto con lambda, incluso si no es necesario. Tengo una función de iterador que hace esto con solo rendimientos. Con tres líneas, puedo obtener esto iteraltools.count y tener comprueba los primeros dos. –

+0

@RobinHood bu Si su ejemplo es como tratar de encajar una clavija cuadrada en un agujero redondo, realmente no es para lo que las lambdas son buenas. Llevaría a una solución extremadamente artificial. Eche un vistazo a esta [publicación] (http://stackoverflow.com/questions/890128/python-lambda-why) para obtener ideas sobre cuándo es apropiado usar un 'lambda' –

1

Se puede combinar una lambda con itertools.dropwhile:

import itertools 
n = itertools.dropwhile(lambda n: 'test_name_dup_%d' % n in testlist, range(1, len(testlist))).next() 

En cuanto a su última pregunta, puede escribir un generador de nombres, como:

def possible_names(prefix): 
    yield prefix 
    yield prefix + '_dup' 
    n = 0 
    while True: 
     n += 1 
     yield '%s_dup_%d' % (prefix, n) 

y luego utilizar este generador con dropwhile:

unique_name = itertools.dropwhile(lambda x: x in testlist, possible_names('test_name')).next() 
print unique_name 
1

Usted está un poco fuera de la pista. Lambdas no son más que funciones "simples", a menudo utilizadas por su rápida sintaxis en programación funcional. Son las funciones integradas integradas perfectas "mapa", "reducir", "filtro", pero también para funciones más complicadas definidas en itertools. Por lo tanto, lo más útil que hacer con ellos es generar/manipular objetos iterables (especialmente listas). Tenga en cuenta que lambdas reducirá la velocidad de su código en la mayoría de los casos si se compara con la lista de comprensiones/bucles normales y dificultará la lectura. Aquí hay un ejemplo de lo que quiere hacer con lambdas.

>>> filter(lambda i: i!=(0 if len(testlist[i].split("_"))==3 else int(testlist[i].split("_")[-1])), range(len(testlist)))[0] 
2 

O podría utilizar funciones más complicadas con itertools. De todos modos, le sugiero encarecidamente que no use lambdas para este tipo de asignaciones, ya que la legibilidad es terrible. Prefiero usar un ciclo for bien estructurado, que también es más rápido.

[Editar]

Para probar que lambdas + órdenes internas no son más rápidas que las listas por comprensión: consideran un problema sencillo, para x en el rango (1000) crear una lista de x desplazado por 5.

$ python -m timeit 'mapa (lambda x: x >> 5, rango (1000))' 1000 bucles, lo mejor de 3: 225 USEC por lazo

$ python -m timeit '[x >> 5 para x en el rango (1000)] '10000 loops, lo mejor de 3: 99.1 usec por ciclo

Tiene un> 100% de aumento de rendimiento sin lambdas.

+0

Según tengo entendido, si se va a anidar, lambdas puede proporcionar un beneficio de velocidad para algunos otros bucles. No he encontrado algo que realmente me permita entender la diferencia. Esta función lambda está destinada a estar anidada en una función para fusionar/mover carpetas/archivos en ciertas circunstancias. La razón por la que parecía una buena opción para mí, al menos para descubrir algunas de las funciones de lambdas, es que esto no va a ser algo que deba mejorarse, cambiarse, etc. Se encargará de nombrar cosas cuando va a haber dos de algo en el mismo lugar. –

+0

@RobinHood ver mi edición para actuaciones – luke14free

5

Lambdas son sólo otra manera de definir una función

def foo(x): 
    return x + x 

es lo mismo que

foo = lambda x: x + x 

Así que vamos a empezar con una función para hacer lo que quiera

def first_missing(items, base): 
    for number in itertools.count(): 
     text = base + '_' + str(number) 
     if text not in items: 
      return text 

El Lo primero que debe tener en cuenta es que no puede usar bucles dentro de una lambda. Entonces necesitaremos reescribir esto sin un bucle. En su lugar, vamos a utilizar la recursividad

def first_missing(items, base, number = 0): 
     text = base + '_' + str(number) 
     if text not in items: 
      return text 
     else: 
      return first_missing(items, base, number + 1) 

Ahora bien, tampoco podemos utilizar un bloque if/else en una lambda. Sin embargo, podemos utilizar una expresión ternaria

def first_missing(items, base, number = 0): 
     text = base + '_' + str(number) 
     return text if text not in items else first_missing(items, base, number + 1) 

No podemos tener variables locales en un lambda, por lo que vamos a utilizar un truco, los argumentos por defecto

def first_missing(items, base, number = 0): 
     def inner(text = base + '_' + str(number)): 
      return text if text not in items else first_missing(items, base, number + 1) 
     return inner() 

En este punto podemos volver a escribir como interno una lambda

def first_missing(items, base, number = 0): 
     inner = lambda text = base + '_' + str(number): text if text not in items else first_missing(items, base, number + 1) 
     return inner() 

podemos combinar dos líneas de deshacerse de la variable local interno

def first_missing(items, base, number = 0): 
    return (lambda text = base + '_' + str(number): text if text not in items else first_missing(items, base, number + 1))() 

Y por fin, podemos hacer todo esto en una lambda

first_missing = lambda: items, base, number = 0: (lambda text = base + '_' + str(number): text if text not in items else first_missing(items, base, number + 1))() 

suerte que le dan una idea de lo que puede hacer. Pero no todos lo hacen. Como puedes ver, las lambdas realmente dificultan la lectura.

1

Prefiero la comprensión de la lista o el método del iterador. Hace que sea fácil de leer y mantener. Francamente, lambdas pertenecen a algunos lugares, aquí creo que es una solución menos elegante.

my_test = 'test_name' 
prefix = 'test_name_dup_' 
testlist = ['test_name','test_name_dup','test_name_dup_1','test_name_dup_3'] 

from itertools import count 
print next('%s%d' % (prefix, i) for i in count(1) if '%s%d' % (prefix, i) not in testlist) 

Esto devuelve la primera instancia no encontrada en la secuencia, que creo que es la más limpia.

Por supuesto, si usted prefiere una lista de un rango definido, puede modificarlo para convertirse en una lista por comprensión:

print ['%s%d' % (prefix, i) for i in xrange(0,5) if '%s%d' % (prefix, i) not in testlist] 

rendimientos:

['test_name_dup_0', 'test_name_dup_2', 'test_name_dup_4'] 
Cuestiones relacionadas