Obejścia Pythona dla przypisania w lambda

34

To jest wskazówka dotycząca gry w golfa w Pythonie.

W golfie w Pythonie podporządkowanie jest funkcją zdefiniowaną jako lambda. Na przykład,

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

oblicza silnię x.

Format lambda ma dwie duże zalety :

  • Płyta kotła f=lambda x:...lub lambda x:...jest krótsza niż def f(x):...return...lubx=input()...print...
  • Wywołanie rekurencyjne może być użyte do zapętlenia z niewielkim narzutem bajtowym.

Jednak lambdas mają tę wadę, że pozwalają tylko na jedno wyrażenie, bez instrukcji. W szczególności oznacza to brak zadań takich jak c=chr(x+65). Jest to problematyczne, gdy ktoś ma długie wyrażenie, do którego wartości należy odwoływać się dwukrotnie (lub więcej).

Takie przypisania E=enumeratesą możliwe poza funkcją lub jako opcjonalny argument, ale tylko wtedy, gdy nie zależą od danych wejściowych funkcji. Argumenty opcjonalne, takie jak f=lambda n,k=min(n,0):...niepowodzenie, ponieważ dane wejściowe nnie zostały zdefiniowane, gdy ksą oceniane w czasie definicji.

W rezultacie czasami ssie się powtarzanie długiego wyrażenia w lambda, ponieważ alternatywą jest długa nie-lambda.

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

Punkt rentowności wynosi około 11 znaków ( szczegóły ), po których przełączasz się na a deflub program. Porównaj to ze zwykłym punktem rentowności o długości 5, aby uzyskać powtarzające się wyrażenie:

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

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

Inne języki mają obejścia, na przykład Octave . Istnieją znane triki dla Pythona, ale są one długie, niezgrabne i / lub mają ograniczone zastosowanie. Krótka, uniwersalna metoda symulacji przypisania u lambdy zrewolucjonizuje grę w golfa w Pythonie.


W jaki sposób golfista Python może pokonać to ograniczenie lub obejść go? Jakie potencjalne pomysły powinni mieć na uwadze, gdy widzą długi wyraz powtórzony dwukrotnie w lambda?

Moim celem w tym pytaniu jest zanurzenie się głęboko w ten problem i:

  • Kataloguj i analizuj obejścia gry w golfa w celu sfałszowania zadania w lambda
  • Odkryj nowe potencjalne szanse na lepsze metody

Każda odpowiedź powinna wyjaśniać obejście lub potencjalną potencjalną szansę.

xnor
źródło
Zgaduję, że jest to jedna z tych rzeczy, których nie da się dobrze zrobić w Pythonie. JavaScript ma przewagę nad tym.
mbomb007
Podobnie jak w przypadku odpowiedzi orlp, sugestia Neila (usunięta) dotycząca używania zagnieżdżonych lambd niekoniecznie jest dłuższa niż def w przypadkach, gdy i tak potrzebujesz zagnieżdżonej lambdy. Myślę, że zasługuje na dokładniejszą analizę.
Martin Ender
2
Dla dokładnego przykładu podanego z odwróconą łączeniem małych liter można po prostu pójść lambda s:(s+s[::-1]).lower(). Oczywiście to nie odpowiada na rzeczywiste pytanie.
Jonathan Allan,
@JonathanAllan Good point, zmieniłem na strip.
xnor

Odpowiedzi:

6

eval

To samo w sobie nie jest tak świetne, ale jeśli twoje rozwiązanie już używa evalw jakiś sposób lub formie, zwykle możesz użyć tej techniki.

eval("%f*%f+%f"%((5**.5,)*3))
orlp
źródło
Wow, to sprytne! Dlaczego nigdy nie widzę tego rodzaju kodu?
z0rberg,
6
@ z0rberg's Prawdopodobnie dlatego, że eval jest złem.
HyperNeutrino,
Nie podpisuję się pod tym kodeksem. #EvalLivesMatter ... ale tak na poważnie, jak to jest gorsze od zwykłego wstrzykiwania biblioteki DLL?
z0rberg,
6

Wyrażenia przypisania w Pythonie 3.8

Python 3.8 ( TIO ) wprowadza wyrażenia przypisania , które służą :=do przypisania zmiennej inline jako części wyrażenia.

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

Można tego użyć wewnątrz lambda, gdzie przypisania nie są zwykle dozwolone. Porównać:

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

Zobacz tę wskazówkę, aby uzyskać więcej.

xnor
źródło
2

Lambda wewnętrzna

Umożliwiają one zdefiniowanie wielu zmiennych jednocześnie.

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

vs.

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

jest znacznie dłuższy, ale jeśli masz wiele zmiennych lub zmiennych, które są dłuższe i które są powtarzane wiele razy:

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

Liczba znaków

Początkowa: (lambda:)()(11 bajtów)
Pierwsza zmienna: [space]a(2 bajty)
Kolejne zmienne: ,b,(3 bajty)
Użyj: a(1 bajt).

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

(Zapisuje również w nawiasach)

To zajmuje 3n + 10bajty, gdzie njest liczba zmiennych. Jest to wysoki koszt początkowy, ale ostatecznie może się opłacić. Zwraca nawet swoją wartość wewnętrzną, więc możesz zagnieździć wiele (chociaż to szybko nie będzie tego warte).

Jest to naprawdę przydatne tylko w przypadku długich pośrednich obliczeń w zrozumieniu listy zagnieżdżonej, ponieważ def f():a=...;b=...;returnzwykle będzie krótszy.

Dla 1 wartości zapisuje to:, uses * length - length - uses - 13więc jest użyteczne tylko wtedy, gdy to wyrażenie jest dodatnie.
W przypadku nróżnych wyrażeń użytych uw sumie razy, gdzie ich łączna długość to l, oszczędza się:
l - (3 * n) - u - 10 ( + brackets removed )

Artyer
źródło
1

Użyj listy

Zadeklaruj listę jako parametr i użyj .append() ordo przechowywania wartości:
lambda s:s.lower()+s.lower()[::-1]
zamienia się w
lambda s,l=[]:l.append(s.lower())or l[-1]+l[-1][::-1]

Liczba znaków:

,l=[]5 znaków
l.append()or13 znaków
l[-1]5 znaków dla każdego użycia

Wychodzić na czysto

Ilość dodanego znaku wynosi:
uses*(5-length) + 18 + length
W poprzednim przykładzie instrukcja ma s.lower()długość 9 znaków i jest używana 2 razy, stosując tę ​​technikę dodano 19 znaków. Jeśli zostanie użyty 7 razy, nastąpi redukcja o 1 znak.
Wartość minimalnego wykorzystania tej techniki jest warta
min_uses = (18+length)/(length-5)

Plusy

  • Zezwól na nowe zadanie przy nieco obniżonym koszcie (lista jest już zadeklarowana)
  • Jest to listobiekt tak [0], .pop(), [x:y]oraz inne funkcje lista może być stosowany do sztuczek. wysoce sytuacyjny

Wady

  • Wysoki koszt początkowy
  • Wysokie koszty użytkowania
  • Działa tylko w przypadku zastosowań o długości większej niż 5

Użyj słownika

dzięki @Zgarb
Taki sam pomysł jak powyżej Zadeklaruj słownik jako parametr i użyj go .setdefault()do przechowywania (i zwracania) wartości:
lambda s:s.lower()+s.lower()[::-1]
zamienia się w
lambda s,d={}:d.setdefault(0,s.lower())+d[0][::-1]
Zwróć uwagę, że w przeciwieństwie do listodpowiednika, setdefaultzwraca przypisaną wartość.

Liczba znaków:

,d={}5 znaków
d.setdefault(k,)16 znaków
d[k]4 znaki dla każdego użycia

Wychodzić na czysto

Ilość dodanego znaku wynosi:
(uses-1)*(4-length) + 21
W poprzednim przykładzie instrukcja ma s.lower()długość 9 znaków i jest używana 2 razy, przy zastosowaniu tej techniki dodano 16 znaków. Jeśli zostanie użyty 7 razy, nastąpi redukcja o 1 znak.
Wartość minimalnego wykorzystania tej techniki jest warta
min_uses = 1-21/(4-length)

Wady / zalety

  • Zasadniczo taki sam jak lista
  • Działa tylko w przypadku zastosowań o długości większej niż 4

Inne uwagi

  • Jeśli ta technika jest warta zmniejszenia liczby znaków, to lambdaprawdopodobnie można ją usunąć, a funkcję przepisać za pomocą def/ inputdla krótszego programu.
  • Jak wskazał @FlipTack , lista i dykta SĄ PRZECHOWYWANE między wywołaniami funkcji, podczas gdy będzie to w większości przeszkadzać, może być używane w wywołaniach rekurencyjnych. ponownie wysoce sytuacyjny
  • Słownik zawsze będzie krótszy niż lista.
Pręt
źródło
3
Zgłoszenia funkcji muszą być użyteczne więcej niż jeden raz. Obecnie używa tej samej listy za każdym razem, gdy uruchamiana jest lambda, co może zepsuć rzeczy w późniejszych uruchomieniach.
FlipTack,
@FlipTack poza zainteresowaniem, czy mógłbyś połączyć źródło takie jak meta? Myślę, że ta reguła może mieć wpływ na kilka sztuczek, które znam.
JAD
Myślę, że możesz lepiej sobie radzić ze słownikiem: lambda s,d={}:d.setdefault(0,s.lower())+d[0][::-1]jest również wielokrotnego użytku.
Zgarb
Możesz użyć, list.extendaby dodać wiele elementów jednocześnie, co będzie krótsze niż list.appendwielokrotne użycie .
mbomb007
0

Służy do ustawiania zmiennych i zwracania danych po operacji, takich jak:

add = lambda x, y: exec("x+=y")
Dylan Eliot
źródło
alternatywnie: {add = lambda x, y: [x.append (x [0] + y), x.reverse (), x.pop ()]}
Dylan Eliot
0

Wyjaśnij listę

Jest to raczej ostatnia deska ratunku, ponieważ jest tak niegrzeczna, ale można zrobić,
[<expression> for <variable> in <value>]
aby pseudo ustawić zmienną w lambda. Zasadniczo jedyną zaletą tej metody jest to, że wewnętrzne wyrażenie może pozostać czytelne, co jest oczywiście najmniejszym problemem podczas gry w golfa.

Tylko ASCII
źródło