Início > Programação > Do What I Mean

Do What I Mean

O DWIM – assim como o TIMTOWTDI (Tim Toady) – é uma das coisas mais interessantes do Perl… e algumas pessoas sabem como eu adoro esses dois acrônimos.

Só que algumas vezes você só percebe a beleza da coisa quando ela acontece na sua frente e você tem o seu momento “ah-há!” :)

Esse caso aconteceu recentemente na lista da minha turma de faculdade, onde estávamos reunindo os nomes dos professores com quem tivemos aula para incluir no nosso convite de formatura. No final, a lista estava pronta mas alguns nomes estavam fora de ordem.

Foi então que um dos meus amigos disse que fazia um programa de 2 linhas em Python (uma só para a leitura dos dados) e após isso eu pensei: “E como será que eu consigo fazer isso da menor (e melhor) maneira possível em Perl?!”. Um parênteses aqui… infelizmente algumas pessoas acharam que isso era uma disputa enfurecida para atacar o princípios éticos e morais de outras pessoas… quando era apenas uma brincadeira, entre amigos, utilizando linguagens/ferramentas – e quem sabe filosofias de desenvolvimento – para resolver um problema. Isso é tão sério quanto um futebol entre amigos… então a menos que você de um chilique quando seu time toma um gol, você não deveria se estressar muito com esse tipo de “disputa” entre linguagens.

Voltando ao problema, apenas formalizando:

Entrada: A partir da entrada padrão (stdin), ler várias linhas onde cada uma contem um nome completo (nome e sobre-nomes).

Saída: Imprimir na saída padrão (stdout), os nomes ordenados em ordem alfabética.

Então minha primeira tacada foi (com 45 caracteres de código):

perl -E "while(<>){chomp;push@n,$_;};say for(sort @n);" -- < nomes.txt

Talvez assim pareça meio confuso ainda, mas o que eu fiz basicamente ali foi utilizar o interpretador do Perl com a opção -E para ele executar o código entre aspas e fazendo o stdin ler do arquivo nomes.txt (– < nomes.txt). O código mesmo é esse:

while(<>){chomp;push@n,$_;};say for(sort @n);

Que talvez fique mais claro assim:


while(<>) {
  chomp;
  push@n,$_;
};
say for(sort @n);

Que basicamente significa: “Enquanto ainda tiver algo para ser lido, leia então remova o line breaker (chomp) e então adicione no array @n (push). Após isso, ordene o array @n e para cada entrada diga o conteúdo na tela.“.

O chomp foi por costume de ser utilizado ao ler coisas da entrada e o say foi para economizar 2 caracteres no lugar do print. Mas agora enquanto escrevo esse post percebi que os 2 ponto-e-vírgula, após o while e após o say são desnecessários… então essa solução poderia ter muito bem 43 caracteres :)

Continuando… aqui comecei a roubar um pouco, pois utilizei duas opções do interpretador do Perl (e com isso fomos para 31 caracteres):

perl -nlE "push@n,$_;END{say for(sort @n)}" -- < nomes.txt

Mas que merda é essa?! O.o

Eu sei calma… a coisa parece que ficou feia, mas vamos tentar explicar isso ai :D

Mas antes eu gostaria de dizer uma coisa… acho que são por essas coisas que programadores Perl fazem, que a linguagem ganha uma fama ruim de ser coisa do capeta e incompreensível. Mas isso não é código de produção… é código onde os próprios programadores querem ver até onde sua linguagem consegue ir (e eu diria que no caso de Perl é ao infinito e além) e isso é algo incrível da comunidade Perl: os programadores gostam de brincar com sua linguagem, gostam de desafiar e tentar dobrar, entortar, puxar e torcer ao máximo sua ferramenta de trabalho para poderem conhecer tudo que ela pode oferecer (será que um dia irão fazer um Will it Blend? do Perl? :P) . Então, não se preocupe Perl utilizado em desenvolvimento é Perl Moderno, elegante e bonito.

Voltando ao nosso problema, as duas novas opções que utilizei ali foram a -n e -l, que estão documentadas no perldoc perlrun, mas vamos tentar explicar aqui:

  • -n: Faz com que o interpretador adicione um “while(<>){ }” envolta do seu código, assim o seu código será responsável por fazer um tratamento de cada linha lida da entrada.
  • -l: Quando utilizado com o -n (ou -p) faz com que o conteúdo lido automaticamente pelo “while(<>){ }” já seja “chompeado” e ao utilizar o print o separador de linha é incluído de volta automaticamente (mas essa segunda parte não faz diferença para nós, pois estamos utilizando o say no lugar do print).

Dessa forma, eu posso retirar o while e o chomp do meu código e economizar alguns caracteres. Mas em compensação preciso adicionar o bloco END, que será executado logo antes do programa terminar (não sei se era a melhor maneira de fazer isso, mas foi a que eu encontrei e aprendi a usar). A documentação do END está no perldoc perlmod.

Ou seja, tirando manipulações da linguagem, o programa faz *exatamente* a mesma coisa que o anterior, apenas tem alguns caracteres a menos.

E gostaria de comentar aqui que nas CNTP eu nunca utilizaria -n, -l e END. Mas ao desafiar a linguagem que uso – e utilizando apenas a documentação dela, o perldoc, e nada mais – fui capaz de aprender 3 coisas novas.

Uns 10 minutos depois, me veio outra solução na cabeça (dessa vez com 29 caracteres):

perl -E "chomp(@n=<>);say for(sort @n)" -- < nomes.txt

Voltei a utilizar só o -E para a execução de código e deixei o -l e -n de lado. Neste exemplo a parte da impressão, continua igual mas o que mudou – novamente – foi a leitura do input:


chomp(@n=<>);
say for(sort @n)

Tem 2 coisas muito legais nesse exemplo e as duas remetem para uma das coisas mais poderosas do Perl: contexto. E foi por causa do contexto que consegui reduzir e simplificar o código:

  • Ao fazermos “@n=<>” o Perl entende que estamos em contexto de array e não vai ler apenas uma linha, mas sim todas as linhas do input de uma vez. Ok, isso não é recomendável se o seu texto for extremamente gigante pois vai tudo para a memória, mas nesse caso era perfeitamente aceitável. (E nesse caso eu não iria utilizar um método de sort utilizando arquivos em disco para ordenar os nomes dos professores…)
  • Ao fazermos um chomp(@n), o chomp irá perceber que está recebendo uma lista como parâmetro e irá retirar o line breaker de todas as linhas de uma só vez (e no caso retorna o número de line breakers removidos, mas isso não interessa para a gente).

Contexto, todos adoram.

Estamos quase no final ;)

Meia hora depois disso, percebi que eu não precisava desse bendito chomp. Eu só estava utilizando-o, pois estava usando o say ao invés do print depois!

Então, aqui mais uma tentativa e dessa vez ficou com 17 caracteres:

perl -E "print for(sort<>)" -- < nomes.txt

Sério, acho que aqui não tem mistério algum (ainda mais depois dos exemplos iniciais):


print for(sort<>)

Novamente utilizamos contexto, pois o sort recebe um array e quando utilizamos o operador de leitura – aka diamante “<>” – o Perl entende que deve ler tudo da entrada e passar ao sort, que por sua vez vai ordenar e passar ao for e que por sua vez vai passar uma entrada de cada vez para o print. Sério, não é tão complicado e acho que assim terminamos, certo?

Não!

E esse é o ponto desse post gigante, Do What I Mean!

Acho que se desde o início eu tivesse deixado de lado um pouco meu pensamento de programador e simplesmente pedido ao Perl para fazer o que eu queria, teria chegado de primeira na solução final com 12 caracteres:

perl -E "print sort<>" -- < nomes.txt

Aqui está só o código:

print sort<>

Leia isso como:

Ei Perl, por favor, imprima depois de ordenar tudo isso que você vai ler.

Você pode pensar que isso é mágica… mas não, é Perl! Menor, mais elegante e tão funcional quanto!

Seu sentimento sobre Perl agora.

E espero que vocês tenham gostado… e da próxima vez, perguntem ao Perl se ele pode resolver a sua tarefa de forma bem simples! :D

PS: E no final das contas, o meu amigo do Python fez uma versão em Python utilizando apenas uma linha e só uns 50 caracteres:

sorted([x for x in open('nomes.txt').readlines()])

PPS: E se alguem quiser saber como coloquei esse syntax highlighting aqui no WordPress, só olhar aqui.

Anúncios
  1. Pericolo
    março 6, 2011 às 11:21 am

    Cada dia que passa me surpreendo mais com Perl. Realmente é um alicate Suíço e muito mais :D
    De fato tem muita gente que pensa que esse é o Perl usado em produção, e logo fica com preconceito já que “ninguém deveria programar assim. Isso é altamente impossível de manter.”
    Sortudos são os que se permitem a viver novas experiências :D
    Que bom que eu participei do curso de Perl =P

  2. março 6, 2011 às 12:30 pm

    Aquele list comprehension no código Python é desnecessário. Poderia ser só:

    sorted(open(‘nomes.txt’).readlines())

    Outra coisa… Isso pode ser feito em shell unix também, né? :P

    sort < nomes.txt

    Para completar, quem vai querer postar uma versão disso para a máquina 2002? :P

    • brunobuss
      março 6, 2011 às 12:40 pm

      Boa Denilson! :)

      Sobre o shell… vale afinal não tem regras! :P
      (Mas é mais roubado que todos xD)

      Estou esperando uma versão da maq2002 e em brainfuck :D

  3. garu
    março 7, 2011 às 6:49 pm

    12 caracteres? Isso tudo?!

    perl -lE ‘say sort’ — < nomes.txt

    10 :)

  4. garu
    março 7, 2011 às 6:52 pm

    arg, porcaria de wordpress q não deixa eu usar tags… Nova tentativa:

    perl -lE ‘say sort <>’ -- < nomes.txt

    (O wordpress realmente deveria aprender mais sobre DWIM >:)

    • brunobuss
      março 7, 2011 às 7:46 pm

      Menos 2 chars, hooray!

  5. março 8, 2011 às 1:06 am

    o que eu comentei pro buss em um e-mail foi o fato curioso de a diminuição dos caracteres não ter implicado em diminuir a legibilidade do código. Na verdade, ocorreu o contrário. A primeira versão do buss é MUITO mais ilegível que a última.

    Geralmente, quanto menos código, mais ilegível o programa fica, e por isso achei esse exercício em particular muito curioso.

  1. No trackbacks yet.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: