Personal tools
You are here: Home Blog (português)
Showing blog entries tagged as: python

Fazendo um pouco melhor (ainda a provinha do GDD)

Ainda não desisti de resolver a prova do GDD em Erlang. Não. Não preciso resolver em Erlang, mas, com tanta gente usando Java, PHP e até PL/SQL pra resolvê-la (e com um amigo que usou Haskell), eu fiquei com vontade.

Também posso repetir uma do ano passado e fazê-la em Lisp.

Então. Erlang é avessa a loops. Loops fazem coisas mudarem de estado e linguagens funcionais não gostam que coisas mudem de estado. O dialeto de Lisp que eu usei ano passado também, mas faz uma concessão e me deixa fazê-los.

O comparador

Assim, a nossa primeira função com loop da prova, cmp_goog, que é assim:

def cmp_googlon(p1, p2):
    v = 'jgptzmqskbclrhdfnvwx'
    for l1, l2 in zip(p1, p2):
        if v.index(l1) != v.index(l2):
            return v.index(l1) - v.index(l2)
    return len(p1) - len(p2)

Ficaria assim:

def cmp_googlon(p1, p2):
    v = 'jgptzmqskbclrhdfnvwx'
    if p1 == p2: return 0
    elif len(p1) == 0 or len(p2) == 0: return len(p1) - len(p2)
    elif p1[0] == p2[0]: return cmp_googlon(p1[1:], p2[1:])
    else: return  v.index(p1[0]) - v.index(p2[0])

Note que, em vez de comparar as strings em um loop, eu comparo só seus primeiros elementos e, se os dois forem iguais, eu chamo o comparador de novo, agora com as strings sem a primeira posição.

Bases numéricas

A outra função serve para nos dar o valor em base 10 de um número em googlon. O original é um clássico, em que você percorre os dígitos do número e vai totalizando os valores de cada casa:

def valor_numerico(p):
    v = 'jgptzmqskbclrhdfnvwx'
    vn = 0
    i = 0
    for c in p:
        vn += v.index(c) * (20 ** i)
        i += 1
    return vn

A idéia é que o valor de um número é o valor do seu dígito menos significativo somado ao produto da base multiplicada pelo valor do resto. No caso de números em base 10, 123 é dado pela soma de 3 com o produto de 10 e 12, sendo que 12 é dado por 2 somado a 10 vezes 1. Transcrito em Python, a nova versão é bem mais concisa:

def valor_numerico_f(p):
    v = 'jgptzmqskbclrhdfnvwx'
    if len(p) == 1:
        return v.index(p)
    else:
        return v.index(p[0]) + 20 * valor_numerico_f(p[1:])

Agora eu preciso de algumas horas para escrever a versão em Erlang. Desejem-me sorte.

Read More…

Solução para a provinha do Google Developer Day 2011 em Python

Esse ano, resolvi tratar com um pouco mais de respeito a prova do GDD. Ano passado, eu resolvi com o prompt do Python aberto, olhando o browser em uma janela e o prompt na outra. Esse ano, as perguntas eram um pouco mais trabalhosas e as strings maiores e isso me fez, em vez disso, escrever um programa. Mais tarde eu coloco o programa no github, mas, por hora, eu transcrevo e comento aqui a minha solução.

A prova apresenta dois textos escritos no idioma Googlon, com os quais vamos trabalhar, e quatro perguntas. Para ajudar (eles são realmente bonzinhos), eles dão as soluções para o primeiro texto, o que ajuda a testar se o código está correto.

Letras foo, bar e preposições

A primeira pergunta explica que algumas letras são chamadas de "letras foo" e todas as demais, "letras bar" e que preposições são palavras de cinco letras que começam com uma letra bar e não contém a letra "q" (é muito provável que a sua prova seja diferente da minha e que elas sejam geradas aleatoriamente - é como eu faria). Para ajudar a resolver, eu escrevi algumas funções

def foo(l):
    return l in "snrbg"

def bar(l):
    return not foo(l)

def preposicao(p):
    return len(p) == 5 and bar(p[-1]) and 'q' not in p

Aí, testar a resposta dada no enunciado é fácil:

>>> palavras_a = texto_a.split(' ')
>>> len(filter(preposicao, palavras_a))
63

Assim como responder a pergunta:

>>> palavras_b= texto_b.split(' ')
>>> len(filter(preposicao, palavras_b))
57

Verbos e verbos em primeira pessoa

A segunda pergunta ensina que verbos são palavras de 6 letras que terminam em uma letra bar. Se ele também começar com uma letra bar, estará em primeira pessoa.

def verbo(p):
    return len(p) >= 6 and bar(p[-1])

def verbo_primeira_pessoa(p):
    return verbo(p) and bar(p[0])

Agora podemos testar nosso código com o dado do enunciado:

>>> len(filter(verbo, palavras_a)) == 216
True
>>> len(filter(verbo_primeira_pessoa, palavras_a)) == 160
True

E descobrir a nossa própria resposta:

>>> len(filter(verbo, palavras_b))
224
>>> len(filter(verbo_primeira_pessoa, palavras_b))
154

No meu caso, eu tinha 224 verbos, dos quais 154 em primeira pessoa.

Vocabulário

Agora o problema pede para criar uma lista com o vocabulário, ordenado segundo o alfabeto googlon. Para isso, eu vou usar o ordenador que já vem embutido nas listas do Python e vou produzir uma função de comparação. Essa função pode ser facilmente plugada em seu próprio sort, se você quiser muito:

def cmp_googlon(p1, p2):
    v = 'jgptzmqskbclrhdfnvwx'
    for l1, l2 in zip(p1, p2):
        if v.index(l1) != v.index(l2):
            return v.index(l1) - v.index(l2)
    return len(p1) - len(p2)

E podemos testar com os dados do enunciado:

>>> vocabulario_a_unsorted = list(set(palavras_a))
>>> ' '.join(sorted(vocabulario_a_unsorted, 
...                        cmp = cmp_googlon)) == vocabulario_a
True

Nota: ao construir um set com as palavras do texto A, eu eliminei as repetições. Como objetos do tipo set não são ordenáveis, eu transformei o conjunto em uma lista. 

Agora podemos encontrar nossa resposta:

>>> vocabulario_b_unsorted = list(set(palavras_b))
>>> vocabulario_b = ' '.join(sorted(vocabulario_b_unsorted, cmp = cmp_googlon))
>>> vocabulario_b
'jgspd jgv jpgzkx jzvjw jmrmlq jmdxx jmntpzq jqw jspk jkc jbb jcphbk jch jcv jlkm...

Números

Agora a prova nos explica que todas as palavras em googlon tem um valor numérico. Em googlon, os números são escritos do dígito menos significativo para o mais significativo e em base 20. Pra isso, precisamos de mais uma função:

def valor_numerico(p):
    v = 'jgptzmqskbclrhdfnvwx'
    vn = 0
    i = 0
    for c in p:
        vn += v.index(c) * (20 ** i)
        i += 1
    return vn

que, podemos testar contra o enunciado:

>>> valor_numerico('blsmgpz') == 262603029
True

Essa parte está certa. Mas a pergunta pede para contarmos os números bonitos. Para eles, números bonitos são divisíveis por 5 e maiores que 492528.

def numero_bonito(p):
    return valor_numerico(p) > 492528 and (valor_numerico(p) % 5 == 0)  

Agora podemos testar:

>>> len(filter(numero_bonito, vocabulario_a.split(' '))) == 75
True

E chegar na nossa resposta:

>>> len(filter(numero_bonito, vocabulario_b.split(' '))) 
71

Conclusão

Foi difícil? Nem um pouco. Se você não conseguiu responder por conta própria, precisa estudar mais. Foi mais trabalhosa do que a do ano passado? Um pouco. Por outro lado, ela tinha informações suficientes para você poder testar suas próprias soluções - e isso ajudou quem teve mais problemas para resolver a prova a aprender alguma coisa. Prova boa é assim - você aprende enquanto faz.

Uma última observação: eu gosto de list comprehensions, mas também gosto de map, filter e reduce. O código fica mais limpo quando eles são bem empregados.

Read More…

Sobre a provinha do Google Developer Day 2011

Ano passado, quando a Google abriu as inscrições para o GDD, eu escrevi o primeiro de uma série de três artigos sobre a tal provinha. Esperei até o final das inscrições (e o sinal verde de um amigo que trabalha lá) para publicá-lo.

Não vou fazer diferente esse ano. Se você veio aqui querendo ver a solução dos problemas do ano passado, eles estão relacionados abaixo. Se, por outro lado, chegou aqui procurando um jeito fácil de entrar no evento, devia ter vergonha. Eu respeito a decisão da Google de usar a prova como uma forma de garantir que a platéia tenha melhores chances de entender o que o palestrante vai falar e não vou ajudar a estragar o processo de seleção de convidados deles. Quando as inscrições estiverem encerradas, eu volto aqui e publico a minha solução e aí você pode comparar com a sua.

De resto, boa sorte. A prova não é difícil e eu tenho certeza que você consegue. Você já chegou até aqui, afinal.

Um abraço e até lá.

Nota: Se você colocar aqui um comentário mostrando uma solução antes que sejam fechadas as inscrições, eu vou apagar pela mesma razão que me fez não publicar a minha ainda. Se você tem uma solução, volte depois.

Outra nota: Agora, que as inscrições estão encerradas, você pode achar soluções procurando no Google. Uma delas (em Java) foi postada em um comentário aqui. A mihha em Python está aqui. Há outras, algumas com escolhas de linguagem ainda mais estranhas do que a minha versão para computadores de 8-bits do ano passado. Acho uma pena que tantos tenham praticado GDD (Google-Driven Development, pun intended) e tenham googlado por respostas em vez de aprender a programar.

Read More…

A "provinha" do Google Developer Day

Algumas pessoas acharam estranho o processo de inscrição para o Google Developer Day. Nas edições anteriores, você preenchia um formulário, contava o que você fazia e onde trabalhava e ficava nisso. Os primeiros a se inscreverem iriam ao evento e pronto. Os menos atentos ganhavam a lista de espera. Agora, além do formulário, você tem que mandar um currículo e resolver alguns problemas que exigem familiaridade com alguma ferramenta de programação.

Não sei se foi essa a intenção da Google, mas, com esse processo de inscrição, eles têm seus dados de contato, seu currículo e sua avaliação em uma prova.

Mas não se anime. Essa prova não deve ser a que o RH do Google usa. Eu, pessoalmente, acredito que ela apenas existe para separar quem vai entender alguma coisa do GDD de quem não vai entender nada e evitar, com isso, que gente que poderia melhor aproveitar o evento fique de fora.

Agora que as inscrições foram fechadas e o prazo para a entrega das provas terminou, eu me sinto à vontade para publicar esse post.

A prova

A prova tem 5 questões, um pouco diferentes para cada candidato. Eu não sei se foi honesto, mas eu resolvi as minhas com uma janela e um interpretador Python do lado. E eu vou fazer o mesmo aqui.

O que esse programa faz?

x = 7
y = 4
if y > 2, then
   y = y * 2
else
   x = x * 2
print (x + y)

Esse é facil. Na janela do ipython:

In [1]: x = 7

In [2]: y = 4

In [3]: if y > 2:
   ...:     y = y * 2
   ...: else:
   ...:     x = x * 2
   ...:     
   ...:     

In [4]: x + y
Out[4]: 15

Quantas vezes esse programa imprime "hello"?

Uma observação: os loops incluem as extremidades.

for i = 1 to 5
   if i != 2, then
      for j = 1 to 9
         print 'hello'

De novo, com o mesmo truque (só que em Python, o range não inclui a extremidade maior):

In [1]: for i in range(1, 6):
   ...:     if i != 2:
   ...:         for j in range(1, 10):
   ...:             print 'hello'
   ...:             
   ...:             
hello
hello
hello
hello
...

Erm... Chato assim. Vamos tentar de outro jeito

In [1]: linha = 1

In [2]: for i in range(1, 6):
   ...:     if i != 2:
   ...:         for j in range(1, 10):
   ...:             print linha, 'hello'
   ...:             linha += 1
   ...:             
   ...:             
1 hello
2 hello
3 hello
4 hello
(...)
36 hello

Agora sim. Mesmo sem o computador, de olhar dá pra sacar que o primeiro loop roda 5 vezes e o segundo, 9. Como a condição que pula o segundo loop pula uma iteração (nem todos os testes fazem isso), o programa imprime 4 x 9 hello's.

Quais números, entre 5 e 2675 são pares e divisíveis por 3?

O exercício diz que eu posso escrever um programa para isso. Deve ser pra acomodar o pessoal que não vive sem um compilador.

In [1]: len([ n for n in range(5, 2676) if n % 2 == 0 and n % 3 == 0 ])
Out[1]: 445

Mas isso foi porco. Quem estava acordado no ginásio vai lembrar que se um número é par (divisível por 2 - essa é do primário) e divisível por 3, ele é divisível por 6. Assim, podemos simplificar nossa solução:

In [2]: len([ n for n in range(5, 2676) if n % 6 == 0 ])
Out[2]: 445

Podemos respirar aliviados agora que vimos que o resultado continua o mesmo.

Números bonitos

A pergunta nos apresenta Barbara e seus critérios para que números sejam ou não bonitos. Para ela, números são bonitos se contiverem um dígito 4 e não contiverem um dígito 9. Ao final, nos pergunta quais números entre 14063 and 24779, inclusive, são bonitos.

Para saber quantos são bonitos, podemos começar com uma função:

In [1]: def bonito(x):
   ...:     return '4' in str(x) and '9' not in str(x)
   ...:

E podemos testá-la, para ver se  fizemos tudo direito:

In [2]: bonito(4)
Out[2]: True

In [3]: bonito(9)
Out[3]: False

In [4]: bonito(49)
Out[4]: False

In [5]: bonito(1491)
Out[5]: False

Daí, basta aplicá-la usando a mesma técnica que usamos no problema anterior:

In [6]: len([ n for n in range(14063, 24780) if bonito(n) ])
Out[6]: 3047

Se você preferir, pode fazer tudo em uma linha:

In [7]: len([ n for n in range(14063, 24780) if '4' in str(n) and '9' not in str(n) ])
Out[7]: 3047

Ou, ainda:

In [8]: len(filter( lambda x: '4' in str(x) and '9' not in str(x), range(14063, 24780)))
Out[8]: 3047

Os telefones

A última pergunta nos apresenta um país em que os números de telefone têm 6 dígitos. Números não podem ter dois dígitos consecutivos idênticos, porque isso é caído. A soma dos dígitos tem que ser par, porque isso é legal e o último dígito não pode ser igual ao primeiro, porque isso dá azar.

Vamos começar com os caídos

In [1]: def caido(x):
   ...:     for i in range(0, len(str(x))):
   ...:         if str(x)[i] == str(x)[i - 1]:
   ...:             return True
   ...:     return False
   ...:

Agora vamos para os legais

In [2]: def legal(x):
   ...:     return sum([ int(n) for n in str(x) ]) % 2 == 0
   ...:

E, se olharmos a função "caido", vamos ver que ela considera caídos os números que dão azar.

In [3]: caido(123451)
Out[3]: True

Assim, basta usarmos duas no nosso critério:

In [4]: len([ n for n in range(100000, 1000000) if not caido(n) and legal(n) ])
Out[4]: 238500

Mas o exercício não perguntou quantos caídos entre 100000 e 999999. Eles nos deu uma lista. Com um pouco de mágica de clipboard, colocamos os números em uma string, que quebramos e fazemos uma lista:

In [5]: tudo = '''214966
   ...: 215739
   ...: 220686
   ...: 225051
   ...: 225123
   ...: 226810
   ...: 228256
(...)
   ...: 720576
   ...: '''

In [6]: tudo
Out[6]: '214966\n215739\n220686\n225051\n225123\n...720202\n720568\n720576\n'

Opa! Tem um '\n' no final do qual precisamos nos livrar

In [7]: numeros = tudo.split('\n')[:-1]

In [8]: len(numeros)
Out[8]: 200

Então, usamos a lista e chegamos no resultado:

In [9]: len([ n for n in numeros if not caido(n) and legal(n) ])
Out[9]: 61

61 de 200 parece razoável.

Motivo para pânico?

Não desanime se seus números forem muito diferentes dos meus. Os enunciados variam de teste para teste. Além disso, até agora eu não recebi confirmação da minha inscrição. Isso pode indicar que eu errei tudo.

Boa sorte!

Read More…

Quem for, me conta como foi

Essa semana rola (quinta, sexta e sábado), no Rio de Janeiro, a PyConBrasil.

Para quem não sabe (e não quis clicar no link) a PyConBrasil é uma conferência anual voltada aos usuários, curiosos e simpatizantes da linguagem de programação Python.

Python é uma linguagem poderosa, completa e fácil de aprender (eu consigo ensinar um programador a dar os primeiros passos em Python em coisa de duas horas - menos, se ele for esperto) e que está no centro de coisas muito legais que eu uso todos os dias como o Zope, o Plone, o Django e o Google App Engine. É duca.

Então... Eu estou aqui, olhando para a grade de palestras e lamentando minha inépcia ao negociar contratos que me custou uma ida ao Rio.

Então... Quem for, por favor, me conte se foi legal.

Read More…

OpenID e Mission Creep

Posted by Ricardo Bánffy at Sep 10, 2008 09:10 AM |

"Mission Creep" é um termo militar usado quando o escopo de uma missão se expande além do originalmente planejado. Foi usado originalmente para descrever a operação de ajuda humanitária à Somália em 1992 que terminou no que poderia ser descrito como uma guerra civil.

OpenID é um padrão de autenticação cross-site que deixa você usar suas credenciais do Google, do Yahoo e de mais um monte de outros para se autenticar em outros sites participantes do padrão (como esse que você está lendo).

Ontem eu resolvi consertar o problema que estava acontecendo com o OpenID - usuários de Yahoo e Google não conseguiam se autenticar para deixar comentários no blog. No início, a coisa parecia simples: impor números de versão no arquivo de configuração do sistema de buildout e deixar que ele resolvesse tudo sozinho.

Não funcionou. O buildout queria instalar duas versões diferentes não importando o que eu dissesse no arquivo de configuração

Instalar os eggs manualmente e fingir que nada aconteceu também não funcionou. O Plone levantava e "não dizia coisa com coisa" (não era capaz de renderizar nenhum template)

A solução foi atualizar um dos componentes do sistema de buildout (o componente responsável pela "receita" de como se faz um Plone), rodar o sistema de buildout e deixá-lo fazer tudo por conta própria.

Dessa vez deu certo, mas eu acabei com uma versão nova do Plone (3.1.5.1) e de todos os outros produtos instalados nele.

Eu tinha um problema com uma biblioteca que era parte de um dos dois sistemas de login do site. Terminei com versões novas de tudo.

Crianças... Nunca façam isso em servidores de produção. Só não foi mais emocionante porque eu tinha bons backups.

Read More…

Djangocon

Posted by Ricardo Bánffy at Sep 08, 2008 10:10 AM |

Estava sábado na Djangocon, que foi transmitida ao vivo para o escritório da Google em São Paulo. Obrigado ao Rodolfo, que interrompeu as férias dele para fazer isso acontecer, e a todo o pessoal da Google, que cedeu infra, comida e tudo o que foi necessário para a coisa acontecer tanto aqui como lá.

A diferença de horário mata um pouco - o evento rola até as 23 horas (aqui em GMT-3) - mas as palestras de ontem foram bem interessantes. Quem sabe ano que vem rola a transmissão dos dois tracks simultâneos. Quem sabe, no ano que vem, eu consigo assistir mais delas. A organização promete que elas estarão disponíveis no Google Videos. Eu acredito. O pessoal é bom.

Se você não conhece Django, azar o seu. Você poderia estar fazendo mais, melhor e com menos esforço.

Read More…