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.