Soluzioni alternative Python per assegnazione in lambda


34

Questa è una domanda di consigli per giocare a golf in Python.

Nel golf Python, è comune che una presentazione sia una funzione definita come lambda. Per esempio,

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

calcola il fattoriale di x.

Il formato lambda ha due grandi vantaggi :

  • La piastra della caldaia f=lambda x:...o lambda x:...è più corta della def f(x):...return...ox=input()...print...
  • Una chiamata ricorsiva può essere utilizzata per eseguire il loop con un sovraccarico di byte ridotto.

Tuttavia, i lambda hanno il grande svantaggio di consentire solo una singola espressione, nessuna affermazione. In particolare, ciò significa che non ci sono incarichi simili c=chr(x+65). Ciò è problematico quando si ha un'espressione lunga il cui valore deve essere referenziato due volte (o più).

Assegnazioni come E=enumeratesono possibili al di fuori della funzione o come argomento facoltativo, ma solo se non dipendono dagli input della funzione. Argomenti opzionali come f=lambda n,k=min(n,0):...fail perché l'input nnon è stato definito quando kviene valutato al momento della definizione.

Il risultato è che a volte fai schifo ripetendo una lunga espressione in un lambda perché l'alternativa è un lungo non-lambda.

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

Il punto di pareggio è di circa 11 caratteri ( dettagli ), oltre il quale si passa a defo program. Confronta questo con il solito punto di pareggio di lunghezza 5 per un'espressione ripetuta:

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

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

Altre lingue hanno soluzioni alternative, ad esempio Octave . Esistono trucchi noti per Python, ma sono lunghi, goffi e / o di uso limitato. Un metodo breve e generico per simulare l'assegnazione in una lambda rivoluzionerebbe il golf Python.


Quali sono i modi in cui un golfista Python può superare o aggirare questa limitazione? Quali potenziali idee dovrebbero avere in mente quando vedono una lunga espressione ripetuta due volte in una lambda?

Il mio obiettivo con questa domanda è di approfondire questo problema e:

  • Catalogare e analizzare le soluzioni alternative per giocare a golf per falsificare il compito all'interno di una lambda
  • Esplora nuovi contatti per metodi migliori

Ogni risposta dovrebbe spiegare una soluzione alternativa o potenziale lead.


Immagino che questa sia una di quelle cose che non possono essere fatte bene in Python. JavaScript ha un vantaggio su questo.
mbomb007,

Proprio come con la risposta di orlp, il suggerimento (cancellato) di Neil per l'uso di lambda nidificati non è necessariamente più lungo di def nei casi in cui è comunque necessario un lambda nidificato. Penso che meriti un'analisi più approfondita.
Martin Ender,

2
Per l'esempio esatto fornito con la concatenazione di stringhe in minuscolo inversa, si potrebbe semplicemente optare per lambda s:(s+s[::-1]).lower(). Naturalmente questo non risponde alla domanda reale.
Jonathan Allan,

@JonathanAllan Buon punto, l'ho cambiato in strip.
xnor

Risposte:


6

eval

Questo non è eccezionale in sé, ma se la tua soluzione utilizza già evalin qualche modo o forma di solito puoi usare questa tecnica.

eval("%f*%f+%f"%((5**.5,)*3))

Wow, è intelligente! Perché non vedo mai questo tipo di codice?
z0rberg,

6
@ z0rberg's Probabilmente perché eval è malvagio.
HyperNeutrino,

Non sottoscrivo questo codice. #EvalLivesMatter ... ma seriamente, come è peggio della semplice iniezione di dll?
z0rberg

6

Espressioni di assegnazione in Python 3.8

Python 3.8 ( TIO ) introduce espressioni di assegnazione , che usano :=per assegnare una variabile in linea come parte dell'espressione.

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

Questo può essere usato all'interno di a lambda, dove i compiti non sono normalmente consentiti. Confrontare:

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

Vedi questo consiglio per di più.


2

Lambdas interiori

Questi ti consentono di definire più variabili contemporaneamente.

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

vs.

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

è molto più lungo, ma se hai più variabili o variabili più lunghe, che si ripetono più volte:

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))

Conteggio dei caratteri

Iniziale: (lambda:)()(11 byte)
Prima variabile: [space]a(2 byte)
Variabili successive: ,b,(3 byte)
Utilizzare: a(1 byte).

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

(Salva anche tra parentesi)

Quindi, questo richiede 3n + 10byte, dove si ntrova il numero di variabili. Questo è un costo iniziale elevato, ma alla fine può ripagare. Restituisce anche il suo valore interiore, quindi puoi nidificare più (anche se questo non diventerà rapidamente valsa la pena.)

Questo è davvero utile solo per calcoli intermedi lunghi nelle comprensioni di elenchi nidificati, poiché di def f():a=...;b=...;returnsolito sarà più breve.

Per 1 valore, questo salva:, uses * length - length - uses - 13quindi è utile solo quando quell'espressione è positiva.
Per ndiverse espressioni utilizzate utempi in totale, dove è la loro lunghezza combinata l, questo consente di risparmiare:
l - (3 * n) - u - 10 ( + brackets removed )


1

Usa un elenco

Dichiarare un elenco come parametro e utilizzare .append() orper memorizzare il valore:
lambda s:s.lower()+s.lower()[::-1]
si trasforma in
lambda s,l=[]:l.append(s.lower())or l[-1]+l[-1][::-1]

Conteggio dei caratteri:

,l=[]5 caratteri
l.append()or13 caratteri
l[-1]5 caratteri per ogni utilizzo

Pareggiare

La quantità di carattere aggiunto è:
uses*(5-length) + 18 + length
Nell'esempio precedente la frase è s.lower()lunga 9 caratteri e viene utilizzata 2 volte, applicando questa tecnica sono stati aggiunti 19 caratteri. Se fosse usato 7 volte, ci sarebbe una riduzione di 1 carattere.
La quantità di usi minimi per questa tecnica vale la pena
min_uses = (18+length)/(length-5)

upsides

  • Consenti un nuovo incarico a un costo leggermente ridotto (l'elenco è già dichiarato)
  • È un listoggetto così [0], .pop(), [x:y]e altre funzioni elenco può essere utilizzato per i trucchi. altamente situazionale

Svantaggi

  • Alto costo iniziale
  • Costi di utilizzo elevati
  • Funziona solo per usi con lunghezza superiore a 5

Usa un dizionario

grazie @Zgarb
Stessa idea di cui sopra Dichiara un dizionario come parametro e usa .setdefault()per memorizzare (e restituire) il valore:
lambda s:s.lower()+s.lower()[::-1]
si trasforma in
lambda s,d={}:d.setdefault(0,s.lower())+d[0][::-1]
Nota che, a differenza della listcontroparte, setdefaultrestituisce il valore assegnato.

Conteggio dei caratteri:

,d={}5 caratteri
d.setdefault(k,)16 caratteri
d[k]4 caratteri per ogni utilizzo

Pareggiare

La quantità di carattere aggiunto è:
(uses-1)*(4-length) + 21
Nell'esempio precedente la frase è s.lower()lunga 9 caratteri e viene utilizzata 2 volte, applicando questa tecnica sono stati aggiunti 16 caratteri. Se fosse usato 7 volte, ci sarebbe una riduzione di 1 carattere.
La quantità di usi minimi per questa tecnica vale la pena
min_uses = 1-21/(4-length)

Upsides / Svantaggi

  • Fondamentalmente uguale all'elenco
  • Funziona solo per usi con lunghezza superiore a 4

Altre considerazioni

  • Se questa tecnica vale la riduzione del carattere, è lambdapossibile che la funzione venga probabilmente abbandonata e che la funzione venga riscritta con def/ inputper un programma più breve.
  • Come indicato da @FlipTack , l'elenco e il comando SONO TENUTI tra le chiamate di funzione, mentre ciò può disturbare maggiormente, può essere utilizzato su chiamate ricorsive. di nuovo altamente situazionale
  • Il dizionario sarà sempre più breve dell'elenco.

3
L'invio di funzioni deve essere utilizzabile più di una volta. Attualmente questo utilizza lo stesso elenco ogni volta che viene eseguito lambda, che potrebbe rovinare le cose nelle successive esecuzioni.
FlipTack

@FlipTack per interesse, ti dispiacerebbe collegare una fonte come meta? Penso che quella regola possa avere un certo impatto su alcuni trucchi che conosco.
JAD,


Penso che tu possa fare di meglio con un dizionario: lambda s,d={}:d.setdefault(0,s.lower())+d[0][::-1]è anche riutilizzabile.
Zgarb,

È possibile utilizzare list.extendper aggiungere più elementi contemporaneamente, che sarà più breve rispetto all'uso list.appendpiù volte.
mbomb007,

0

Utilizzare per impostare le variabili e restituire i dati dopo l'operazione in questo modo:

add = lambda x, y: exec("x+=y")

in alternativa: {add = lambda x, y: [x.append (x [0] + y), x.reverse (), x.pop ()]}
Dylan Eliot

0

Comprensione delle liste

Questo è più di ultima istanza poiché è così ungolfy, ma puoi fare
[<expression> for <variable> in <value>]
per pseudo-impostare una variabile in un lambda. Fondamentalmente l'unico aspetto positivo di questo metodo è che l'espressione interiore può rimanere leggibile, il che è ovviamente il minimo problema quando si gioca a golf.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.