Meu Quadro de Medalhas: Aprendendo Ruby

O Soro escreveu há um tempo atras sobre como conseguir motivação pra aprender algo novo, como uma linguagem de programação. Desde a primeira vez em que ouvi falar de Ruby, la em 2001, eu estou tentando achar algum jeito de aprender a linguagem, mas a procrastinação sempre fala mais alto. Acho que o único jeito que encontrei pra aprender uma nova linguagem é ter um projeto, alguma coisa prática de verdade, nem que seja algo meio sem sentido mesmo, só pra ter um objetivo concreto e ir aprendendo com as necessidades que vão surgindo. Como não tava tão inspirado pra criar um novo job4dev (que foi o “projetinho” do miguel), e estamos em tempos de Olimpíadas, eu resolvi aprender um pouco de Ruby escrevendo um programa pra gerar meus próprios quadros de medalhas.

Eu sempre tive uma certa ressalva com esses quadros de medalhas olímpicos. Em geral eles colocam medalhas de ouro acima das de prata, e essas acima das de bronze. Isso faz com que um país que tenha uma mísera medalha de ouro fique na frente de um outro que tenha 10 de prata, o que eu acho meio que injusto. É claro que agora com os EUA perdendo da China nos ouros, a TV americana tá colocando o ranking baseado no total de medalhas. Mas daí você tá dizendo que uma medalha de prata vale o mesmo que uma de ouro, o que não é a verdade. Uma solução seria colocar pesos pra cada tipo de medalha.

Outra coisa meio injusta é que na China tem muito mais gente que na Austrália, então um ranking baseado em população também seria interessante. E mais ainda, um atleta como o Michal Phelps consegue 8 medalhas numa olimpíada, mas um time completo de futebol consegue no máximo 1. Porque não multiplicar o valor da medalha pelo numero de atletas na equipe (titular, pelo menos)?

Então a idéia inicial que eu tive foi a seguinte. Estamos em plena Web2.0, então eu devo conseguir alguns serviços que provenham os dados necessários e eu faco uma espécie de mash-up, calculando meus rankings. Eu precisaria de 2 serviços, um com os dados sobre as medalhas e esportes, e outro com dados sobre as populações dos países.

Infelizmente logo na minha primeira tarefa eu já encontrei problemas. Informação é dinheiro, e foi impossível encontrar algum site na Web que disponibilizasse as informações sobre as medalhas. Existem vários sites mostrando tabelas ou disponibilizando RSS com noticias, mas uma API pras medalhas não existe. Acabei descobrindo que existe uma tal de World News Press Association que tem o monopólio das feeds sobre os eventos olímpicos e eu teria que pagar pra conseguir acesso (como o google fez).

A minha solução foi criar um programinha em Ruby que fizesse parser das paginas do UOL. Primeiro eu baixo a pagina do quadro geral de medalhas, daí eu recursivamente baixo as paginas de cada pais (que tem no combo box). Não é exatamente uma solução elegante, mas graças as boas tags no fonte da UOL e ao HPricot (uma biblioteca pra fazer parse de HTML) não foi tao difícil agregar os dados e gerar um CSV. Porem, surgiram dois problemas. Primeiro que eu tive que abandonar minha idéia de multiplicar as medalhas pelo numero de atletas no esporte, pois essa informação seria muito chata de conseguir. Depois que eu tive que criar uma tabela traduzindo os nomes dos países de português pra inglês, já que eu queria apresentar as tabelas em inglês, e os dados de população também estariam em inglês.

O segundo passo seria conseguir informações sobre população. Eu achei um tal de Population Reference Bureau com as informações necessárias, só que de novo esbarrei em problemas. Primeiro que eles não tem um RSS ou um web service, então eu tive que fazer copy e paste da tabela. E segundo que algumas informações relativas aos países são diferentes da pagina da UOL, ou tem que ser agregadas (por exemplo, tive que adicionar a população de varias colonias francesas na da França). Pra alguns países estranhos (como “Ilhas Virgens Americanas”), que não tinham informação no Bureau, eu tive que pegar no wikipedia mesmo.

Finalmente, com todos os dados em arquivos, eu pude criar um outro programinha em Ruby pra analisar os dados e gerar meus rankings. Eh claro que como isso é just for fun, a interface deixa muito a desejar. Mas pra quem tiver interesse, alguns rankings que eu gerei estão nesse diretório. Eu atualizo os dados usando o parser uma vez por dia. Os cabeçalhos dos rankings descrevem o que foi usado pra gerar os”values” pras medalhas (counters) e pro valor total (aggregators) a partir do numero de medalhas, e por quais campos ordenar os países.

Bom, lições aprendidas:

  • realmente o melhor jeito pra aprender uma linguagem de programação é usando. Eu não posso dizer que agora sou um programador Ruby, mas aprendi um monte de detalhes e foi bem melhor do que qualquer tutorial.
  • apesar de existir muita informação pela web, o mais chato (e difícil) é conseguir agregar a informação de varias fontes que em geral são “um pouquinho diferentes”. Por exemplo, não teria como automatizar o matching dos nomes dos países em diferentes línguas, nem como programar a intuição de “somar as populações das colonias”, a não ser que você tenha muito mais meta-informação, ou recorra a heurísticas.
  • a China nem é tao boa assim se a gente considerar o tamanho da população ;-)
  • o Brasil continua ruim, em qualquer ranking mais ou menos razoável que se faça.

O proximo passo seria aprender Ruby on Rails e fazer uma interface em que os usuarios pudessem escolher como fazer os rankings. Mas como eu teria que instalar outro servidor web e um banco de dados, e eu to meio sem tempo, vai ter que ficar pra depois… e se interessar a alguem eu mando os dados.

Links do dia: Computação em foto

  • The history of computer data storage, in pictures
    Pequeno resumo em imagens da evolução dos mecanismos de armazenamento de dados em computadores.

  • Programming Languages and their Celebrity Equivalents

    Comparação divertida entre linguagens de programação e pessoas famosas. Minha comparação favorita foi a do PHP.

  • If a programming language was a boat…

    A idéia é a mesmao do link anterior, mas desta vez a comparação é com barcos. É fácil notar que os autores de cada um dos posts tem visões muito diferentes sobre o mundo da programação. Aquele que compara linguagens com pessoas famosas parece gostar muito de Java, da MS e não ser fã de linguagens dinâmicas e script. Já a comparação com barcos claramente puxa a sardinha pro lado de Ruby.

Frase do Dia

“There are only two kinds of languages: the ones people complain about and the ones nobody uses.”

-Bjarne Stroustrup(Criador do C++)

Bricando com bits: Ponto flutuante

Nos últimos tempos tenho trabalhado na integração de dois sistemas, um em C (legado) e um e Java. A integração consiste em em ler mensagens enviadas pelo programa em C através de um socket e gerar objetos e ações correspondentes do lado Java. Nada de muito excepcional, é um cenário bastante comum. O divertido da coisa é que eu estou tendo que descer bem baixo na manipulação dos bits, e estou tendo que relembrar vários conceitos que aprendemos algum dia na faculdade e que consideramos como sendo inúteis pro resto da nossa vida, já que boa parte é resolvida por alguma API.

Ontem acabei de me deparando com um probleminha interessante, e que me fez relembrar o conceito de representação de números em ponto flutuante. Vou tentar explicar aqui a parte teórica da coisa, e no final apresento o problema e a solução. Se você não gosta de teoria, pode pular pro último parágrafo.

Muito bem. Suponha que você tenha o seguinte código em Java:

int i = 15;
float f = (float)i;
System.out.println(i+”, “+f);

O resultado da impressão será 15, 15.0.

Até aí nenhum novidade. O casting faz com que o número passe a ser tratado como ponto flutuante, mas mantém o valor inteiro. OK. Alguém aí já parou pra olhar como estes números são representados binariamente, por baixo do pano?


15 inteiro = 00000000 00000000 00000000 00001111
15 float = 01000001 01110000 00000000 00000000

Para alguns talvez isto não seja novidade, e para outros uma descoberta. E para alguns como eu era algo que eu já imaginava, mas nunca olhei a fundo.

No caso do 15 inteiro, nenhuma novidade: 15 = 2^3 + 2^2 + 2^1 + 2^0 = 8 + 4 + 2 + 1 = 15.

No caso do 15 float, é necessário entendermos o padrão que rege a representação dos números de ponto flutuante: IEEE 754. Resumindo: segundo o padrão, um número de ponto flutuante pode ser representado com precisão simples (32 bits, float em Java) ou precisão dupla (64 bits, double em Java). A IEEE define apenas a precisão de 32 bits como obrigatória, as outras sendo opcionais.

No caso da representação simples, o layout dos bits é o seguinte: o primeiro bit representa o sinal (0 positivo, 1 negativo), os 8 bits subsequentes representam o expoente e o restante (23 bits), a parte fracionária (chamada de mantissa). O desenho abaixo, obtido da wikipedia, ilustra este layout:

O expoente pode mapear valores entre -127 a 127 (no caso de um expoente de 8 bits). Para evitar o uso de bit de sinal, que pode dificultar certas operações, foi definido que o valor armazenado seria e + 127, onde e é o real valor a ser considerado.

A mantissa armazena um valor fracionario, e pode ser traduzido como 1.<padão de bits da mantissa>. Se a nossa mantissa for 1000000 00000000 00000000, é equivalente a 1.1000000 00000000 00000000. Aqui vale ressaltar mais um ponto: estamos considerando números binários, portanto os valores após às virgulas podem ser considerados como potências negativas de 2 (assim como números fracionários na base 10). No caso do exemplo, 1.1 é equivalente à 1 + 2^(-1) = 1 + 0.5 = 1.5.

Tendo estes elementos em mão é fácil calcular o resultado final: f = (sinal) * 2^(e -127) * 1.<mantissa>.

No caso do número 15, vamos ver como fica a coisa:

  • sinal = 0 = +1
  • expoente = 10000010 = 130. Portanto e = 130 - 127 = 3.
  • mantissa = 1110000 00000000 00000000, portanto 1.111 = 1 + 2^(-1) + 2^(-2) + 2^(-3)  = 15/8
  • Resultado = +1 * 2^3 * 15/8 = 8 * 15/8 = 15.

Muito bem. Mas a pergunta: porquê eu estou me preocupando com isso???

Simples: eu tenho que ler um fluxo de bytes, seguindo um descritor de mensagem, e reconstituir certos tipos de dados (ints, booleanos, campos de bits, etc…). Como eu tenho que ler e extrair alguns campos de bits perdidos no meio de bytes, resolvi implementar uma estratégia de baixo nível, lendo byte a byte e concatenando quando necessário em tipos de dados maiores. Além disso, estou usando como repositório temporário destes bits uma variável do tipo long, que possui 64bits, o que é mais do que necessário para armazenar qualquer dado que eu receba. A operação é simples: recebo um byte, e “colo” ele no long usando operações SHIFT e OR binários.

Agora suponha que o programa em C me mande o número 15 em ponto flutuante de 32 bits, como definido acima, e que eu armazene os bits recebidos em um long. Se ao final da leitura, eu pegar a variável temporária  e fizer um casting para float direto, a JVM vai pegar o valor do long (no caso 1097859072) e simplesmente converter para o padrão IEEE 754 correspondente, o que me resultaria num float com valor 1097859072.0, o que definitivamente não me interessa. O que eu preciso é de uma função que receba um long ou um int, seja capaz de olhar para o dado apenas como um repositório de bits e gere o float o double correspondente. No caso, os métodos Float.intBitsToFloat(int) e Double.longBitsToDouble(lont).

PS: Quem estiver interessado em ter uma leitura mais teórica e aprofundada sobre o tema pode ler o artigo What Every Computer Scientist Should Know About Floating-Point Arithmetic.

Beleza booleana

Até há muito pouco tempo atrás, expressões booleanas para mim eram apenas uma questão de verdadeiro ou falso, ifs, whiles e do..whiles. Nesta época, eu ria quando encontrava trechos de códigosdo estilo

boolean b = true; if (b == true){...}.

A vida era simples, linda e limitada.

Daí eu comecei a brincar seriamente com javascript e python, e descobri que existe muito mais coisas entre o céu e as funções booleanas do que poderia sonhar minha vã filosofia. Neste mundo novo, expressões booleanas podem ser muito mais do que apenas true ou false: elas podem ser formas extremamente elegantes e compactas para atribuição de valores.

Vamos por partes. Em Java, apenas variáveis do tipo booleanas ou expressões que retornem variáveis do tipo booleanas (maior que, menor que, igual a, contém, …) podem ser utilizadas em expressões booleanas. Uma construção do tipo if(1) não funciona.

Em linguagens de script, qualquer objeto pode ser avaliado como uma expressão booleana. Em Python por exemplo, a seguinte regra é aplicada: valores False (booleano), 0, “”, e None (valor nulo) são avaliados como falso; qualquer outro valor é avaliado como verdadeiro. A expressão (1 and 2) seria avaliada como true por exemplo.

Um outro ponto interessante em Python e Javascript é que expressões booleanas não retornam simplesmente True ou False: elas retornam o valor de um dos objetos da expressão. Uma expressão com AND retorna o último valor da expressão caso todos elementos sejam avaliados como verdadeiros, ou retorna o primeiro valor que fez com que toda a expressão fosse avaliada como falsa. Por exemplo: 1 and "miguel" and 2 retorna 2, porque todos os elementos da expressão são verdadeiros, e portanto a expressão toda é verdadeira. Já 1 and 0 and 2 retorna 0, uma vez que este é o primeiro elemento avaliado como false. E como todos sabemos, se um elemento é falso, a expressão toda é falsa.

No caso de uma expressão OR, ela retorna o primeiro elemento que for avaliado como verdadeiro, ou o último elemento da expressão caso todos os anteriores sejam avaliados como falsos. Por exemplo (1 or 2) retorna 1, e (0 or "") retorna “”.

Ótimo. Então como podemos usar isso para escrever expressões compactas, úteis e elegantes? Eis algumas sugestões:

  • Valor default para uma variável: OR pode ser usado para garantir que caso um parâmetro seja passado com valor nulo para uma função por exemplo, ele receba um valor default:
    var v = param || “default”
  • Expressões ternárias: o uso de OR e AND pode criar uma função ternária em linguagens que não possuem esta construção (Python por exempl0):
    var v = (condicao AND valor1) OR valor 2

Aproveito pra levantar um ponto: estilo compacto é algo bom ou ruim? Um argumento muito utilizado contra expressões compactas como as descritas acima é que prejudica a legibilidade. De fato, pode ser mais difícil de ler à primeira vista. Mas mesmo construções mais verborrágicas podem ser dificeis de serem lidas: tudo depende de quão treinado é o olho e o cérebro do leitor. Portanto, eu acho que é apenas uma questão de estar acostumado a ler código de qualidade.

Python

Python

De XKCD

Turing completeness (Pra que linguagens de programação?)

Pessoal, primeiro queria me desculpar por minha ausência neste blog nos últimos tempos… Infelizmente eu acho que eu sou o colaborador mais esparso por aqui e não tenho grandes perspectivas de melhorar minha taxa :-/ De qualquer forma, como o Miguel ainda não me expulsou, continuo na ativa para a felicidade (ou não) dos dois leitores deste blog (não fossem os comentários de outras pessoas e as estatísticas do blog eu chegaria a acreditar piamente que só o Raphael e o Bruno lêem este blog nos últimos tempos… obrigado pela participação de todos!). :)
Como todos devem saber, o Raphael e o Bruno vêm há algum tempo em uma discussão (às vezes) quase religiosa com dezenas de comentários sobre o artigo “Viva a diversidade!” do Miguel. Muita gente deve estar cheia de ler os comentários deles! Eu, inclusive, já passei por uma fase assim, mas acabei lendo todos os comentários em batch depois. Acho que é isto que levou o Miguel a “censurar” (será?) os dois. :) De qualquer forma, acho que este blog deve ser um meio livre de expressão. Se eles querem se matar discutindo um assunto que, a meu ver, não tem muito sentido, deixa eles lá.

De certa forma, discussões grandes como esta acabam gerando ligações com assunto muitas vezes nada a ver com a discussão principal. E foi exatamente isso que me chamou a atenção neste comentário do Raphael. Mais especificamente, a seguinte parte me chamou a atenção: “Em última análise, TODAS as linguagens são possíveis de se fazer QUALQUER tipo de construção lógica. Se não fosse assim, elas não seriam Turing-Complete, tampouco mereceriam o nome “Linguagem de Programação”.”

Isso me lembrou de um fato que eu aprendi há muito tempo e que é uma daquelas coisas que não mudam sua vida porque provavelmente você nunca mais vai ter que lembrar dele no seu dia-a-dia mas fazem com que tudo faça sentido (se um dia você estiver buscando a essência das coisas, como eu acho que o Raphael estava quando ele escreveu esta frase).

Turing completeness é um conceito que, na verdade, estrapola linguagens de programação. Qualquer máquina abstrata ou linguagem de programação que seja capaz de executar qualquer tarefa computacional é classificada como turing complete.

Quando você desce o nível, das linguagens de programação de alto nível para as coisas mais essenciais, como assembly e linguagem de máquina, você vai descobrir que ser turing complete nada mais é do que ser capaz de executar pouco mais de meia dúzia de instruções. Descendo mais o nível, veríamos que para projetar um circuito lógico capaz de executar todo este conjunto de instruções você precisaria de apenas um tipo de portas lógicas: ou só NANDs ou só NORs. Você não vai precisar de mais que dois transistores para fazer cada uma destas portas lógicas. Duvida? Aqui e aqui.

Desta forma, eu acho que toda esta discussão sobre qual linguagem de programação é melhor realmente é inócua. Parodiando o célebre post de Linux Torvalds, homens de verdade precisam apenas de máquinas turing complete. Nada mais!

E é por isso que eu conclamo todos vocês a largarem toda esta porcariada de frameworks, modelos de programação, orientação à objeto, programação funcional, lambdas e o escambal e passemos todos a projetar tudo que precisamos fazer em termos de NANDs (por um gosto pessoal eu prefiro o NAND ao NOR, mas, para não parecer inflexível, acho que podemos liberar o NOR também). Caso alguém realmente precise de algo de mais alto nível, são permitidos os instructions set mínimos ou, já que eu sou uma pessoa benevolente, o uso da linguagem Whitespace, que, segundo o artigo sobre instruction set mínimo, chega perto de algo minimalista (mas não deixa de ser uma linguagem de alto nível, na minha opinião, então use com parcimônia). Coisas como Path language nem pensar. Elas não são suficientemente minimalistas, por mais que a home page dela te faça pensar o contrário.

Com estas tecnologias (e apenas com elas!) acho que daqui há uns 200 anos conseguiremos chegar num nível tecnológico suficiente para termos blogs funcionando novamente. Quem sabe assim eu não ganho tempo suficiente para pensar no meu próximo artigo… :)
Nota: Antes que alguém me acuse de reacionário: esta é uma obra de ficção. Qualquer relação dela com seu autor ou com qualquer outra pessoa é azar deles.

O legal de um blog como o log4dev…

É que é um lugar com gente inteligente, bem educada. Daqueles onde só pode falar quem sabe sentar-se à mesa e usar os talheres. Não tem muito lugar para criança.

Então, lendo o último post do Miguel, eu fiquei com vontade de falar “Java is TeH SuXXXXXoR”. Mas como precisamos fazer as coisas aqui com estilo, acabei escrevendo isso aqui, ó.

Viva a diversidade!

Emacs vs Vi, Mac vs Windows vs Linux, Intel vs AMD, OO vs Funcional. Vários temas na Computação são capazes de gerar discussões intermináveis, exaltadas, quase religiosas. Mas atualmente, a que mais me irrita é a interminável discussão sobre qual a melhor linguagem de programação.

Defensores de Java dizem que linguagens de script não aguentam sistemas grandes, estruturados. Defensores de Ruby ou Python dizem que Java é um lixo feito para quem não sabe programar. Amantes de Ocaml, Haskell e Erlang acham o resto do mundo é inferior. Quem não programa em Ocaml, Haskell e Erlang acha que são linguagens exóticas e acadêmicas. Todos acham que .NET é um lixo. Cada um quer mostrar quão melhor é a sua linguagem, como pode fazer tudo o que as outras fazem e muito mais.

É inevitável que uma pessoa acabe ficando mais a vontade com uma ou duas linguagens, aquelas que ela usa no dia a dia. Eu por exemplo considero que minha primeira linguagem hoje em dia é Java. Foi a primeira linguagem séria que eu aprendi, e venho trabalhando com ela comercialmente há vários anos. Mas sinceramente não gosto de ser marcado como Programador Java. Aliás, não gostaria de ser marcado como Programador X, onde X é uma linguagem qualquer. Acho que isso reduz. Me considero um desenvolvedor de tecnologia, de produtos, e assim sendo gosto de sempre procurar uma linguagem que se adeque melhor ao tipo de programa que tenho que escrever.

Tirando Pascal, que aprendi na universidade por motivos didáticos (curso básico de programação), todas as linguagens que eu programei minimamente foram motivadas por motivos práticos. Aprendi Perl quando eu quis escrever um etiquetador de palavras em português: dada uma lista de palavras, ele determinava se era oxítonas, paroxítonas ou proparoxítonas, e se eram mono, bi, tri ou polisilábicas (comecei escrevendo em C, mas rapidamente percebi que seria muito mais rápido e eficiente aprender Perl do que ficar fazendo regexp na unha). Aprendi Python quando precisava de uma linguagem de script clean e bem estruturada para processar arquivos com dados de biologia molecular. Aprendi de fato C quando precisei escrever um captador e analisador de imagens via firewire em tempo real para um sistema embarcado. Descobri o poder de Javascript quando comecei a querer implementar aplicações Web com interfaces ricas, AJAX. Aprendi Java quando quis começar a desenvolver sistemas OO multiplataformas e mais tarde, sistemas web.

Não existem nenhuma tecnologia perfeita. Um bom desenvolvedor tem que saber definir quais características são importantes pro projeto dele, e determinar qual tecnologia é a mais adequada. Temos a tendência a sempre querer usar aquelas que conhecemos melhor. É inevitável. Mas as vezes é saudável analisar outras opções e ver se existem coisas melhores. Sim, coisas melhores podem existir! É importante portanto que sejamos capazes de reconhecermos as fraquezas de cada linguagem, e eventualmente abrirmos mão daquela que sempre usamos.

Dois pontos tem que ficar bem claros para pessoas ligadas ao mundo do desenvolvimento.

O primeiro ponto é entender que o que diferencia bons desenvolvedores de péssimos desenvolvedores é a capacidade de entender e manipular as tecnologias existentes, ter uma visão crítica do que existe, saber se adaptar. O mundo da computação evolui de forma muito rápida. As necessidades mudam constantemente. Ficar obcecado por uma linguagem e tentar provar a todos que é capaz de fazer tudo com ela não leva a lugar nenhum. Bons desenvolvedores são pessoas curiosas, que gostam e são capazes de aprender coisas novas, querendo sempre aprimorar seus conhecimentos. Bons desenvolvedores são aqueles que entendem (ou procuram entender) os conceitos por trás das ferramentas que utilizamos.

O segundo ponto é que em última instância, o que paga nossos salários e o que nos torna úteis para a sociedade não é o fato de saber escrever if-then-else em uma determinada linguagem. O que nos torna úteis é a nossa capacidade de desenvolver produtos que facilitem de alguma forma o dia a dia das pessoas. Esse é o grande poder da tecnologia. Assim sendo, o nosso objetivo é criar estes produtos da melhor forma possível, utilizando os melhores recursos disponíveis. Usuário não vê o que está por baixo dos panos, e tampouco está interessado nisso. Usuário vê apenas se aquilo que desenvolvemos ajuda ou não. Nesse ponto, o que interessa portanto não é a tecnologia, mas sim o design da solução (aconselho a leitura do texto do Raphael sobre este tema).

Link do Dia: Rosetta Code

Para quem gosta de ler código alheio, ver diferentes formas de abordar um problema e de comparação entre linguagens, sugiro dar uma lida no wiki Rosetta Code (http://www.rosettacode.org/wiki/Main_Page).

Descrição do  wiki: “Rosetta Code is a repository for code examples that go beyond the traditional “Hello World!” example. A series of tasks are listed, and visitors to this site are asked to complete the tasks in the language of their choice. The tasks are intended to cover a variety of needs commonly found in software.”

Kata para programadores

Kata - Um conjunto estilizado de movimentos que simulam uma variedade de defesas e ataques contra um inimigo invisível. Um kata é utilizado para aperfeiçoar o estilo, aprender concentração, assim como demonstrar ataques, defesa e contra-ataques. ( Extraído de http://la.essortment.com/karateterminolo_raan.htm - tradução livre )

Dojo - literalmente “lugar do Caminho”. Também “lugar da iluminação”. ( Extraído de http://www.aikido.itu.edu.tr/dictionary.htm - tradução livre )

Com certeza, todos o leitores com mais de 20 anos lembram-se do Daniel Larusso (vulgo Daniel Sam), aquele rapaz com cara de bebê chorão e chassi de pernilongo, destruindo seus adversários no “All Valley Karate Tournament”. E tudo isso graças aos gloriosos ensinamentos do Sr Miyagi, que iam de pintura de paredes e enceramento de carros, a dancinhas esquisitas à beira da praia, vulgo Kata. Provavelmente, baseado nesse incrível exemplo, algumas pessoas como Dave Thomas resolveram aplicar esta prática ao mundo dos programadores. De acordo com Dave, assim como em outras áreas, para desenvolvermos nossas habilidades precisamos de muito treino. Baseado nisto, ele desenvolveu uma série de katas para programadores - pequenos exercícios para treinar nossas habilidades de programação, lógica, estrutura de dados, etc.

Baseado neste mesmo conceito, um grupo de pessoas criou um Dojo virtual, para que as pessoas se reunam para a prática de katas. Este Dojo ainda está engatinhando, e atualmente contém somente cinco (em 04/09/2007) katas cadastrados. Mas certamente irá crescer.

Pessoalmente, gostei muito desta idéia. Muitos programadores têm o interesse e a vontade de aperfeiçoar suas habilidades, mas não sabem como fazê-lo. Desenvolver pequenos projetos sozinho, ou em grupos (comunidade open-source) é uma boa alternativa, mas nem todos têm tempo para se comprometer suficientemente para tais atividades. Além disto, nem sempre se consegue exercitar as diferentes habilidades úteis a um programador faixa-preta quinto dan.

Como gostei muito desta idéia, tentarei à medida do possível colocar uns katas aqui. E aqueles que quiserem, podem entrar em contato pelo alexandre(at)log4dev.com, que teremos prazer em divulgar suas sugestões, e dar-lhes sempre todo o crédito.

Nota: falando em dar o devido crédito, a idéia deste post surgiu a partir de um artigo de Amr Elssamadisy do InfoQ

Powered by ScribeFire.

AJAX em 20 minutos

Antes de mais nada, vamos ajustar certos conceitos.

AJAX = Asynchronous Javascript XML. Portanto, AJAX é basicamente uma técnica que consiste em fazer requisições HTTP de forma assíncrona para o servidor (Nota: AJAX não é uma tecnologia. É uma técnica. Quem disser que é uma tecnologia merece arder no fogo do inferno pela eternidade). Se você não sabe o que é uma requisição HTTP, sugiro dar uma olhada aqui. Não tem como escapar, é a base de tudo.

Fazer uma requisição assíncrona significa que o processo irá rodar por trás das cortinas, não interferindo no funcionamento da tela no navegador, ao contrário do que acontece em uma requisição normal. Quando executamos uma requisição síncrona no navegador (através de um link ou submetendo os dados de um formulário), toda a interface para de responder a eventos. Isto fica particularmente claro (e chato) quando a página é pesada e/ou a rede esta lenta.

As vantagens da técnica são óbvias: interfaces mais leves, suaves, muito próximas daquelas de aplicativos desktop.

Pré requisitos para conseguir mexer com AJAX? conhecer um pouco de Javascript, DOM HTML e conhecer pelo menos uma linguagem de programação server side.

Passo 1: Preparando o terreno

Para começar, uma má notícia: muito do trabalho normalmente feito pelo navegador quando submetemos um formulário com dados tem que ser feito pelo script javascript. Ou seja, tem que ser feito pelo desenvolvedor. Um deles é construir a string de requição. O formato da string é algo do tipo

chave1=valor1&chave2=valor2&…

Não existe muito segredo. O único cuidado a ser tomado é em relação aos caracteres especiais. Espaços, símbolos como &, =, caracteres acentuados e caracteres não ASCII devem ser convertidos para o formato Unicode. Em Javascript existem várias funções que fazem este tipo de conversão, mas o mais adequado é o par encodeURIComponent/decodeURIComponent.

O seu uso é muito simples: var string_codificada = encodeURIComponent(string_original).

Em caso de requisições muito longas, aconselho o uso de um StringBuffer, que torna o processo de contatenação de strings muito mais eficiente. Veja aqui como fazer isso.

Passo 2: enviando os dados para o servidor

Muito bem. Já temos uma string de requisição, agora precisamos enviar isto para o servidor. Para isso, existem 2 métodos: GET e POST .

  • O método GET envia os parâmetros na requisição (www.meusite.com?chave1=valor1&chave2&valor2…) e tem uma limitação de tamanho de 1024 bytes. É mais indicado para enviar parâmetros relacionados á busca de informações no servidor.
  • O método POST envia os parâmetros de forma separada, e não tem limitação de tamanho. É mais indicado para envio de dados de formulários, textos longos e dados que serão armazenados no servidor. Se os dados forem maiores que 4KB, alguns cuidados especiais devem ser tomados, como mostrado aqui.

Para executar a requisição, o primeiro passo é criar o objeto HTTPRequest:

var req;

if (window.XMLHttpRequest)

    req =  new XMLHttpRequest();

else if (window.ActiveXObject)

    req = new ActiveXObject('Microsoft.XMLHTTP');

else

    req =  new ActiveXObject('Microsoft.XMLHTTP2');

O código acima cria um objeto de requisição para Windows ou Firefox (eles são ligeiramente diferentes, apesar de terem interfaces de uso iguais). Com o objeto de requisição na mão, o próximo passo é definir a função que será chamada quando o resultado da requisição enviado pelo servidor estiver disponível:

req.onreadystatechange = minhafunc;

O uso desta função será explicado no próximo passo. Finalmente, o envio dos dados. Se o método usado for GET, a receita de bolo é

requestObject.open('GET', url, true);
requestObject.send(null);

Onde url = endereçodapagina?stringderequisicao. Se o método for POST, a receita de bolo é

requestObject.open('POST', url, true);

requestObject.setRequestHeader('Content-Type','application/x-www-form-urlencoded');

requestObject.setRequestHeader("Connection", "close");

if (parameters != null)

    requestObject.setRequestHeader("Content-length", parameters.length);

requestObject.send(parameters);

onde URL é apenas o endereço do servidor, e parameters contém a string de requisição. Muito bem, os dados foram enviados. Agora é esperar os dados voltarem, e irmos para o próximo passo.

Passo 3: obtendo a resposta

No passo 2, definimos o valor do atributo req.onreadystatechange. Esse atributo é uma função que será chamada pelo navegador para tratar a resposta. Uma forma de definir a função é

req.onreadystatechange= function(){

    //corpo da função vai aqui

}

Um ponto a ser ressaltado é que esta função pode ser chamada várias vezes, sem a resposta estar disponível ainda: a o processo passa por vários estados antes de estar pronto para processamento. Portanto é necessário testar o status do processamento. Isto pode ser feito com o seguinte código

req.onreadystatechange= function(){
if (req.readyState == 4){
if (req.status == 200){
//Resposta disponivel
}
}
}

O atributo readyState pode assumir 4 valores:

  • 0: requisição não inicializada
  • 1: conexão estabelecida
  • 2: requisição recebida
  • 3: processando
  • 4: requisição terminad, resposta pronta.

Já o atributo status fornece o status do protocolo HTTP. 200 significa OK.

Muito bem, temos a resposta. O último passo é processar o resultado. Para obter o DOM XML, basta executar req.responseXML. Para trabalhar com o conteúdo como se fosse uma sequência de texto, basta executar req.responseText. E para processar o resultado como JSON, basta executar eval(”(”+req+”)”).

Segue um exemplo completo de código que recebe um snippet HTML (um trecho de código em HTML) e atualiza o conteúdo de uma DIV com este código:

function atualizaDiv(){

    var req;

    if (window.XMLHttpRequest){

        req = new XMLHttpRequest();

    } else if (window.ActiveXObject){

       req =  new ActiveXObject('Microsoft.XMLHTTP');

    }else{

       req =  new ActiveXObject('Microsoft.XMLHTTP2');

    }    req.open('POST', url, true);

    req.setRequestHeader('Content-Type','application/x-www-form-urlencoded');    req.onreadystatechange = function(){

        if (req.readyState == 4){

           if (req.status == 200){

               document.getElementById("minhaDiv").innerHTML=req.responseText;

               document.body.style.cursor = "default";

           }

        }

    }

req.send(params);

}

Em breve escreverei um post comentando sobre vantagens e desvantagens de se usar texto, XML ou JSON como resposta de uma requisição.

Caso esteja interessado em usar uma biblioteca simples para desenvolver sites com Ajax, ou queria ver exemplos de código escrito em Javascript, dê uma olhada na Juice Lib, uma biblioteca em Javascript que estou desenvolvendo. O código está em http://code.google.com/p/juicelib.

Quer aprender mais dicas avançadas de Javascript? Funções com argumentos variáveis em Javascript, Captura de teclas em Javascript - Parte 1, Captura de teclas em Javascript - Parte 2 e Concatenação eficiente de Strings em Javascript

Lendas urbanas: custo de alocação em Java

Segundo este artigo da IBM, estudos mostram que o tempo de alocação de um objeto em Java, utilizando a palavra chave new, é menor que o custo de um malloc em C. Os dados são interessantes: um new requer 10 instruções de máquina, enquanto que um malloc requer entre entre 60 e 100 instruções. Outro dado interessante do artigo é que o uso do Garbage Collector tornaria o processo mais eficiente do que o gerenciamento manual de memória, pelo fato do primeiro tratar de blocos maiores.

Meus comentários a respeito:

  1. A performance de Java melhorou muito ao longo do tempo. Mas o footprint da JVM, para qualquer programinha, ainda é muito grande. Alocação de objetos em Java pode ser mais rápido do que em C/C++, mas com certeza programas em C/C++ fazem um uso mais racional de memória.
  2. Esta área de otimização de memória, footprint, execução é a área de aprimoramento que a Sun deveria se concentrar exclusivamente, em vez de ficar inventando moda com closures, Java FX e outras coisas script-like. Java não vai conseguir concorrer com linguagens de script. Java tem que concorrer com C++.
  3. A minha experiência com alocação de grandes quantidades de dados em Java, aplicada ao processamento de sequências genéticas, não foi das melhores.

Curso online de Javascript

Quem estiver procurando um curso minimamente estruturado de Javascript, que aborde conceitos interessantes da linguagem e não apenas formas de manipulação de DOM, aconselho este site: http://eloquentjavascript.net/.
Algo que me chamou a atenção foi o fato do curso abordar tópicos de cursos básicos de teoria de computação (raramente abordados quando se fala em Javascript) como estruturas de dados, heaps, árvores binárias, algoritmos de busca e expressões regulares. Este texto se encaixa na teoria de que Javascript é uma linguagem real de programação, e não apenas uma ferramentinha pra deixar páginas web mais dinâmicas.

Dicas Python

Achei este artigo sobre o modo Python de ver a vida

http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html

Bem interessante!

Next Page »