Python-Problemumgehungen für die Zuweisung in Lambda

34

Dies ist eine Tippfrage zum Golfen in Python.

Beim Python-Golfen ist es üblich, dass eine Vorlage eine als Lambda definierte Funktion ist. Beispielsweise,

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

berechnet die Fakultät von x.

Das Lambda-Format hat zwei große Vorteile :

  • Die Kesselplatte von f=lambda x:...oder lambda x:...ist kürzer als die von def f(x):...return...oderx=input()...print...
  • Ein rekursiver Aufruf kann verwendet werden, um eine Schleife mit geringem Byte-Overhead auszuführen.

Lambdas haben jedoch den großen Nachteil, dass sie nur einen einzigen Ausdruck zulassen, keine Aussagen. Insbesondere bedeutet dies keine Zuordnungen wie c=chr(x+65). Dies ist problematisch, wenn ein langer Ausdruck vorhanden ist, auf dessen Wert zweimal (oder öfter) verwiesen werden muss.

Zuweisungen wie E=enumeratesind außerhalb der Funktion oder als optionales Argument möglich, jedoch nur, wenn sie nicht von den Funktionseingaben abhängen. Optionale Argumente wie f=lambda n,k=min(n,0):...fail, da die Eingabe nnicht definiert wurde, wenn sie kzum Definitionszeitpunkt ausgewertet wird.

Das Ergebnis ist, dass Sie manchmal einen langen Ausdruck in einem Lambda wiederholen, weil die Alternative ein langwieriges Nicht-Lambda ist.

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

Die Gewinnschwelle beträgt ungefähr 11 Zeichen ( Details ), nach denen Sie zu einem defoder wechseln program. Vergleichen Sie dies mit dem üblichen Break-Even der Länge 5 für einen wiederholten Ausdruck:

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

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

Andere Sprachen haben Workarounds, zum Beispiel Octave . Es gibt bekannte Tricks für Python, aber sie sind langwierig, klobig und / oder nur eingeschränkt verwendbar. Eine kurze, universelle Methode zur Simulation der Zuordnung in einem Lambda würde das Python-Golfen revolutionieren.


Wie kann ein Python-Golfer diese Einschränkung überwinden oder umgehen? Welche potenziellen Ideen sollten sie in Betracht ziehen, wenn sich ein langer Ausdruck in einem Lambda zweimal wiederholt?

Mein Ziel mit dieser Frage ist es, tief in dieses Problem einzutauchen und:

  • Katalogisieren und analysieren Sie Golf-Workarounds, um die Zuordnung innerhalb eines Lambdas zu fälschen
  • Entdecken Sie neue Leads für bessere Methoden

Jede Antwort sollte eine Problemumgehung oder einen möglichen Hinweis enthalten.

xnor
quelle
Ich vermute, dies ist eines der Dinge, die in Python nicht gut gemacht werden können. JavaScript hat ein Bein oben auf diesem.
mbomb007
Genau wie bei der Antwort von orlp ist Neils (gelöschter) Vorschlag, verschachtelte Lambda zu verwenden, nicht unbedingt länger als def, wenn Sie ohnehin ein verschachteltes Lambda benötigen. Ich denke, es verdient eine gründlichere Analyse.
Martin Ender
2
Für das genaue Beispiel mit der umgekehrten Verkettung von Kleinbuchstaben könnte man einfach gehen lambda s:(s+s[::-1]).lower(). Dies beantwortet natürlich nicht die eigentliche Frage.
Jonathan Allan
@ JonathanAllan Guter Punkt, änderte es zu strip.
Xnor

Antworten:

6

eval

Dies ist an sich nicht so toll, aber wenn Ihre Lösung bereits evalauf irgendeine Weise oder in irgendeiner Form verwendet wird, können Sie normalerweise diese Technik verwenden.

eval("%f*%f+%f"%((5**.5,)*3))
orlp
quelle
Wow, das ist schlau! Warum sehe ich so einen Code nie?
z0rbergs
6
@ z0rberg's Wahrscheinlich weil eval böse ist.
HyperNeutrino
Ich unterschreibe diesen Kodeismus nicht. #EvalLivesMatter ... aber im Ernst, wie ist das schlimmer als eine einfache DLL-Injektion?
z0rbergs
6

Zuweisungsausdrücke in Python 3.8

In Python 3.8 ( TIO ) werden Zuweisungsausdrücke eingeführt , mit denen :=eine Variable inline als Teil des Ausdrucks zugewiesen wird.

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

Dies kann innerhalb von verwendet werden lambda, wo Zuweisungen normalerweise nicht zulässig sind. Vergleichen Sie:

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

Weitere Informationen finden Sie in diesem Tipp .

xnor
quelle
2

Innere Lambdas

Mit diesen können Sie mehrere Variablen gleichzeitig definieren.

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

gegen

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

ist viel länger, aber wenn Sie mehrere oder längere Variablen haben, die oft wiederholt werden:

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)

gegen

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

Zeichenanzahl

Anfänglich: (lambda:)()(11 Bytes)
Erste Variable: [space]a(2 Bytes)
Nachfolgende Variablen: ,b,(3 Bytes)
Verwendung: a(1 Byte).

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

(Spart auch an Klammern)

Dies dauert also 3n + 10Bytes, wobei ndie Anzahl der Variablen ist. Dies ist ein hoher Anfangsaufwand, der sich aber am Ende auszahlen kann. Es gibt sogar seinen inneren Wert zurück, so dass Sie mehrere verschachteln können (dies wird sich jedoch schnell nicht mehr lohnen.)

Dies ist wirklich nur für lange Zwischenberechnungen in verschachtelten Listenverstehen nützlich, da def f():a=...;b=...;returnes normalerweise kürzer sein wird.

Bei einem Wert von 1 wird dadurch Folgendes gespeichert: Dies uses * length - length - uses - 13ist nur nützlich, wenn dieser Ausdruck positiv ist.
Für nverschiedene Ausdrücke, die uinsgesamt mal verwendet werden und deren kombinierte Länge beträgt l, wird Folgendes gespart:
l - (3 * n) - u - 10 ( + brackets removed )

Artyer
quelle
1

Verwenden Sie eine Liste

Deklarieren Sie eine Liste als Parameter und verwenden Sie .append() or, um den Wert zu speichern:
lambda s:s.lower()+s.lower()[::-1]
wird zu
lambda s,l=[]:l.append(s.lower())or l[-1]+l[-1][::-1]

Zeichenanzahl:

,l=[]5 Zeichen
l.append()or13 Zeichen
l[-1]5 Zeichen für jede Verwendung

Die Gewinnzone erreichen

Die Anzahl der hinzugefügten Zeichen beträgt:
uses*(5-length) + 18 + length
Im vorherigen Beispiel ist die Anweisung s.lower()9 Zeichen lang und wird zweimal verwendet, wobei diese Technik angewendet wird und 19 Zeichen hinzugefügt werden. Wenn es 7 Mal verwendet würde, würde es eine Zeichenreduktion von 1 geben.
Die Menge an minimalen Einsätzen, die diese Technik wert sein kann, ist
min_uses = (18+length)/(length-5)

Upsides

  • Erlaube eine neue Aufgabe zu einem etwas reduzierten Preis (die Liste ist bereits deklariert)
  • Ist ein listObjekt so [0], .pop(), [x:y]und andere Listenfunktionen können für Tricks verwendet werden. sehr situativ

Nachteile

  • Hohe Anschaffungskosten
  • Hohe Nutzungskosten
  • Funktioniert nur für Verwendungen mit einer Länge größer als 5

Benutze ein Wörterbuch

thanks @Zgarb
Gleiche Idee wie oben Deklarieren Sie ein Wörterbuch als Parameter und verwenden Sie es .setdefault(), um den Wert zu speichern (und zurückzugeben):
lambda s:s.lower()+s.lower()[::-1]
wird zu
lambda s,d={}:d.setdefault(0,s.lower())+d[0][::-1]
Beachten Sie, dass im Gegensatz zum listGegenstück setdefaultder zugewiesene Wert zurückgegeben wird.

Zeichenanzahl:

,d={}5 Zeichen
d.setdefault(k,)16 Zeichen
d[k]4 Zeichen für jede Verwendung

Die Gewinnzone erreichen

Die Anzahl der hinzugefügten Zeichen beträgt:
(uses-1)*(4-length) + 21
Im vorherigen Beispiel ist die Anweisung s.lower()9 Zeichen lang und wird zweimal verwendet, wobei diese Technik angewendet wird und 16 Zeichen hinzugefügt werden. Wenn es 7 Mal verwendet würde, würde es eine Zeichenreduktion von 1 geben.
Die Menge an minimalen Einsätzen, die diese Technik wert sein kann, ist
min_uses = 1-21/(4-length)

Vor- / Nachteile

  • Grundsätzlich das gleiche wie die Liste
  • Funktioniert nur für Verwendungen mit einer Länge größer als 4

Andere Überlegungen

  • Wenn diese Technik die Zeichenreduktion wert ist, lambdakann die wahrscheinlich fallengelassen und die Funktion mit def/ inputfür ein kürzeres Programm umgeschrieben werden.
  • Wie @FlipTack zeigte, SIND die Liste und das Diktat zwischen den Funktionsaufrufen gleich. Dies stört zwar meistens, kann aber bei rekursiven Aufrufen verwendet werden. wieder sehr situativ
  • Das Wörterbuch ist immer kürzer als die Liste.
Stange
quelle
3
Funktionseinreichungen müssen mehrfach verwendbar sein. Derzeit wird jedes Mal, wenn das Lambda ausgeführt wird, dieselbe Liste verwendet, was zu Problemen bei späteren Ausführungen führen kann.
FlipTack
@FlipTack aus Interesse, würde es Ihnen etwas ausmachen, eine Quelle wie Meta zu verlinken? Ich denke, diese Regel könnte sich auf ein paar Tricks auswirken, die ich kenne.
JAD
Ich denke, man kann es mit einem Wörterbuch besser machen: lambda s,d={}:d.setdefault(0,s.lower())+d[0][::-1]Es ist auch wiederverwendbar.
Zgarb
Mit können list.extendSie mehrere Elemente gleichzeitig hinzufügen. Dies ist kürzer als die list.appendmehrfache Verwendung .
mbomb007
0

Verwenden Sie diese Option, um Variablen festzulegen und die Daten nach der Operation wie folgt zurückzugeben:

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

Listenverständnisse

Dies ist eher ein letzter Ausweg, da es so unrühmlich ist, aber Sie können
[<expression> for <variable> in <value>]
eine Variable in einem Lambda pseudo-setzen. Grundsätzlich ist das einzig Gute an dieser Methode, dass der innere Ausdruck lesbar bleibt, was Sie beim Golfen offensichtlich am wenigsten interessiert.

Nur ASCII
quelle