Desenferrujando

2008-08-22

Se você gosta de programar e adora Perl… nah… mesmo que você odeie Perl, pare o que está fazendo e assista já à entrevista do Damian Conway.

São meros 35 minutos em que ele fala do seu PhD em biologia computacional, do sotaque “errado” dos americanos, de linguagens de programação em geral e de Perl 6 especificamente. Eu nunca li ou ouvi uma explicação mais interessante sobre a diferença entre tipagem estática e tipagem dinâmica.

E o final da última resposta merece até uma tentativa de tradução:

…para ser um bom programador você tem que efetivamente programar. E isso é algo que não acontece. Sabe, a gente estuda computação, a gente aprende todas aquelas coisas e fica o tempo todo fazendo exercícios e provas. Daí a gente se forma e começa a freqüentar reuniões, a desenhar modelos, diagramas e todo o resto e você pára de programar. E se você é promovido, então você é literalmente promovido a perder a oportunidade de continuar programando e eu acho que isso é um problema. Se você quer ser um jogador de tênis realmente bom, você vai treinar todos os dias. Se você quer ser um grande lutador de artes marciais você vai pro tatame todo santo dia. Se você quer ser um grande programador, você vai programar todo dia—mesmo que você tenha que usar seu próprio tempo pra isso. Se você está acordado, você é um programador. Se você está acordado às 11 horas da noite, ou às 3 da manhã, você tem que usar parte desse tempo para programar porque assim que você começa a enferrujar você começa a morrer como um programador.


Desafio regex

2008-04-07

Meu amigo Rogério Zamboim me desafiou a resolver um problema com uma expressão regular. Desconfio que isso estava lhe causando insônia e ele queria transferi-la pra alguém… De qualquer modo, o desafio acabou sendo bem interessante.

O problema é validar um campo cujo conteúdo deve ser uma seqüência de dígitos, de 1 a 8, separados por barras invertidas, em ordem monotônica decrescente. Exemplificando, os seguintes valores são válidos para o campo:

8\7\6\5\4\3\2\1
8
8\1
8\7\2
1
2\1
7\5\3

Já os seguintes valores são inválidos:

8\
\7
8\6\
\2\1
6\\2
7\6\6\2
6\2\5

Confesso que de cara dei um tiro n’água e enviei-lhe a seguinte “solução”, obviamente errada:

   /^[1-8](?:\\[1-7])?(?:\\[1-6])?(?:\\[1-5])?(?:\\[1-4])?(?:\\[1-3])?(?:\\[1-2])?(?:\\1)?$/

O problema é que ela não garante que os dígitos são decrescentes. Por exemplo, a string “4\5” é aceita por ela.

Pensando melhor, ficou claro que expressões regulares “puras” não têm poder de expressão suficiente pra especificar a regra de validação de modo conciso. Isso porque elas não conseguem expressar a ordenação dos dígitos. Não se trata de um impossibilidade teórica, mas prática. Uma solução puramente regular envolveria a descrição explícita de todas as possibilidades, resultando numa expressão enorme. Algo como:

   /^(?:[1-8]|[2-8]\\1|[3-8]\\2|...|[3-8]\\2\\1|[4-8]\\3\\1|.../

Convencido de que não seria possível uma solução direta, resolvi usar uma expressão regular apenas para garantir a forma básica, i.e., uma seqüência de dígitos separados por barras invertidas, e deixar a verificação da ordem para um código posterior. O melhor que consegui foi o seguinte:

   sub validate_loop {
       my ($string) = @_;
       my $top = 9;
       for my $val (split /\\/, $string) {
           return 0 unless $val =~ /^[1-8]$/;
           return 0 unless $top > $val;
           $top = $val;
       }
       return 1;
   }

Esta função recebe uma string com o valor do campo. A variável $top contém sempre o valor máximo que o próximo dígito pode conter mais um, e começa com 9, já que o primeiro dígito pode estar entre 1 e 8. O loop quebra a string nas barras invertidas e verifica se o que há entre elas são dígitos entre 1 e 8 e se eles são menores que $top, atribuindo a $top o valor do próximo dígito.

É razoável, mas não satisfatório. Usa duas expressões regulares e faz muitos testes separados… Não estava muito bom.

Fiquei matutando uns dois dias sobre isso e pensando se dentre as várias extensões que Perl oferece além dos operadores básicos de expressões regulares não haveria algum que me permitisse construir uma solução mais sucinta. Relendo a documentação deparei-me com o operador “(?{code})“, que permite inserir código no meio de uma expressão regular. Hmmm… parecia útil, mas eu nunca havia usado algo assim.

Depois de algumas tentativas frustradas acabei bolando a seguinte solução:

   sub validate_re {
       my ($string) = @_;
       our ($dec, $last) = (1);
       return ($string =~ /^([1-8])(?{$last=$^N})(?:\\([1-8])(?{$dec=0 if $last <= $^N; $last=$^N}))*$/) && $dec;
   }

A expressão regular ficou maior por causa do código embutido dentro dela. Remova mentalmente os operadores (?{…}) e você verá que ela está simplesmente verificando se a string consiste em uma sequência de dígitos separados por barras invertidas.

No primeiro operador de código, a variável $last recebe o valor do primeiro dígito da string. (A variável implícita $^N lembra o valor da última captura por parêntesis.) No segundo operador, usamos seu valor para verificar se o próximo dígito é menor que o anterior e atribuímos o novo dígito a ela.

A variável $dec mantém o estado da verificação de ordem. Ela começa como 1 e recebe 0 caso o segundo operador de código detecte que há algum dígito fora de ordem monotônica decrescente.

Eu demorei um bom tempo pra chegar à conclusão de que essas duas variáveis tinham que ser globais (declaradas com “our”). Se elas são locais (declaradas com “my”) não funciona. A documentação não é clara a esse respeito e eu desconfio que seja um bug do próprio interpretador Perl, mas não tenho certeza.

A função acaba retornando a conjunção da avaliação da expressão regular com o valor final de $dec.

Pronto, agora eu posso dormir em paz.


Evidence Based Scheduling

2007-11-08

Eu não acredito em cronogramas, mas que eles existem, existem. Bem, nem todo cronograma é furado… os ferroviários ingleses que o digam. 🙂

O Joel Spolsky, que é um cara inteligente e experiente, diz que os desenvolvedores de software em geral não gostam de fazer cronogramas por duas razões. Primeiro, porque é um saco. Segundo, porque ninguém acredita mesmo que o cronograma seja realístico.

Mas, como dono de uma empresa de software, o Joel deve ter seu “lado administrador” sempre procurando encontrar uma forma de “controlar as coisas”. Sabe como é: planejamento, planilhas, cronogramas…

O fato é que ele bolou um novo método de produzir cronogramas baseado no método de Monte Carlo e conseguiu me fazer acreditar que funcione. Ele chamou o método Evidence Based Scheduling, ou EBS, que parece bastante apropriado.

Para os que gostam (e principalmente para os que, como eu, não gostam) de cronogramas, eu sugiro a leitura do artigo em que o Joel explica detalhadamente o método. Para os demais (hã?) aqui vai um resumo:

O início não tem novidade. O grupo de desenvolvedores que vai trabalhar num projeto faz uma WBS detalhada do mesmo, quebrando-o em atividades de, no máximo, 16 horas. Cada atividade é atribuída a um desenvolvedor e é ele quem decide o tempo que vai levar para executá-la.

Todas as estimativas devem ser registradas e associadas ao desenvolvedor que as fez. É importantíssimo que cada desenvolvedor registre também o tempo que ele efetivamente gastou na execução das tarefas. Deste modo, ao longo do tempo, vai-se acumulando informações sobre a capacidade de “estimação” de cada desenvolvedor.

Depois de algum tempo, é possível plotar um gráfico para cada desenvolvedor, correlacionando suas estimativas com suas respectivas execuções. A idéia é que “estimadores” experientes tendem a cometer sempre os mesmos erros. Já estimadores inexperientes tendem a variar bastante nos seus erros.


De posse desse histórico e do conjunto de estimativas da WBS do novo projeto, o sistema gera 100 simulações diferentes usando, cada vez, um valor aleatório do histórico de erros passados de cada desenvolvedor para projetar o tempo de execução efetivo de cada atividade. O resultado dessas 100 simulações é um gráfico mostrando a probabilidade de que o projeto termine em um intervalo de datas dentro do qual caíram todas as 100 simulações.


O que é bacana neste método é que ele não determina uma data mágica, mas sim um intervalo de datas com probabilidades geradas a partir do histórico de sucesso de cada desenvolvedor individualmente.

O artigo original mostra várias outras vantagens do método e sugere algumas técnicas gerais para tornar o exercício mais previsível. Vale realmente a pena lê-lo.

Eu estou convencido de que o método tem mérito, mas isso não quer dizer que ele funcione. É óbvio que ele depende fortemente de que os desenvolvedores registrem suas estimativas e suas execuções o mais fielmente possível e também que produzam muitas estimativas pra aumentar a qualidade do histórico. Mas mais que tudo, o método supõe que a capacidade de estimativa dos desenvolvedores tenda a se estabilizar à medida em que eles ganham experiência. Isso parece fazer sentido, mas não conheço estudos empíricos que validem esta suposição.

Outra coisa importante é que não dá pra fazer as simulações na mão. É preciso usar um sistema que automatize o processo. Mas o Joel cuidou disso também. Ele adicionou um módulo de EBS no FogBugz 6.0, a nova versão da sua ferramenta de gestão de projetos. 🙂


Podcasts que irritam

2007-04-22

Só há uma coisa mais irritante do que uma coisa extremamente irritante: uma coisinha um pouco irritante que a gente tem que enfrentar um monte de vezes. Das coisinhas que me irritam, a mais chata são os podcasts que não identificam adequadamente seus arquivos mp3 com tags id3.

Dois podcasts me irritam: os da CBN e o do Diogo Mainardi, na Veja. O problema dos podcasts da CBN é que vêm todos com um título (Rádio CBN) e um álbum padrão (CBN – A rádio que toca notícia). Isso é quase que completamente inútil. Costumo carregar meu ipod uma vez por semana e geralmente tenho uns 20 comentários da CBN pra ouvir. Como eles têm todos a mesma informação, não dá pra saber nem a data e nem de que comentarista é cada podcast. O problema do podcast do Mainardi é mais simples: o pessoal da Veja simplesmente não põe informação alguma nos mp3.

Cansado de ter que classificá-los manualmente pela interface do gtkpod, procurei por alguma ferramenta de linha de comando que pudesse me ajudar. O mp3info, que vem no pacote de mesmo nome no meu Ubuntu Linux, parece perfeito. Escrevi um pequeno script Perl (podcast-fix) pra automatizar o processo. O script recebe uma lista de arquivos mp3 como argumentos. Ele procura identificar dentre eles os podcasts da CBN e do Mainardi e invoca o mp3info para inserir as tags id3 que eu quero. Como eu baixo meus podcasts todos num único diretório, pra corrigir as tags dos arquivos problemáticos basta eu chamar o script assim:

podcast-fix ~/casts/*

Pronto. Uma irritação a menos na minha vida. 🙂


Literate Commenting

2006-11-20
Literate programming is a programming methodology that combines a programming language with a documentation language, making programs more robust, more portable, and more easily maintained than programs written only in a high-level language.
Donald E. Knuth

Literate Programming é uma idéia genial. Se você é programador e gosta da arte de programar, sugiro que leia o livro ou que vá direto à fonte.

Por volta de 1995, quando eu estava lendo o livro, tive muita vontade de experimentar a metodologia. Mais ou menos na mesma época eu estava ouvindo o disco Aqualung do Jethro Tull. Na capa do disco havia um texto, meio que um poema, que me interessou–talvez porque eu não tenha conseguido entendê-lo. Algumas palavras do texto chamaram a minha atenção, não por sua relação com o texto em si, mas por sua relação com o meu trabalho como programador. Daí me veio a idéia de tentar usar o texto como comentários de um programa. O programa não precisaria fazer muito sentido, mas precisaria ser compilável e “casar” o mais perfeitamente possível com os comentários.

Até que o exercício não foi muito difícil. Depois da primeira versão, corrigi alguns poucos bugs e, modéstia às favas, fiquei bastante satisfeito com o resultado. Cheguei até a pensar em mandá-lo para o Knuth como sugestão para ele usá-lo como exemplo em algum novo livro seu, mas desisti. Possivelmente ele diria que isto está mais para Literate Commenting que para Literate Programming.

De qualquer modo, eis o resultado do meu esforço. Ah! Estou aberto para receber críticas e sugestões. As modificações incorporadas em futuras versões do programa terão suas fontes devidamente citadas. 🙂

#include <cassert>

typedef int (*Entity)();

int Man()
{
// 1. In the beginning Man created God;
// and in the image of Man
// created he him.

    Entity God = Man;

// 2. And Man gave unto God a multitude of
// names, that he might be Lord of all
// the earth when it was suited to Man

    Entity &Lord = God;
    Entity &Jeovah = God;
    Entity &Alah = God;
    Entity &Rah = God;

    static bool suited = true;

    if (suited)
        suited = false;
    else
        return 0;

// 3. And on the seven millionth
// day Man rested and did lean
// heavily on his God and saw that
// it was good.

    for (unsigned day=0; day != 7000000; ++day);

    assert(God != 0);

// 4. And Man formed Aqualung of
// the dust of the ground, and a
// host of others likened unto his kind.

    char *Aqualung = new char[1024];

    char *host[10];

    for (int i=0; i < 10; ++i)
        host[i] = new char[1024];

// 5. And these lesser men were cast into the
// void; And some were burned, and some were
// put apart from their kind.

    void *lesser[10];

    for (int i=0; i < 10; ++i) {
        if ((i % 2) == 0)
            delete[] host[i];
        else
            lesser[i] = static_cast(host[i]);
    }

// 6. And Man became the God that he had
// created and with his miracles did
// rule over all the earth.

    God();

// 7. But as all these things
// came to pass, the Spirit that did
// cause man to create his God
// lived on within all men: even
// within Aqualung.

    char *Spirit = “main”;

    Aqualung = “main”;

// 8. And man saw it not.

    bool saw = false;

// 9. But for Christ’s sake he’d
// better start looking.

    assert(saw);
}

main()
{
    Man();
}