Python обходные пути для назначения в лямбде


34

Это вопрос подсказки для игры в гольф на Python.

В гольфе Python представление является функцией, определяемой как лямбда. Например,

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

вычисляет факториал х.

У лямбда-формата есть два больших преимущества :

  • Образец 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):...fail, потому что ввод nне был определен, когда kоценивается во время определения.

В результате вы иногда повторяете длинное выражение в лямбде, потому что альтернатива - длинная не лямбда.

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

Точка безубыточности составляет около 11 символов ( детали ), после которой вы переключаетесь на a 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

Другие языки имеют обходные пути, например , Octave . Существуют известные приемы для 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's

6
@ z0rberg's Наверное, потому что eval - это зло.
HyperNeutrino

Я не подписан на этот кодизм. #EvalLivesMatter ... но серьезно, как это хуже, чем простая dll-инъекция?
z0rberg's

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]

против

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)

против

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]и другие функции списка могут быть использованы для трюков. высоко ситуативный

Downsides

  • Высокая начальная стоимость
  • Высокая стоимость использования
  • Работает только для использования с длиной, превышающей 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)

Расквитаться / Downsides

  • В основном так же, как список
  • Работает только для использования с длиной, превышающей 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.