Soluções alternativas em Python para atribuição em lambda

34

Esta é uma pergunta de dicas para jogar golfe em Python.

No golfe em Python, é comum que um envio seja uma função definida como um lambda. Por exemplo,

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

calcula o fatorial de x.

O formato lambda tem duas grandes vantagens :

  • O boilerplate de f=lambda x:...ou lambda x:...é mais curto que o def f(x):...return...oux=input()...print...
  • Uma chamada recursiva pode ser usada para fazer um loop com pouca sobrecarga de bytes.

No entanto, as lambdas têm a grande desvantagem de permitir apenas uma única expressão, sem instruções. Em particular, isso significa que não há tarefas como c=chr(x+65). Isso é problemático quando se tem uma expressão longa cujo valor precisa ser referenciado duas vezes (ou mais).

Atribuições como E=enumeratesão possíveis fora da função ou como argumento opcional, mas somente se elas não dependerem das entradas da função. Argumentos opcionais como f=lambda n,k=min(n,0):...falha porque a entrada nnão foi definida quando ké avaliada no momento da definição.

O resultado é que, às vezes, você aspira repetir uma expressão longa em uma lambda porque a alternativa é uma longa não lambda.

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

O ponto de equilíbrio é de cerca de 11 caracteres ( detalhes ), após o qual você alterna para um defou program. Compare isso com o ponto de equilíbrio usual de comprimento 5 para uma expressão repetida:

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

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

Outros idiomas têm soluções alternativas, oitava por exemplo . Existem truques conhecidos para o Python, mas eles são longos, desajeitados e / ou de uso limitado. Um método curto e de uso geral para simular a atribuição em um lambda revolucionaria o golfe em Python.


Quais são as maneiras de um jogador de golfe Python superar ou contornar essa limitação? Que idéias potenciais eles devem ter em mente quando vêem uma expressão longa repetida duas vezes em uma lambda?

Meu objetivo com esta pergunta de dicas é mergulhar profundamente nesse problema e:

  • Catalogue e analise soluções alternativas de golfe para atribuições falsas dentro de uma lambda
  • Explore novos leads para melhores métodos

Cada resposta deve explicar uma solução alternativa ou potencial lead.

xnor
fonte
Acho que essa é uma daquelas coisas que não podem ser bem executadas no Python. JavaScript tem uma vantagem sobre este.
precisa saber é o seguinte
Assim como na resposta do orlp, a sugestão de Neil (excluída) para usar lambdas aninhadas não é necessariamente maior que def nos casos em que você precisa de uma lambda aninhada de qualquer maneira. Eu acho que merece uma análise mais completa.
Martin Ender
2
Para o exemplo exato dado com a concatenação de string em letras minúsculas invertidas, pode-se simplesmente procurar lambda s:(s+s[::-1]).lower(). Claro que isso não responde à pergunta real.
Jonathan Allan
@ JonathanAllan Bom ponto, mudou para strip.
Xnor

Respostas:

6

eval

Isso não é tão bom por si só, mas se sua solução já usa evalde alguma forma ou forma, geralmente você pode usar essa técnica.

eval("%f*%f+%f"%((5**.5,)*3))
orlp
fonte
Uau, isso é inteligente! Por que nunca vejo esse tipo de código?
z0rberg's
6
@ z0rberg's Provavelmente porque eval é mau.
HyperNeutrino
Eu não subscrevo este codismo. #EvalLivesMatter ... mas sério, como isso é pior do que a simples injeção de dll?
de z0rberg
6

Expressões de atribuição em Python 3.8

O Python 3.8 ( TIO ) introduz expressões de atribuição , que são usadas :=para atribuir uma variável embutida como parte da expressão.

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

Isso pode ser usado dentro de a lambda, onde as atribuições geralmente não são permitidas. Comparar:

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

Veja esta dica para mais.

xnor
fonte
2

Lambdas internas

Isso permite definir várias variáveis ​​ao mesmo tempo.

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

vs.

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

é muito mais longo, mas se você tiver várias variáveis ​​ou variáveis ​​mais longas, que serão repetidas várias vezes:

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

Contador de caracteres

Inicial: (lambda:)()(11 bytes)
Primeira variável: [space]a(2 bytes)
Variáveis ​​subsequentes: ,b,(3 bytes)
Use: a(1 byte).

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

(Também economiza colchetes)

Então, isso leva 3n + 10bytes, onde né o número de variáveis. Esse é um alto custo inicial, mas pode render no final. Ele ainda retorna seu valor interno, para que você possa aninhar vários (embora isso rapidamente não valha a pena).

Isso é realmente útil apenas para cálculos intermediários longos em compreensões de lista aninhadas, pois def f():a=...;b=...;returngeralmente será mais curto.

Para 1 valor, isso salva:, uses * length - length - uses - 13portanto, só é útil quando essa expressão é positiva.
Para nexpressões diferentes usadas uvezes no total, onde está o comprimento combinado l, isso economiza:
l - (3 * n) - u - 10 ( + brackets removed )

Artyer
fonte
1

Use uma lista

Declare uma lista como parâmetro e use .append() orpara armazenar o valor: se
lambda s:s.lower()+s.lower()[::-1]
transforma em
lambda s,l=[]:l.append(s.lower())or l[-1]+l[-1][::-1]

Contador de caracteres:

,l=[]5 caracteres
l.append()or13 caracteres
l[-1]5 caracteres para cada uso

Empatar

A quantidade de caracteres adicionados é:
uses*(5-length) + 18 + length
No exemplo anterior, a instrução s.lower()possui 9 caracteres e é usada 2 vezes. A aplicação dessa técnica adicionou 19 caracteres. Se fosse usado 7 vezes, haveria uma redução de 1 caractere.
A quantidade mínima de usos para essa técnica vale a pena é
min_uses = (18+length)/(length-5)

Upsides

  • Permitir uma nova tarefa a um custo ligeiramente reduzido (a lista já está declarada)
  • É um listobjeto para [0], .pop(), [x:y]e outras funções de lista pode ser usado para truques. altamente situacional

Desvantagens

  • Alto custo inicial
  • Alto custo de uso
  • Funciona apenas para usos com comprimento superior a 5

Use um dicionário

thanks @Zgarb A
mesma ideia acima Declare um dicionário como parâmetro e use-o .setdefault()para armazenar (e retornar) o valor: se
lambda s:s.lower()+s.lower()[::-1]
transforma em
lambda s,d={}:d.setdefault(0,s.lower())+d[0][::-1]
Observe que, diferentemente da listcontraparte, setdefaultretorna o valor atribuído.

Contador de caracteres:

,d={}5 caracteres
d.setdefault(k,)16 caracteres
d[k]4 caracteres para cada uso

Empatar

A quantidade de caracteres adicionados é:
(uses-1)*(4-length) + 21
No exemplo anterior, a instrução s.lower()possui 9 caracteres e é usada 2 vezes, aplicando esta técnica adicionamos 16 caracteres. Se fosse usado 7 vezes, haveria uma redução de 1 caractere.
A quantidade mínima de usos para essa técnica vale a pena é
min_uses = 1-21/(4-length)

Upsides / Downsides

  • Basicamente o mesmo que a lista
  • Funciona apenas para usos com comprimento superior a 4

Outras considerações

  • Se essa técnica vale a redução de caracteres, lambdaé provável que ela possa ser descartada e a função ser reescrita com def/ inputpara um programa mais curto.
  • Como o @FlipTack apontou, a lista e o ditado SÃO MANTIDOS entre as chamadas de função, embora isso atrapalhe principalmente, ele pode ser usado em chamadas recursivas. novamente altamente situacional
  • O dicionário sempre será mais curto que a lista.
Cajado
fonte
3
Os envios de funções devem ser utilizáveis ​​mais de uma vez. Atualmente, isso usa a mesma lista sempre que o lambda é executado, o que pode atrapalhar as coisas nas execuções posteriores.
FlipTack
@FlipTack por interesse, você se importaria de vincular uma fonte como a meta? Eu acho que essa regra pode ter algum impacto em alguns truques que eu conheço.
1128 JAD
Eu acho que você pode fazer melhor com um dicionário: lambda s,d={}:d.setdefault(0,s.lower())+d[0][::-1]também é reutilizável.
Zgarb 12/01
Você pode usar list.extendpara adicionar vários elementos ao mesmo tempo, que serão mais curtos do que usando list.appendvárias vezes.
mbomb007
0

Use para definir variáveis ​​e retornar os dados após a operação, como:

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

Compreensões de lista

Este é mais um último recurso, uma vez que é tão desagradável, mas você pode fazer
[<expression> for <variable> in <value>]
para pseudo-definir uma variável em uma lambda. Basicamente, o único ponto positivo desse método é que a expressão interna pode permanecer legível, o que obviamente é a menor preocupação para você quando joga golfe.

Somente ASCII
fonte