Páginas

Sunday, November 9, 2008

A Neighborhood of Infinity: On writing Python one-liners.

Graças ao meu orientador Carlo Emmanoel, cheguei a um post [1] no blog do sigfpe sobre como escrever programas em Python em apenas uma linha.

Não que eu ache isso realmente útil, mas é no mínimo um desafio interessante. É de longa data, e independente de linguagem, ver programadores tentando mostrar as qualidades de sua linguagem predileta, e um dos "recursos" em pauta costuma sempre ser os one-liners...

A solução proposta pelo sigfpe é centrada no uso de lambdas (e impressionante como tem gente que não conhece isso no Python, como explicitado por alguns dos comentários no post original). Porém, assim que li o título do post na minha inbox do delicious, tive uma idéia diferente.

É simples, mas permite escrever QUALQUER CÓDIGO em apenas uma linha. Não sei se vão falar que "burlei as regras", porém é uso de Python puro, logo, para mim está valendo!
Basicamente, podemos pegar um trecho qualquer de código-fonte e transformá-lo em uma string. Uma string pode ser arbitrariamente grande e sempre caber em uma única linha.
Em seguida, usamos o provavelmente também pouco conhecido statement exec para executar a string com o código.

Vamos fazer isso para cada exemplo do post original:
# Primeiro o código original da função que retorna um incremento
# de um número executada para 20 números

def inc(n):
return n+1

print "1.",map(inc,range(0,20))

# E a solução usando lambda...

print "1.",map(lambda x:x+1,range(0,20))

# Minha solução usando exec...

exec compile('def inc(n):\n return n+1\n\nprint "1.",map(inc,range(0,20))\n', '<string>', 'exec')
# Aqui a idéia para simular variáveis locais com lambdas

def f1(n):
m = 2*n+1
return m+m*m

print "2.",map(f1,range(0,20))

print "2.",map(lambda n:(lambda m:m+m*m)(2*n+1),range(0,20))

# A solução com exec é inconveniente pois ocupa muito espaço,
# já que é preciso preservar os espaços em branco da indentação

exec compile('def f1(n):\n m = 2*n+1\n return m+m*m\n\nprint "2.",map(f1,range(0,20))\n', '<string>', 'exec')
def f(n):
def g(n):
return 2*n+1

def h(n):
return 3*n-2

return g(h(g(h(n))))

print "3.",map(f,range(0,20))

print "3.",map(lambda n:(lambda g,h,n:g(h(g(h(n)))))(lambda n:2*n+1,lambda n:3*n-2,n),range(0,20))

# Note também que a solução com exec é "burra", estamos apenas escrevendo
# o código com quantidade de linhas arbitrárias em uma única linha e mandando
# o Python interpretar

exec compile('def f(n):\n def g(n):\n return 2*n+1\n\n def h(n):\n return 3*n-2\n\n return g(h(g(h(n))))\n\nprint "3.",map(f,range(0,20))\n', '<string>', 'exec')
def fact(n):
if n<=1: return 1 else: return n*fact(n-1) print "4.",map(fact,range(0,20)) print "4.",map(lambda n:(lambda f:f(f,n))(lambda f,n:{True:lambda:1,False:lambda:n*f(f,n-1)}[n<=1]()),range(0,20)) # Na prática não há diferença entre o código em várias linhas e o código # one-liner. Podem ocorrer problemas para trechos de código mais longo, # onde a abordagem trivial de pegar e string do código e rodar não vai funcionar exec compile('def fact(n):\n if n<=1:\n return 1\n else:\n return n*fact(n-1)\n\nprint "4.",map(fact,range(0,20))\n', '<string>', 'exec')

O processo é realmente mecânico e imediato, tanto que criei um pequenino script:
'''
onelify.py :: Transform arbitrary Python code into an one-liner equivalent.

2008/11/09 - Initial release

Rodolfo Henrique Carvalho :: lifeatmymind.blogspot.com
'''

def onelinefy(code):
return "exec compile(%s, '<string>', 'exec')" % (repr(code),)

[1] A Neighborhood of Infinity: On writing Python one-liners.

UPDATE: 2008/12/26 - Agora usando o Google Code Prettifier para colorir a sintaxe!

1 comment:

Unknown said...

hahahahahah...robou mto com exec!
hahahahahahahaha...mto bom!