Soluciones de Python para asignación en lambda

34

Esta es una pregunta de consejos para jugar golf en Python.

En el golf de Python, es común que un envío sea una función definida como lambda. Por ejemplo,

f=lambda x:0**x or x*f(x-1)

calcula el factorial de x.

El formato lambda tiene dos grandes ventajas :

  • La repetitiva de f=lambda x:...o lambda x:...es más corta que la def f(x):...return...ox=input()...print...
  • Se puede usar una llamada recursiva para hacer un bucle con una pequeña sobrecarga de bytes.

Sin embargo, las lambdas tienen el gran inconveniente de permitir solo una sola expresión, sin declaraciones. En particular, esto significa que no hay tareas como c=chr(x+65). Esto es problemático cuando uno tiene una expresión larga cuyo valor necesita ser referenciado dos veces (o más).

Las asignaciones como E=enumerateson posibles fuera de la función o como argumento opcional, pero solo si no dependen de las entradas de la función. Argumentos opcionales como f=lambda n,k=min(n,0):...error porque la entrada nno se ha definido cuando kse evalúa en el momento de la definición.

El resultado es que a veces apestas repetir una expresión larga en una lambda porque la alternativa es una larga no lambda.

lambda s:s.strip()+s.strip()[::-1]
def f(s):t=s.strip();print t+t[::-1]

El punto de equilibrio es de aproximadamente 11 caracteres ( detalles ), pasado el cual cambia a un defo program. Compare esto con el punto de equilibrio habitual de longitud 5 para una expresión repetida:

range(a)+range(b)
r=range;r(a)+r(b)

print s[1:],s[1:]*2
r=s[1:];print r,r*2

Otros idiomas tienen soluciones alternativas, por ejemplo , Octave . Hay trucos conocidos para Python, pero son largos, torpes y / o de uso limitado. Un método breve y de propósito general para simular la asignación en una lambda revolucionaría el golf de Python.


¿Cuáles son las formas para que un golfista de Python supere o evite esta limitación? ¿Qué ideas potenciales deberían tener en mente cuando ven una expresión larga repetida dos veces en una lambda?

Mi objetivo con esta pregunta de consejos es profundizar en este problema y:

  • Cataloga y analiza soluciones de golf para falsificar tareas dentro de una lambda
  • Explore nuevos clientes potenciales para obtener mejores métodos

Cada respuesta debe explicar una solución alternativa o potencial cliente potencial.

xnor
fuente
Supongo que esta es una de esas cosas que no se pueden hacer bien en Python. JavaScript tiene una ventaja sobre este.
mbomb007
Al igual que con la respuesta de orlp, la sugerencia (eliminada) de Neil para usar lambdas anidadas no es necesariamente más larga que def en los casos en que necesita una lambda anidada de todos modos. Creo que merece un análisis más exhaustivo.
Martin Ender
2
Para el ejemplo exacto dado con la concatenación de cadenas en minúscula invertida, uno podría elegir lambda s:(s+s[::-1]).lower(). Por supuesto, esto no responde la pregunta real.
Jonathan Allan el
@JonathanAllan Buen punto, lo cambió a strip.
xnor

Respuestas:

6

eval

Esto no es tan bueno en sí mismo, pero si su solución ya se usa evalde alguna manera o forma, generalmente puede usar esta técnica.

eval("%f*%f+%f"%((5**.5,)*3))
orlp
fuente
Wow, eso es inteligente! ¿Por qué nunca veo este tipo de código?
12 de
66
@ z0rberg's Probablemente porque eval es malo.
HyperNeutrino
No me suscribo a este codeismo. #EvalLivesMatter ... pero en serio, ¿cómo es eso peor que la simple inyección dll?
z0rberg's
6

Expresiones de asignación en Python 3.8

Python 3.8 ( TIO ) introduce expresiones de asignación , que se utilizan :=para asignar una variable en línea como parte de la expresión.

>>> (n:=2, n+1)
(2, 3)

Esto se puede usar dentro de a lambda, donde las asignaciones no se permiten normalmente. Comparar:

lambda s:(t:=s.strip())+t[::-1]
lambda s:s.strip()+s.strip()[::-1]
def f(s):t=s.strip();return t+t[::-1]

Vea este consejo para más.

xnor
fuente
2

Lambdas interiores

Estos le permiten definir múltiples variables a la vez.

lambda s:s.strip()+s.strip()[::-1]

vs.

lambda s:(lambda t:t+t[::-1])(s.strip())

es mucho más largo, pero si tiene múltiples variables, o variables que son más largas, que se repiten muchas veces:

lambda a,b,c:a.upper()*int(c)+b.lower()*int(c)+a.upper()[::-1]+b.lower()[::-1]+a.upper()*int(c)+a.lower()*int(c)

vs.

lambda a,B,c:(lambda A,b,n:A*n+b*n+A[::-1]+b[::-1]+A*n+b*c)(a.upper(),B.lower(),int(c))

Número de letras

Inicial: (lambda:)()(11 bytes)
Primera variable: [space]a(2 bytes)
Variables posteriores: ,b,(3 bytes)
Uso: a(1 byte).

(lambda* a*_,b_:)(*<value a>*_,<value b>_)

(También ahorra entre paréntesis)

Entonces, esto toma 3n + 10bytes, donde nes el número de variables. Este es un alto costo inicial, pero puede pagar al final. Incluso devuelve su valor interno, por lo que puede anidar múltiples (aunque esto rápidamente no valdrá la pena).

Esto realmente solo es útil para cálculos intermedios largos en comprensiones de listas anidadas, ya def f():a=...;b=...;returnque generalmente será más corto.

Para 1 valor, esto guarda:, uses * length - length - uses - 13por lo que solo es útil cuando esa expresión es positiva.
Para ndiferentes expresiones utilizadas uen total, donde su longitud combinada es l, esto guarda:
l - (3 * n) - u - 10 ( + brackets removed )

Artyer
fuente
1

Use una lista

Declarar una lista como parámetro y usar .append() orpara almacenar el valor: se
lambda s:s.lower()+s.lower()[::-1]
convierte en
lambda s,l=[]:l.append(s.lower())or l[-1]+l[-1][::-1]

Número de letras:

,l=[]5 caracteres
l.append()or13 caracteres
l[-1]5 caracteres para cada uso

Cubrir los gastos

La cantidad de caracteres añadidos es:
uses*(5-length) + 18 + length
en el ejemplo anterior, la declaración s.lower()tiene 9 caracteres de longitud y se usa 2 veces, aplicando esta técnica se agregaron 19 caracteres. Si se usara 7 veces, habría una reducción de 1 carácter.
La cantidad de usos mínimos que vale esta técnica es
min_uses = (18+length)/(length-5)

Al revés

  • Permitir una nueva asignación a un costo ligeramente reducido (la lista ya está declarada)
  • Es un listobjeto de modo [0], .pop(), [x:y]y otras funciones de la lista se puede utilizar para hacer trucos. altamente situacional

Desventajas

  • Alto costo inicial
  • Alto costo de uso
  • Solo funciona para usos con una longitud superior a 5

Usa un diccionario

gracias @Zgarb
Misma idea que la anterior Declarar un diccionario como parámetro y usar .setdefault()para almacenar (y devolver) el valor: se
lambda s:s.lower()+s.lower()[::-1]
convierte en
lambda s,d={}:d.setdefault(0,s.lower())+d[0][::-1]
Tenga en cuenta que, a diferencia de la listcontraparte, setdefaultdevuelve el valor asignado.

Número de letras:

,d={}5 caracteres
d.setdefault(k,)16 caracteres
d[k]4 caracteres para cada uso

Cubrir los gastos

La cantidad de caracteres agregados es:
(uses-1)*(4-length) + 21
En el ejemplo anterior, la declaración s.lower()tiene 9 caracteres de longitud y se usa 2 veces, aplicando esta técnica se agregaron 16 caracteres. Si se usara 7 veces, habría una reducción de 1 carácter.
La cantidad de usos mínimos que vale esta técnica es
min_uses = 1-21/(4-length)

Ventajas / desventajas

  • Básicamente lo mismo que la lista
  • Solo funciona para usos con una longitud superior a 4

Otras Consideraciones

  • Si esta técnica vale la reducción de caracteres, entonces lambdaprobablemente se puede descartar y la función se puede reescribir con def/ inputpara un programa más corto.
  • Como señaló @FlipTack , la lista y el dict SE MANTENEN entre llamadas de función, aunque esto molestará principalmente, se puede usar en llamadas recursivas. de nuevo altamente situacional
  • El diccionario siempre será más corto que la lista.
Barra
fuente
3
Las presentaciones de funciones deben ser utilizables más de una vez. Actualmente, esto usa la misma lista cada vez que se ejecuta lambda, lo que podría estropear las cosas en ejecuciones posteriores.
FlipTack
@FlipTack por interés, ¿te importaría vincular una fuente como meta? Creo que esa regla podría tener algún impacto en algunos trucos que conozco.
JAD
Creo que puedes mejorar con un diccionario: lambda s,d={}:d.setdefault(0,s.lower())+d[0][::-1]también es reutilizable.
Zgarb
Puede usar list.extendpara agregar varios elementos a la vez, lo que será más corto que usar list.appendvarias veces.
mbomb007
0

Se usa para establecer variables y devolver los datos después de la operación de la siguiente manera:

add = lambda x, y: exec("x+=y")
Dylan Eliot
fuente
alternativamente: {add = lambda x, y: [x.append (x [0] + y), x.reverse (), x.pop ()]}
Dylan Eliot
0

Lista de comprensiones

Este es más un último recurso, ya que es muy infiel, pero puedes hacer
[<expression> for <variable> in <value>]
para seudo-establecer una variable en una lambda. Básicamente, el único punto bueno de este método es que la expresión interna puede seguir siendo legible, lo que obviamente es lo que menos le preocupa al jugar golf.

Solo ASCII
fuente