Обхідні шляхи Python для призначення в лямбда


34

Це питання щодо порад щодо гольфу в Python.

У Python golfing звичайним є те, що подання - це функція, визначена лямбда. Наприклад,

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

обчислює факторіал x.

Формат лямбда має дві великі переваги :

  • Котла f=lambda x:...або lambda x:...коротша за def f(x):...return...абоx=input()...print...
  • Рекурсивний виклик може використовуватися для циклу з малою кількістю байтів.

Однак у лямбдів є великий недолік, що дозволяє лише одне висловлення, без висловлювань. Зокрема, це означає відсутність таких завдань c=chr(x+65). Це проблематично, коли ви маєте довгий вираз, на значення якого потрібно посилатись двічі (або більше).

Призначення на зразок E=enumerateможливі за межами функції або як необов'язковий аргумент, але лише якщо вони не залежать від входів функції. Необов’язкові аргументи, такі як f=lambda n,k=min(n,0):...помилка, тому що введення nне було визначено, коли kоцінюється під час визначення.

Результат полягає в тому, що іноді ви всмоктуєте повторення тривалого вираження в лямбда, тому що альтернативою є тривалий не лямбда.

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

Точка беззбитковості становить приблизно 11 символів ( деталі ), повз яких ви переходите на defабо program. Порівняйте це із звичайною точкою беззбитковості довжиною 5 для повторного виразу:

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

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

Інші мови мають обхідні шляхи, наприклад , Октава . Відомі трюки для Python, але вони тривалі, незграбні та / або обмежені. Короткий, загальноприйнятий метод для імітації завдання в лямбда може революціонізувати гольф на Python.


Які способи подолати гравця Python або подолати це обмеження? Які потенційні ідеї вони повинні мати на увазі, коли вони бачать довгий вираз, повторений двічі в лямбді?

Моя мета з питанням цих порад - зануритися в цю проблему і:

  • Каталогізуйте та проаналізуйте обхідні шляхи з гольфу, щоб підробити завдання в лямбда
  • Вивчіть нові результати для кращих методів

Кожна відповідь повинна пояснювати рішення чи потенційний потенціал.


Я здогадуюсь, що це одна з тих речей, які неможливо зробити в Python добре. У JavaScript є нога на цій.
mbomb007

Так само, як і у відповіді orlp, пропозиція Ніла (видалено) щодо використання вкладених лямбдашів не обов'язково довше, ніж def у випадках, коли вам потрібна вкладена лямбда. Я думаю, що це заслуговує на більш ретельний аналіз.
Мартін Ендер

2
Точний приклад із зворотним конкатенацією рядкових рядків можна просто продовжити lambda s:(s+s[::-1]).lower(). Звичайно, це не дає відповіді на власне питання.
Джонатан Аллан

@JonathanAllan Добре, змінив його на strip.
xnor

Відповіді:


6

eval

Це не так вже й само по собі, але якщо ваше рішення вже evalпевним чином або формується, зазвичай ви можете використовувати цю техніку.

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

Ого, це розумно! Чому я ніколи не бачу такого коду?
z0rberg

6
@ z0rberg Мабуть тому, що eval - це зло.
HyperNeutrino

Я не підписуюся на цей кодеїзм. #EvalLivesMatter ... але серйозно, як це гірше, ніж простий dll-ін'єкцій?
z0rberg від

6

Вирази призначення на Python 3.8

Python 3.8 ( TIO ) вводить вирази присвоєння , які використовують :=для призначення змінної вбудованої частини як частини виразу.

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

Це можна використовувати всередині lambda, де призначення заборонено зазвичай. Порівняйте:

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

Докладніше див. Цю пораду .


2

Внутрішні лямбда

Вони дозволяють визначити кілька змінних одночасно.

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

vs.

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

набагато довше, але якщо у вас є декілька змінних або змінних, які довші, які повторюються багато разів:

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

Кількість символів

Початкові: (lambda:)()(11 байт)
Перша змінна: [space]a(2 байти)
Наступні змінні: ,b,(3 байти)
Використання: a(1 байт).

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

(Також зберігається на дужках)

Отже, це займає 3n + 10байти, де nкількість змінних. Це висока початкова вартість, але може зрештою окупитися. Це навіть повертає внутрішню цінність, тому ви можете вкласти декілька (хоча це швидко стане не варто).

Це дійсно корисно лише для довгих проміжних обчислень у розумінні вкладених списків, оскільки, як def f():a=...;b=...;returnправило, коротше.

Для 1 значення це економить:, uses * length - length - uses - 13тому корисно лише тоді, коли це вираження є позитивним. Загалом
для nрізних виразів u, де їх об'єднана довжина l, економиться:
l - (3 * n) - u - 10 ( + brackets removed )


1

Скористайтеся списком

Оголосіть список як параметр і використовуйте .append() orдля збереження значення:
lambda s:s.lower()+s.lower()[::-1]
перетворюється на
lambda s,l=[]:l.append(s.lower())or l[-1]+l[-1][::-1]

Кількість символів:

,l=[]5 символів
l.append()or13 символів
l[-1]5 символів для кожного використання

Беззбитковість

Кількість доданого символу:
uses*(5-length) + 18 + length
У попередньому прикладі вислів становить s.lower()9 символів і використовується 2 рази, застосовуючи цю техніку, додано 19 символів. Якби його використовували 7 разів, було б зменшення на 1 символ.
Кількість мінімальної користі для цієї методики варто того, яка є
min_uses = (18+length)/(length-5)

Перевернуті

  • Дозволити нове завдання за дещо зниженою вартістю (список уже оголошено)
  • Чи є listоб'єкт таким чином [0], .pop(), [x:y]і інші функції списку можуть бути використані для трюків. сильно ситуативні

Недоліки

  • Висока початкова вартість
  • Висока вартість використання
  • Працює лише для використання з довжиною вище, ніж 5

Використовуйте словник

спасибо @Zgarb
Та сама ідея, що вище Визначте словник як параметр і використовуйте .setdefault()для зберігання (і повернення) значення:
lambda s:s.lower()+s.lower()[::-1]
перетворюється на
lambda s,d={}:d.setdefault(0,s.lower())+d[0][::-1]
Примітка, яке, на відміну від listаналога, setdefaultповертає призначене значення.

Кількість символів:

,d={}5 символів
d.setdefault(k,)16 символів
d[k]4 символи для кожного використання

Беззбитковість

Кількість доданого символу:
(uses-1)*(4-length) + 21
У попередньому прикладі вислів становить s.lower()9 символів і використовується 2 рази, застосовуючи цю техніку, додано 16 символів. Якби його використовували 7 разів, було б зменшення на 1 символ.
Кількість мінімальної користі для цієї методики варто того, яка є
min_uses = 1-21/(4-length)

Вгору / знизу

  • В основному те саме, що і список
  • Працює лише для використання з довжиною вище, ніж 4

Інші міркування

  • Якщо ця техніка вартує скорочення символів, то, lambdaймовірно, можна скинути і функцію переписати за допомогою def/ inputдля коротшої програми.
  • Як зазначав @FlipTack , перелік та диктування ПЕРЕКЛЮЧАЮТЬСЯ між функціональними дзвінками, хоча це здебільшого заважає, його можна використовувати для рекурсивних дзвінків. знову сильно ситуативний
  • Словник завжди буде коротшим, ніж список.

3
Функції подання функцій повинні бути використані не один раз. В даний час для цього використовується той самий список кожного разу, коли запускається лямбда, що може зіпсувати речі на пізніших запусках.
FlipTack

@FlipTack з інтересу, чи не заперечуєте ви пов’язати таке джерело, як мета? Я думаю, що це правило може мати певний вплив на декілька відомих мені трюків.
JAD


Я думаю, ви можете краще зі словником: lambda s,d={}:d.setdefault(0,s.lower())+d[0][::-1]це також багаторазове використання.
Згарб

Ви можете використовувати list.extendдля додавання відразу декількох елементів, що буде коротше, ніж використання list.appendдекількох разів.
mbomb007

0

Використовуйте для встановлення змінних і повернення даних після таких операцій:

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

альтернативно: {add = lambda x, y: [x.append (x [0] + y), x.reverse (), x.pop ()]}
Ділан Еліот

0

Перелічіть розуміння

Це скоріше крайній прийом, оскільки це так незрозуміло, але ви можете зробити,
[<expression> for <variable> in <value>]
щоб псевдоставити змінну в лямбда. В основному єдиний хороший момент щодо цього методу полягає в тому, що внутрішній вираз може залишатися читабельним, що, очевидно, найменше хвилює вас при гольфі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.