2006-12-31

PHP estúpido


É por essas e por outras que não gosto de PHP:

<?php
if ("6 Galinhas" + "4 Gansos" == "10 Aves") {
echo "PHP estúpido\n";
} else {
echo "Seria inteligente se caísse aqui\n";
}
?>


Tal lógica é arma na mão daqueles que são a favor de linguagens stupid-friend – ou seja, que não deixam você fazer o que você quer, como Java e C++ – contra linguagens poderosas – como Python, Ruby e Lua – e com razão (quando falam de PHP, claro).

[]'s

2006-12-24

Feliz Natal

Olha só que legal o presente que ganhei do Papai Noel...

Encontrei na rua de repente um amigo meu que eu já não via há mais de cinco anos, o Bruno!

(Aliás, acho que já faz quase sete anos...)

Feliz Natal, Bruno! Pra você e para todos vocês meus amigos!

[]'s

PS: A Luciana disse que as meninas mandam recado pro Fausto por você, Bruno, porque você é Alexandre (o Grande) só em segundo (nome). Hehehehehehehehehe =D

2006-12-21

Amigo Oculto

Lua Neste final de ano estão todos pensando em amigo oculto...

Meu professor de Java passou como trabalho criar um serviço de amigo oculto, o pessoal lá do trabalho está fazendo amigo oculto e um colega meu estava com um problema com um programa (acho que em PHP) de amigo oculto.

O Silvio me sugeriu fazer um algoritmo de sorteio para amigo oculto em Lua, já que estamos envolvidos com um Kepler.

Bem, vamos então brincar um pouco com isso. =)

A primeira coisa que precisamos é uma lista dos amigos. Isso pode ser feito via web (sinceramente não estou com muito saco para ficar fazendo a UI, quem quiser que se vire), linha de comando ou GUI, ao gosto do frequês. Depois esta tabela precisa ser passada para o algoritmo de sorteio.

Todos os recursos para criar uma interface web (e até para armazenar os dados em banco de dados) são fornecidos por Kepler, portanto não fiquem acanhados! Quem quiser bricar de fazer isso é só dar uma olhada nos manuais (mais tarde publico um artigo sobre como usar alguns recursos).

Bem, digamos que a tabela – indexada, um vetor – tenha sido passada para a variável amigos.

Vamos agora embaralhar essa brincadeira:

local temp = amigos
amigos = {}
while table.getn(temp) > 0 do
-- Pega um índice aleatório
local i = math.random(table.getn(temp))
table.insert(amigos, table.remove(temp, i))
end


Agora a tabela amigos está toda embaralhada. Vamos então gerar o dicionário que retornará para quem cada um deve dar seu presente:

-- tabela global amigoOculto
amigoOculto = {}
table.foreachi(amigos, function(i, v)
amigoOculto[v] = amigos[i + 1] or amigos[1]
end)


Cada um tem seu amigo oculto. Para saber para quem José das Couves tem de dar presente, é só pedir o valor de amigoOculto["José das Couves"]. Isso pode então ser guardado num banco de dados, por exemplo.

Agora é só desenvolver uma interface, para que cada um possa logar e ver para quem deve dar presente e não possa ver mais nada.

[]'s

Mudança de apelido

Gostaria de exclarecer a mudança de apelido.

Atualmente não estou trabalhando mais tanto com Python (daí a esperantização Pitonisto = Pitona programisto = «programador Python») quanto com Lua. Estou num projeto de pelo menos um ano e meio de Kepler, portanto preciso me dedicar mais a Lua do que a Python.

Não que eu tenha deixado de ser La Pitonisto! Apenas estou sendo mais lunatika (lunático) do que pitonista, por isso da mudança de apelido.

Reiterando: não larguei Python, que considero uma das melhores linguagens de programação já concebidas, apenas estou trabalhando mais com Lua – outra linguagem entre as melhores – do que com Python.

Consequentemente, é muito provável que eu passe a postar mais exemplos de Lua do que de Python, o que não deve ser problema para programadores, pois algoritmos e conceitos (o forte dos artigos) continuam sendo os mesmos.

Eventualmente alguma coisa de Python vai surgir, pois é minha linguagem do coração.

[]'s

PS: Tanto batalema (brigão) quanto lunatika são palavras de cunho pejorativo em Esperanto, mas gosto de ser provocativo.

2006-12-14

Scrotum plenum

Joseph Smith
Incrível como algumas pessoas acreditam que uma comunidade judaica migrou «magicamente» para o América do Norte durante a era pré-cristã, fundando uma comunidade rica e próspera que desapareceu sem deixar qualquer rastro cultural ou arqueológico.

Também é interessante como as pessoas consideram Plutão como um planeta quando ele não possui qualquer característica em comum com os demais planetas, fora o fato de ser esférico, como aliás muitos meteoritos – aliás, o que além da afirmação arbitrária da NASA define um corpo celeste como um planeta?

Bem, estes questionamentos são só uma introdução para chegarmos ao ponto verdadeiramente interessante.

Não posso entender como uma sociedade tão crédula e ingênua assim rejeita outras afirmações que possuem embasamento empírico.

Abrindo aqui um parênteses: já perceberam como em nossa sociedade científica a palavra «empírico» é quase um palavrão?

Os cientistas não-empíricos inclusive inventam causos para ridicularizar o Empirismo, atribuindo-lhe características esdrúxulas que não condizem com suas bases (o nome disso é FUD: fear, uncertain and doubt, medo incerteza e dúvida, que pode ser traduzido perfeitamente por desinformação).

Mas isso só acontece porque os postulados e os axiomas científicos simplesmente desmoronam sob a luz do Empirismo. Então o medo de estar errado leva as pessoas a agirem assim.


Vamos ao ponto...

Há até uns doze mil anos atrás mais ou menos, o nível do mar era muito mais baixo do que é hoje. Quando o nível do mar subiu aproximadamente 30% da área de superfície seca da terra foi inundada.

No período de dezesseis a doze mil anos atrás, a raça humana vivia principalmente na região litorânea (que hoje em dia é mar).

Bem, mergulhadores descobriram construções submersas na Índia e no Japão (e ainda algumas no Mediterrâneo) de causar enveja a qualquer arquiteto moderno. Essas evidências levam a crer que houve uma outra civilização humana durante esse período, com tecnologia (ao menos arquitetônica) semelhante à atual.

A postura científica atual é negar sistematicamente. O argumento contra é que as construções em formato de casas e prédios, com portas e janelas perfeitamente construídas e datadas de mais de doze mil anos, construções que até o século XIX nossa civilização não tinha tecnologia para criar, foram esculpidas pela erosão (alguém mais imaginou um carinha de jaleco se protegendo do sol escaldante com uma peneira?).

Nem mesmo pra pesquisar e descobrir o que é de verdade! Investigações sobre o caso são simplesmente proibidas.

Outro caso interessante é o da colonização das Américas.

Toda vez que se encontram evidências de presença humana anterior a onze mil e quinhentos anos atrás nas Américas, o procedimento padrão é destruí-las.

É sério!

Se algum arqueólogo alegar ter encontrado tais evidências, ele perde seu registro e as provas são destruídas. Evidências históricas de valor inestimável, que poderiam desvendar mistérios sobre a civilização e a cultura ameríndias, são destruídas para preservar o status quo científico atual.

Parece que é um monte de besteiras? Que não faria a menor diferença aceitar ou não? Pois é, parece... mas faz diferença sim...

Conhecimento é poder. Ignorância é submissão.

Por isso conhecimento e cultura são subversivos.

Está na hora da pergunta mestra: por que continuamos aceitando?

Aliás, tenho ainda mais cinco perguntas:

— Por que os E.U.A. invadiram o Iraque sem autorização das Nações Unidas e não existindo as tais armas químicas?
— Por que o Bush se reelegeu presidente dos E.U.A. mesmo tendo perdido nas urnas?
— O que fizeram com o Bin Laden, que foi agente da Cia?
— Por que o sinal do recreio tocou vinte minutos mais cedo?
— Cadê o Bob?

Um de meus professores, o Luc, falou de uma doença e acho que estou sofrendo dela: scrotum plenum.

[]'s

2006-12-10

Zen

Certa vez Sakuraten perguntou a mestre Dori:

— Qual é a verdadeira essência do Budismo?

Mestre Dori respondeu:

— Não faças o mal, pratica somente o bem. Praticar o bem é muito simples. É a essência do Budismo.

Qual religião possui ideais tão sublimes?

Mas ideais assim são encontrados em quase todas as religiões. O grande mal vem quando uma religião se institucionaliza, então seus princípios são torcidos para satisfazer os objetivos de seus líderes.

Isso ocorre sempre que há institucionalização, seja qual for a instituição.

De qualquer forma, segue uma ótima recomendação de leitura: A Tigela e o Bastão. Se você não «entender» os koans, pelo menos irá se divertir muito com os mondos, mesmo se você não tiver nada a haver com Budismo.

[]'s

2006-11-24

Gadgets do Google

Google

Um colega meu (Silvio) me passou esse link interessante: gadgets agitados do Google.

Acho que vou colocar alguns gadgets desses aqui na página... =)

[]'s

2006-10-25

Tcl/Tk

tcl.jpg Faz mais de um mês que não escrevo nada sobre Informática! Como já faz tempo que quero escrever sobre Tcl/Tk, vamos lá!

Tcl (Tool Command Language) é uma linguagem de script de código aberto muito poderosa. Desenvolvido por John Ousterhout, é costume dizer que é uma linguagem fácil, mas discordo.

É baseada em comandos (há uma variante orientada a objetos, chamada [incr Tcl]) com uma proposta interessante: tudo são strings!

Uma string em Tcl não precisa de delimitador, a menos que possua caracteres não-permitidos, como espaço e mudança de linha. Neste caso, o delimitador padrão é " (ou chaves).

Vamos a alguns exemplos...

Para se alterar ou ler o valor de uma variável usa-se o comando set. No exemplo a seguir, o script lê o valor da variável e retorna o dobro:

puts -nonewline stdout {Informe um número: }
gets stdin num
set num [expr [set num] * 2]
puts stdout "O dobro é [set num]"


O primeiro comando (puts) grava na saída padrão (stdout, a tela) a string «Informe um número: » sem mudar de linha (-nonewline). As chaves ({...}) também delimitam uma string, mas não interpretam colchetes (veja adiante).

Então o comando gets lê da entrada padrão (stdin, o teclado) um valor e atribui à variável num.

No próximo comando, set, usamos colchetes ([...]). Os colchetes interpretam um comando e são substituídos por seu retorno.

Ou seja, se foi atribuído o número 123 à variável num, no comando:
expr [set num] * 2


A parte entre colchetes, set num, retorna o valor de num, portanto 123, então os colchetes serão substituídos, recriando a string do comando assim:
expr 123 * 2


O comando expr avalia uma expressão matemática e retorna seu valor. Então:
set num [expr [set num] * 2]


Calcula o dobro do conteúdo de num e atribui a num.

O último comando grava na saída padrão (exibe na tela, ora pois!) a string «O dobro é [set num]», substituindo os colchetes pelo valor de num.

Repare que no primeiro puts, a string está protegida por chaves, e no segundo por aspas. Sempre dou preferência ao uso de chaves, mas as chaves protegem a string de interpretação, enquanto que aspas permitem a interpretação de colchetes (e cifrão, veja a seguir).

Agora, há um jeito de simplificar este código: seguindo as sintaxes de Shell e de Perl, existe uma forma simplificada de escrever set num com cifrão ($):
puts -nonewline stdout {Informe um número: }
gets stdin num
set num [expr $num * 2]
puts stdout "O dobro é $num"


Estruturas de controle


Dada a introdução, quero agora comentar algumas estruturas de controle de fluxo...

O primeiro comando é if. Sua estrutura é assim: if condição bloco1 [else bloco2]. Aqui, condição, bloco1 e bloco2 são strings (como não poderia deixar de ser). Então:
if {$sair == 1} {
puts stdout Tchau
exit
}


Se o conteúdo da variável sair for igual a um, exibe Tchau e sai. Como em outras linguagens, o bloco do else é executado se a condição for falsa.

A igualdade dupla (==) é usada para comparar valores numéricos. Para a comparação de strings, é usado o comando string com a opção equal:
if [string equal $var1 $var2] {
puts stdout {São iguais!}
} else {
puts stdout {São diferentes!}
}


Se quiser uma comparação ignorando maiúsculas e minúsculas, é só usar string equal -nocase. Repare que aqui a string interpretada pelo if como condição não é o comando em si, mas seu resultado.

Outro comando é o famigerado for.

A estrutura: for inicialização condição passo bloco. Novamente cada termo é uma string.

Vamos a um exemplo simples:
for {set i 0} {$i < 10} {incr i} {puts stdout $i}


Vai exibir todos os número de 0 a 9. Mas o comando é feio e difícil de guardar, né?

Então, existe um variante do Tcl chamado TclX (Tcl estendido), com um comando muito útil: loop.
loop i 0 10 {
echo $i
}


O comando echo de TclX é equivalente a puts stdout de Tcl. Em loop é possível passar mais um argumento entre o máximo e o bloco como passo (tipo: loop i 0 20 2 {...).

Obs.: TclX aceita todos os comandos de Tcl e implementa (estende) mais alguns.

Outros comandos de controle de fluxo interessantes em Tcl são: while, switch e foreach.

Funções


O comando para criar uma função é proc, que recebe como primeira string a lista de argumentos (separados por espaços) e como segunda string o bloco de comandos. Ex.:
proc fib n {
set a 0
set b 1
set c 1
while {$c <= $n} {
set aux $a
set a $b
incr b $aux
incr c
}
return $b
}


Então fib vai calcular a sequência de Fibonacci:

for {set i 0} {$i < 10} {incr i} {
puts stdout [fib $i]
}


Tk


Agora a parte mais interessante: Tk!

Tk é o toolkit gráfico de Tcl. O comando para usar Tcl com Tk é wish (e para usar TclX com Tk é wishx).

Em Tk a janela raiz é representada por um ponto (.).

Vamos criar um olá mundo!!!
#!/bin/sh
#\
exec wish "$0" "$@"

tk appname Ola
tk_setPalette gray

label .lbOla -text {Olá Mundo!!!}
button .btOk -text Ok -command exit

pack .lbOla .btOk


As três primeiras linhas são um cabeçalho pra podermos tornar o script executável.

O primeiro comando (tk appname) define o nome da aplicação (Ola), e o comando seguinte (tk_setPalette) diz pra usar como cores variantes de cinza (gray).

O comando label cria um rótulo (label) chamado lbOla na janela raiz (.) com o texto «Olá Mundo!!!». O comando button cria um botão chamado btOk na janela raiz (.) com o texto «Ok» e que, ao ser clicado (-command), executa exit (sair).

O comando pack indica que será usado o geometry manager pack. Geometry managers são gerenciadores de widgets (objetos gráficos) e geometria. Tk usa três: pack («empacota» em relação a posições esquerda, direita, em cima, em baixo, e tenta manter estas relações), grid (usa uma tabela – grade – colocando os widgets nas células da tabela) e place (desaconselhável: coloca os widgets em posições absolutas).

O gerenciador pack por padrão coloca os widgets a partir de cima (top), um de baixo do outro, centralizando (é possível mudar isso usando -side).

Para colocar os widgets um do lado do outro a partir da esquerda, usa-se -side left, a partir da direita: -side right. Para «amarrar» um widget em baixo, usa-se -side bottom.

O grid prende os widgets centralizados (é configurável) em células. A célula exata é informada através dos números de linha (-row) e coluna (-column). É possível que um widget ocupe mais de uma coluna (-columnspan) ou mais de uma linha (-rowspan).

O script acima poderia ser reescrito usando grid assim:
#!/bin/sh
#\
exec wish "$0" "$@"

tk appname Ola
tk_setPalette gray

label .lbOla -text {Olá Mundo!!!}
button .btOk -text Ok -command exit

grid .lbOla -row 0 -column 0
grid .btOk -row 1 -column 0


Para saber mais


O melhor lugar para se encontra mais informações sobre Tcl/Tk é na secção (n) (new) das manpages, mas um macete indispensável é digitar o seguinte comando no shell de um xterm:
tcl <<< tclhelp


[]'s

PS: Procure screenshots no Google!

2006-10-10

Esperanto - complemento

Estes dois textos abaixo são traduções dos primeiros parágrafos do último artigo respectivamente para o Esperanto e para o Arcaicam Esperantom (em Esperanto: Arĥaika Esperanto, criado por Manuel Halvelik).

São apenas exemplos. =)

--------

Antaŭ iom da tempo mi volas skribi pri la Esperanto.

Kutime traktata per antaŭjuĝo, nekonfideco kaj nesciigo, la Esperanto estas la planlingvo plej parolata kaj plej bone planita el la homa historio. Estis planita de Ludwik Zamenhof je 1887 (mil okcent okdek sep), sed akiris propran vivon tra la jardekoj.

Ĝia lernado estas dekope pli rapide ol la lernado de la angla, kaj eble estas akiri bonecgradon neakireblan en la angla (aŭ alia ajn etna lingvo) de nenata parolanto.

— Kial?

Nur ĉar neniam iu estos pli bona fluparolanto ol nata parolanto. Ĉiam estos parolmaniero – ne signifas, ke ne estas parolmaniero de nata parolanto, sed ĝia estas la «ĝusta parolmaniero»...


--------

Antez iohem da tempo mihi volams scribir pri Esperantom.

Cutime tractatam per antezyughom, necomphidetzom cay nestziagom, Esperantom estat planlingvom pley parolatam cay pley bone planitam el homam historiom. Estit planitam de Ludwik Zamenhof ye 1887 (mil octzent ocdec sep), sed aquirit propran vivon tra la yardecoym.

Eghies lernadom estat decope pli rapide ol lernadom angles, cay eble estat aquirir bonetzgradon neaquireblan en anglam (aw alia ayn etnam lingvom) de nenatam parolantom.

— Cuyal?

Nur char nemyahem iu estot pli bonam phluparolantom ol natam parolantom. Cheyahem estot parolmanierom – ne signiphat, que ne estat parolmanierom de natam parolantom, sed eghies estat «ghustam parolmanierom»...

2006-10-04

Esperanto


Faz tempo que quero escrever alguma coisa sobre o Esperanto.

Geralmente tratado com preconceito, desconfiança e desinformação, o Esperanto é a língua artificial mais falada e mais bem planejada da história humana. Foi idealizado por Ludwik Zamenhof em 1887, mas ganhou vida própria ao logo das décadas.

Seu aprendizado é dez vezes mais rápido do que o do inglês, e é possível atingir um grau de excelência impossível de ser conseguido no inglês (ou qualquer língua natural) por um falante não-nato.

— Por quê?

Simplesmente porque ninguém nunca terá a mesma fluência numa língua que tem um falante nato. Sempre terá algum sotaque – não que o falante nato não tenha sotaque, mas seu sotaque é o «sotaque correto»...

Quando se fala de uma língua artificial, todos os sotaques e regionalismos são «corretos», visto que nenhum é o sotaque nativo.

Uma prova disso é o nascimento do Globish.

Além do mais, o uso de uma língua natural para comunicação internacional resulta necessariamente na supremacia cultural dos países que possuem tal língua como nativa/oficial. Exemplos disso são, além do efeito destrutivo das dominações inglesa na Índia e espanhola na Galiza; situações constrangedoras em reuniões da ONU, como quando um representante de um governo do Leste Europeu disse «my government sinks» («meu governo afunda») em vez de «my government thinks» («meu governo acha», obviamente por uma natural dificuldade com o fonema þ) ou quando houve uma má interpretação do termo «period», que causou o fim de uma audiência séria, terminada em uma mal-educada crise de risos dos representantes dos EUA e da Inglaterra.

Bem, tendo sido explicados os motivos, vamos esclarecer alguns equívocos.

Algumas pessoas dizem que a intenção do Esperanto é substituir as demais línguas do mundo. Isso é ridículo. O Esperanto é uma ferramenta de comunicação internacional e não pretende substituir nenhuma língua nativa, mas sim ser uma segunda língua para todos os povos do mundo.

Na verdade o efeito do Esperanto é justamente o contrário. Historicamente sabemos que o uso de uma língua natural para comunicação internacional resulta na destruição de outras línguas e consequentemente de suas respectivas riquezas culturais. Um bom exemplo é a influência do latim na cultura européia – alguém sabe alguma coisa sobre a língua ibérica pré-latina? Ninguém. Nada sobreviviu, a não ser alguns nomes de localidade que hoje não fazem sentido algum.

Devido a suas características peculiares, o uso do Esperanto como ferramenta de comunicação internacional fomenta a preservação das línguas naturais, preservando assim também a Cultura Humana.

Agora vamos ver um pouco do Esperanto em si.

O Esperanto é sistematicamente regular e possui apenas dezesseis (16) regras gramaticais, a saber:

  1. Não existe artigo indefinido. O artigo definido é la, que é invariável quanto a gênero, caso ou número.

  2. Os substantivos são terminados no nominativo singular por -o; para o plural acrescenta-se -j. Há apenas dois casos: nominativo e acusativo, o acusativo forma-se pelo acréscimo da terminação -n.

  3. Os adjetivos são formados pela terminação -a e devem concordar em número e caso com os substativos. O comparativo de superioridade é formado pelo vocábulo pli, o superlativo por plej. O complemento do comparativo é ol, o do superlativo é el.

  4. Os numerais cardinais são: 0 nulo, 1 unu, 2 du, 3 tri, 4 kvar, 5 kvin, 6 ses, 7 sep, 8 ok e 9 naŭ, 10 dek, 100 cent e 1000 mil. Dezenas e centenas se formam pela junção dos numerais (ex.: 23 dudek tri). O acréscimo da terminação -a forma os numerais ordinais (ex.: 2º dua).

  5. Os pronomes pessoais são mi (eu), vi (tu, originalmente ci), li (ele), ŝi (ela), ĝi (neutro da terceira pessoa), ni (nós), vi (vós), ili (eles) e oni (indefinido). Os pronomes possessivos são a forma adjetiva, ou seja, acrescentando-se a terminação -a.

  6. O verbo não varia quanto a número ou pessoa. Os modos/tempos são: presente -as, passado -is, futuro -os, condicional -us, infinitivo -i, imperativo -u, particípio presente ativo -ant-, particípio passado ativo -int-, particípio futuro ativo -ont-, particípio presente passivo -at-, particípio passado passivo -it-, particípio futuro passivo -ot-. A voz passiva se faz com o verbo esti (ser/estar) e o complemento é de («feito por mim» = «farita de mi»).

  7. Os advérbios são terminados em -e. Os graus comparativos e superlativo são formados da mesma forma que os adjetivos.

  8. Todas as preposições por si pedem o caso nominativo.

  9. Cada letra representa um som e cada som é representado por uma única letra, invariavelmente. Toda palavra é lida como se escreve.

  10. Em toda palavra completa (isto é, sem apóstrofo), a sílaba tônica é a penúltima (paroxítona). Nas palavras apostrofadas, a sílaba tônica é a última (oxítona).

  11. Palavras compostas são formadas por justaposição, sendo a principal a última. Terminações gramaticais são consideradas palavras autônomas.

  12. A negação é simples (não = ne). Havendo outra palavra negativa, retira-se a partícula ne.

  13. Para indicar alvo, direção, usa-se a terminação do acusativo (-n). Ex.: kie = onde, kien = para onde.

  14. Toda preposição tem significado constante e bem definido; quando o significado da preposição não é bem definido, usa-se a preposição je.

  15. Palavras estrangeiras são usadas sem alteração.

  16. A terminação substantiva -o e o -a de la podem ser substituídos por um apóstrofo.


Quem leu todas as regras (ou pelo menos as dez primeiras) deve ter visto que a nona regra diz que para cada letra há um som e para cada som um letra.

São 28 letras no Esperanto, cada uma representando um único e invariável som:

  • A – «a» aberto comum, como em casa;
  • B – como em bola;
  • C – como o «t» do latim clássico antes de «i», é um «ts» bem fundido (X-SAMPA: /ts)/);
  • Ĉ – como em tchau;
  • D – como em dado;
  • E – «e» fechado, como na própria palavra fechado;
  • F – como em faca;
  • G – como em gato;
  • Ĝ – como no inglês jet;
  • H – como no inglês hat;
  • Ĥ – exatamente a letra tcheca, ou seja velar uvular fricativo (X-SAMPA: /kR)/);
  • I – «i» vocálico, como em indígena, nunca como em meio;
  • J – semivogal «i», como em meio (é uma consoante);
  • Ĵ – como em jato ou geléia;
  • K – como em carro;
  • L – como em lua;
  • M – como em meio;
  • N – como em nada;
  • O – «o» fechado, como em outro;
  • P – como em pato;
  • R – como em raro;
  • S – como em sapo;
  • Ŝ – como em chato;
  • T – como em tatu;
  • U – «u» vocálico, como em último, nunca como em meu;
  • Ŭ – semivogal «u», como em meu (é uma consoante);
  • V – como em vovó;
  • Z – como em casa.


Não existem vogais nasalizadas, nem «é» e «ó» (abertos). Também não fazem parte do alfabeto as letras q, w, x e y.

Bem, depois disso tudo, seria legal mostrar alguns exemplos, né?

  • «Olá!» – «Saluton!»
  • «Como vai você?» – «Kiel vi fartas?»
  • «Vou bem, obrigado.» – «Bone, dankon.»
  • «Onde fica o banheiro?» – «Kie estas la necesejo?»
  • «Que horas são?» – «Kioma horo estas?»
  • «Quinze para as duas da tarde.» – «Estas kvarono antaŭ la dua posttagmeze.»
  • «Quando vai embora?» – «Kiam vi foriros?»
  • «Ao meio dia.» – «Je tagmezo.»
  • «Você gosta de morangos?» – «Ĉu ŝatas al vi fragoj?»
  • «Sim, gosto.» – «Jes, ŝatas.»
  • «Já está na hora!» – «Jam temp'está!» (herdado do Pra-Esperanto)
  • «Vamos ao trabalho!» – «Ek al la laboro!»
  • «Minha nossa!» – «Ho ve!»
  • «Coitado do cachorro!» – «Ve al la hundo!»
  • «Onde você está indo?» – «Kien vi aliras?»
  • «Estou indo para casa.» – «Mi aliras hejmen.»


Agora já chega! Se quiser aprender procure o Kurso de Esperanto ou o KCE! Depois procure por Gerda malaperis!.

[]'s

2006-09-29

Quantos o diabo matou?

Baal Gente, este texto que segue a baixo me foi enviado por um amigo meu de mente aberta e é de autor desconhecido. Na verdade o autor postou o texto num fórum e não quis se identificar, portanto o mais correto seria dizer que é de autor anônimo.

Não concordo como todos os pormenores do texto e não tenho a pretenção de fazer-lhe apologia, mas concordo com o sentido geral e gostaria de dar a você leitor a chance que tive de refletir sobre o assunto. Acho esse tipo de crítica muito importante para todo religioso.

Segue então o texto:

QUANTOS O DIABO JÁ MATOU? ZERO! E DEUS? LEIAM

O Antigo Testamento é um receituário de dominação prescrito pelos judeus aos povos reféns do banditismo israelita. O próprio nome Israel significa: simaquia com Deus (Deus como seu aliado em guerra).

Raciocine: No Antigo Testamento, Deus é retratado como uma propriedade exclusiva dos judeus (hebreus). Por quê? Porque foram eles quem inventaram Deus! A mulher é descrita como um objeto do homem, no pentateuco. Por quê? Porque aquele conjunto de livros foi escrito integralmente por homens, daí o cunho machista. Ora, se o corpo humano consta de 206 ossos, como poderia a mulher se resumir à fração de 1/206 de um homem, já que Eva foi feita duma costela? (Gen. 2:22).

Outra incoerência: ainda, no Antigo Testamento, Deus aparece na condição de precursor dos métodos de destruição em massa (o aniquilamento de Sodoma e Gomorra), Gen. 19:1-29, algo assemelhado a uma explosão nuclear. Nas dez pragas do Egito, (Ex. 7:14 e 11:10), Deus teria sido o preconizador da guerra química (envenenando as águas do rio Nilo), e da guerra biológica (disseminando gafanhotos pelo reino de faraó). Será se Deus, onisciente e todo-misericordioso, teria cometido aquelas barbáries? Mesmo porque o Senhor já sabia previamente que, no segundo milênio do nascimento do Seu filho unigênito, Seus ensinamentos genocidas iriam habilitar o Bin Laden para atentar contra uma nação protestante, os EUA, que se dizem ser habitados por um povo "santo e escolhido"?

A Deus também o papel de pivô do primeiro fratricídio lavrado na bíblia, ao ficar grato pelas ovelhas recebidas de Abel, e se mostrando indiferente para com as batatas de Caim. Percebendo que o Senhor não reconheceu a sua humilde oferta na mesma medida das ovelhas doadas pelo seu irmão Abel, isso despertou um sentimento de inveja em Caim; levando o segundo a assassinar o primeiro. (Gen. 4:8).

Aqueles escritos são apenas malícias humanas. Em estando sentenciadas nas Sagradas Escrituras, todas as atrocidades perpetradas pelos judeus às outras tribos recebiam uma conotação "divina" e, como tal, eram legitimadas e justificadas. Ainda hoje, israelenses (judeus) e árabes (muçulmanos) se arrastam em um conflito milenar e sangrento, nas terras da Palestina. Tudo teria começado com a interpretação preconceituosa de uma lenda bíblica. Ismael, filho bastardo de Abraão com a escrava egípcia, Agar, teria dado origem ao povo árabe (Gen. 16:5-15). E Isaac, filho legítimo de Abraão (ex-Abrão) com a sua verdadeira esposa, Sara (ex-Sarai), haveria sido o progenitor dos judeus (Gen. 21:1-7). Então, para desqualificá-los, os judeus tacham os árabes de descenderem do filho duma puta. Com efeito, se considerarmos aquelas narrativas nefastas (assassinatos, seqüestros, estupros, pilhagens; por exemplo, leiam: Num. 31:17-18; Deut. 20:10, 13 e 14; 23:13; 25:11 e 12; 2º Reis 19:35; Juízes 8:10; 2ª Cron. 28:6; 13:17 e 14:19; Isa. 37:36......); uns executados pelo próprio Deus, outros pelos Seus jagunços: Davi, Moisés etc., (cumprindo ordem divina), então, temos de dar a mão à palmatória, e concordarmos com o Santo Ofício, como tendo sido um cumprimento fiel às Sagradas Escrituras por parte da Igreja Católica. Acaso, imitar os ensinamentos bíblicos constitui um pecado ou uma virtude? Inquisição que não foi exclusiva apenas do catolicismo, o protestatismo também valeu-se de morticínios para fazer valer seu proselitismo. João Calvino, o pai dos presbiterianos, mandou queimar na fogueira o médico espanhol, Michel Servert Grizar. E tantos outros atos sangrentos promovidos pelos protestantes.

Hoje muitas pessoas ficam horrorizadas ao saberem que, nas favelas das grandes cidades, os comerciantes são obrigados a pagar pedágios para os chefes do tráfico local, se quiserem continuar exercendo suas atividades mercantis. Ora, ora esse critério de sujeição extorsiva já era praticado pelos "capangas de Deus", quando submetiam determinadas comunidades à condição de Cidades Tributárias. Para sobreviverem sem serem molestadas pelos judeus mandões, mafiosos, tais comunidades-reféns tinham de transferir grande parte de sua força de trabalho e riquezas aos seus "protetores". (Vide em 1ª Cron. 18 e 2º Sam. 8).

O mais gozado e incoerente de tudo isso é que Deus, retratado como bandido e sanguinário no Antigo Testamento, ainda assim, Ele personifica a figura do BEM. Ao passo que o diabo, embora nunca tenha matado ou roubado alguém, paradoxalmente, foi eleito como a fonte do MAL.

(Autor anônimo)


Pensem bem sobre o assunto agora.

[]'s

2006-09-22

Voto em vão

Dana Carvey Ontem me perguntaram em quem pretendo votar para governador. Cometi o erro de responder.

Fui então duramente criticado, segundo meus críticos porque vou jogar meu voto fora.

Mas tenho então de votar no candidato que lidera as pesquisas? Ou no segundo lugar? Pensei que o voto fosse um exercício de cidadania, não de dominação (na verdade, escravidão intelectual)!

Tudo bem, não concordo com essa quantidade enorme de candidatos a um cargo único, acho que isso só visa dividir os votos, mas nem por isso vou me render aos líderes de pesquisas. =P

Algumas dicas para quem vai votar:

  • Conheça as propostas dos candidatos, veja se elas atendem a necessidades sociais ou se não passam de pagamento pelo seu voto.
  • Conheça o passado dos candidatos pra saber se eles costumam cumprir as propostas ou não.
  • Não se deixe levar por pesquisas, que só servem para manipular seu voto.
  • Conheça as pesquisas. =P
  • Tenha a mesma preocupação com qualquer voto, seja pra presidente ou deputado estadual.
  • Se você votar com consciência, mesmo que seu candidato não vença, não foi em vão. Seu voto não foi jogado fora. Saiba que pelo menos você está insentivando seu candidato – e deixou de votar num político desonesto só porque todo mundo votou nele.


É tudo o que tenho a dizer.

[]'s

2006-09-19

As doze mais

Poliedro Recentemente o sítio NotíciasLinux.com.br divulgou uma lista das «10 linguagens de programação que você deveria aprender agora mesmo».

Nós (Rodrigo Cacilhas e Walter Cruz) conversamos e decidimos fazer nossas próprias lista. Qual não foi nossa surpresa ao descobrir que estas só divergiam em duas linguagens!

Então resolvemos escrever um artigo sobre as «12+». =)

Segue então a lista em ordem alfabética.

C



rank Cacilhas: 1
rank Walter: 3


C é a raiz de todas as linguagens modernas. Ninguém que precise de desempenho e confiabilidade abre mão de C.

Apesar de ser uma linguagem de alto nível – não confunda notação matemática com baixo nível! –, proporciona um melhor entendimento sobre o funcionamento do sistema, ajudando o programador a ter um pensamento menos dependente das peculidades de cada linguagem. É muito boa para explicar sobre o funcionamento de um computador nos seus detalhes.

C#



rank Cacilhas: 4
rank Walter: 10


É a melhor coisa que a Microsoft já fez. Tem lá seus problemas – como não poderia deixar de ser –, mas é uma linguagem eficiente e a melhor escolha para programação .NET.

Sem falar que é um plágio de Java.

C++



rank Cacilhas: -
rank Walter: 4


É uma linguagem importante devido à quantidade de aplicações desenvolvidas e também porque é a mais neutra das linguagens orientadas a objeto.

Ocupou um lugar relativamente importante na história da orientação a objetos, e foi mercadologicamente suplantada por Java.

Assim como C, ajuda o programador a ter um pensamento mais livre, a livrar-se da escravidão imposta por outras linguagens, apesar de não passar de um «C mais lento».

Cobol



rank Cacilhas: 10
rank Walter: -


Há um mercado legado muito grande de aplicações Cobol no Brasil que necessitam de manuteção – e constantes reindexações de bases de dados. =P

Em cinco ou dez anos talvez esta realidade mude, mas atualmente programadores Cobol são necessários e estão se tornando raros.

Fortran



rank Cacilhas: 9
rank Walter: -


Um linguagem antiga e ultrapassada sim, mas muito usada em ambiente acadêmico: pós-graduação e pesquisas.

Seu foco em análise numérica é perfeitamente conveniente para satisfazer as necessidades da Computação Científica e dificilmente outra linguagem conseguirá tomar seu lugar.

Portanto, para quem não quer ser mais uma batata no saco, é importante conhecer Fortran.

Java



rank Cacilhas: 2
rank Walter: 5


Java foi o que na verdade trouxe a orientação a objetos para a boca dos programadores.

Traz em seu bojo uma cultura que muitos chegam a pensar que é originada de Java, mas isso é um engano. Poderíamos citar dentre eles: Design Patterns, máquina virtual (já presente no Smalltalk na década de 80), refatoração (outro que nasceu na comunidade Smalltalk) e Programação Extrema (outro filho do Smalltalk).

O que mais dizer de Java? Não é lá grande coisa, nem se compara a C ou Fortran, mas funciona e bem.

Quando o front-end é mais importante que o back-end, Java é o que há. E ainda trabalha bem em aplicações de back-end – não tão bem quando as outras, mas a homogeneidade com o front-end compensa.

Um dos grandes problemas que eu vejo em Java é sua quase onipresença. Tem um problema? Não importa qual, mas a solução é Java. Outro grande diferencial é que essa é a linguagem que fala empresariês. É a linguagem que tem mais siglas, mais nomes e expressões complicadas, que no final acabam por se mostrar coisas simples na maioria dos casos. Não me entendam mal – depois de anos, resolvi aprender Java, e de fato estou até gostando. Existem na verdade duas coisas chamadas Java: a plataforma Java (JVM) e a linguagem Java. Apenas recentemente, com o advento do .NET a comunidade Java se tocou que nem todos gostavem de Java e passaram a alardear que outras linguagens poderiam rodar na JVM. Será que isso veio tarde demais? Espero que não.
(Walter)


Lua



rank Cacilhas: 8
rank Walter: 6


A linguagem de extensão por excelência! Combinada a C é imbatível.

Como se já não bastasse dizer que Lua conquistou seu nicho no mercado de animação, nós brasileiros temos um compromisso patriótico com esta linguagem (em vez de tratá-la com ignorância sistemática, como temos feito).

Sem contar que Lua tem méritos próprios. Metatabelas e orientação a objetos em Lua são coisas totalmente originais.
(Walter)


Perl



rank Cacilhas: 5
rank Walter: 8


Perl é uma linguagem pra gente grande. Uma das mais eficientes, não deixando a desejar a nenhuma outra.

É uma linguagem de propósito geral, mas se dá muito bem na Computação Científica, e serve como base de entendimento para outras linguagens mais modernas nela baseadas.

Outra curiosidade de Perl é ter a capacidade de irritar os trolls defensores de verdades absolutas, já que, sendo uma linguagem estrutura de tipagem fraca e orientada a objetos – ao contrário do que dizem os trolls –, é mais eficiente e aplicável do que qualquer linguagem da moda.

Python



rank Cacilhas: 3
rank Walter: 1



Python é tudo o que Java e Ruby pretendem ser.

Simples, clara, multipropósito, fortemente orientada a objetos, fortemente tipada e explícita. Sua sintaxe é clara e fácil, permitindo que iniciantes possam aprender Python rapidamente mesmo sem ter conhecimento algum de programação, mas ao mesmo tempo possui recursos avançados, como metaclasses, e incontáveis módulos, o que permite o desenvolvimento de aplicações complexas, como o Zope.

Assim temos uma linguagem – talvez a única – que faz a ponte entre o iniciante e o programador avançado.

Para saber mais veja Introducing Python.

Na minha opnião, é uma das melhores linguagens pra ensino.
(Walter)


Ruby



rank Cacilhas: 7
rank Walter: 7


Vem ganhando um certa importância atualmente, principalmente por causa dos programadores Java frustrados, mas que não querem dar o braço a torcer para Python e não aguentam esperar pelo Groovy, que é uma mistura de Ruby e Java, só que abençoada pelo JCP.

É uma linguagem da moda, mas faz coisas legais, como Ruby on Rails.

Embora seja muito semelhante a Python na superfície, são bichos de espécies diferentes. Ruby nasce do cruzamento de Perl com Smalltalk, e Python herda da linguagem ABC. Isso é algo importante de se lembrar.
(Walter)


Smalltalk



rank Cacilhas: -
rank Walter: 9


É a orientação a objetos por excelência. Mais antiga que C, quase todos os paradigmas que têm sua criação erroneamente atribuída a Java foram desenvolvidos no Smalltalk (vide seção sobre Java).

É uma linguagem de propósito geral, mas focada na interface com o usuário, há ferramentas profissionais interessantes, como Cincom VisualWorks.

Considero uma ironia que PHP tenha tido um grande sucesso e Smalltalk tenha ficado sempre em segundo plano. É particularmente chatinha de aprender – eu tentei fazer qualquer coisinha aqui e sempre me atrapalho. Isso porque a mente da maioria dos programadores já está viciada em sintaxes C-like. A história da informática seria certamente muito diferente se Smalltalk tivesse alcançado o merecido sucesso. Mas isso é díficil para quem está sempre um passo à frente.
(Walter)


SQL



rank Cacilhas: 6
rank Walter: 2


É impossível manter e interfacear uma base de dados sem conhecer seu funcionamento, e nada melhor para isso do que conhecer ANSI-SQL.

Considerando os principais bancos livres, podemos subdividir este tópico em três:

ANSI-SQL


Cobertor curto, mas conhecendo – e entendendo – o ANSI-SQL, é possível entender qualquer querência estruturada.

MySQL


O MySQL implementa um SQL ligeiramente diferente, mas nada de assustar. =)

PL/pgSQL


Sem o conhecimento das funções e peculiaridades do PostgreSQL, ele não passa de uma alterativa gorda e gulosa ao MySQL.

Mas com o entendimento do PL/pgSQL, o PostgreSQL se torna a mais poderosa das ferramentas de banco de dados.

Omissões Notáveis.

PHP
PHP é na minha opinião uma linguagem de desenho ruim, e que o passar do tempo apenas acentua isso.

JavaScript
Embora seja praticamente impossível desenvolver para a web sem ela, creio que ela já fica coberta pelo tópico Lua. Lua é muito semelhante à JavaScript, com objetos de primeiro nível, closures, e sintaxe parecida.
(Walter)

The Geek Code

-----BEGIN GEEK CODE BLOCK-----
Version: 3.1
GCS/H d? s:+ a C+++$ UL++$ P+>++ L++ E--- W++ N o K- w--
O>++ M>+ V- PS+ PE-- Y PGP(+) t+ 5 X+ R++>+++ tv+ b+(++)
DI- D+ G- e+(++)>++++ h---- r+++ y+++
------END GEEK CODE BLOCK------


[]'s

2006-09-15

Orientação a objetos em Perl

Uma das linguagens mais poderosas que há é Perl. Criada por Larry Wall em 1987 com base em C, Lisp, AWK e sed, Perl se tornou a base de diversas linguagens modernas, como Python e Ruby.

Possui uma abordagem sintática ligeiramente diferente, resultante de seu lema: «há mais de uma forma de fazer algo». Sua tipagem estupidamente fraca (só três tipos básicos, escalar, vetor e vetor associativo ou hash, mais alguns menos usados, como soquete) ajuda mais do que atrapalha, devido a sua abordagem, o que deixa os defensores da tipagem forte vermelhos de raiva.

Só que a implementação Perl de orientação a objetos é no mínimo excêntrica. Não usei outro adjetivo, como «alienígena», ou «transcendental», porque prefiro reservar estes para a implementação Perl de multithreading (vide as manpages perlthrtut e perlothrtut).

Há uma reformulação de POO para Perl chamada reform, mais parecida com Ruby, mas ainda não consegui fazer funcionar direito.


Em Perl, as classes são chamadas «pacotes», e são declaradas com o operador package (óbvio =P). O final do pacote é onde termina o arquivo ou onde é declarado um novo pacote. Caso o programador queira começar o programa em si no mesmo arquivo onde são criados os pacotes, basta declarar o início do pacote main:

package main;


Mas a forma mais elegante é criar um pacote .pm (por exemplo, exemplo.pm) e importanto o pacote no arquivo .pl (use exemplo;).

O método construtor é chamado new e o destruidor DESTROY (com letras maiúsculas mesmo). Há um procedimento padrão no construtor, que deve ser executado invariavelmente: (1) determinar a classe, (2) criar a instância, (3) transformar a instância numa instância «real» da classe (com o operador bless) e (4) retornar a instância pronta.

Alguns passos podem ser executados simultaneamente.

Vamos a um exemplo: a criação de uma classe Pessoa, com os atributos nome e ano_nasc (ano de nascimento):

package Pessoa;

sub new {
my $this = shift;
my $class = ref($this) || $this;
my $self = {
nome => shift,
ano_nasc => shift
};
return bless $self, $class;
}


Funções em Perl recolhem os parâmetros no vetor especial @_. No caso de métodos, o primeiro argumento é a instância escopo do método, assim como Python.

O operador shift retorna o «próximo» elemento do vetor – não sendo informado qual vetor, o padrão é @_. Se for a primeira execução de shift, retorna o primeiro elemento, se for a segunda, retorna o segundo elemento, e assim por diante.

Assim, $this recebe o primeiro argumento, que é o escopo de execução, ou seja, uma representação da própria classe (diferente de todos os demais métodos =P). O próximo comando refina este dado.

A declaração de $self cria escopo escalar semelhante a um hash, cujas chaves são nome e ano_nasc, e os valores são respectivamente o segundo e o terceiro elementos de @_.

O último comando faz duas coisas: transforma $self em uma instância verdadeira de $class (bless) e retorna a instância (return).

Bem, se você não conseguiu acompanhar até aqui, aconselho voltar lá em cima e começar de novo. =P


Vamos agora criar dois métodos! Um para retornar nome e outro para retornar a idade (calculada a partir de ano_nasc). Primeiro o nome:

sub nome {
my $self = shift;
my $v = shift;
$self->{nome} = $v if ($v);
return $self->{nome};
}


O primeiro comando atribui o escopo atual (a instância) a $self, depois a variável escalar $v recebe o próximo argumento (se não houver, recebe undef, equivalente a null de Java). Se foi passado algum parâmetro, este será atribuído à chave (atributo) nome, depois o valor deste é retornado.

Em suma, $inst->nome retorna nome. =)

Agora vamos à idade:

sub idade {
my $self = shift;
my (
$seg, $min, $hora,
$dia, $mes, $ano,
$dsem, $dano, $isdst
) = localtime(time);
return (1900 + $ano - self->{ano_nasc});
}


Novamente o primeiro comando atribui o escopo atual (a instância) a $self.

O próximo comando parece meio estranho, mas é só olhar melhor: localtime(time) retorna a hora local na forma de um vetor de nove posições, onde as três primeiras são a hora e as três seguintes a data (as três últimas também não são complicadas, e são até bem úteis). Assim conseguimos atribuir o número do ano a $ano.

Só que o número do ano em Perl é dado a partir de 1900! Então 2006 é retornado como 106.

Por último é retornada a diferença entre o ano atual (1900 + $ano) e o ano de nascimento atribuído à instância (ano_nasc).

Herança



Agora a coisa fica melhor!

Os defensores de linguagens orientadas a objetos dizem que Perl não é orientado a objetos e pronto, principalmente os programadores C++ e Java. Estranho isso.

Por que estranho?

Porque Perl além de suportar orientação a objetos, suporta herança múltipla, algo que Java não suporta. =P


Em Perl, a herança (simples ou múltipla) é implementada através do vetor @ISA.

Como exemplo, será criado um pacote Retangulo, com o método desenhar e as chaves (atributos) altura e largura, e será criado um pacote filho Quadrado:

(¡NOVO! Corrigi alguns erros de digitação no código seguinte que poderiam impedir o funcionamento do programa ¡NOVO!)

package Retangulo;

sub new {
my $this = shift;
my $class = ref($this) || $this;
my $self = {
altura => shift,
largura => shift
};
return bless $self, $class;
}

sub desenhar {
my $self = shift;
foreach (1 .. $self->{altura}) {
foreach (1 .. $self->{largura}) {
print '(*)';
}
print "\n";
}
}

package Quadrado;
@ISA = qw/ Retangulo /;

sub new {
my $this = shift;
my $class = ref($this) || $this;
my $lado = shift;
my $self = new Retangulo($lado, $lado);
return bless $self, $class;
}

1


[update 2008-09-09]Fica mais claro fazendo assim:
package Quadrado;
use base qw/ Retangulo /;
[/update]


Não há nada de novo no pacote Retangulo. Talvez o foreach, que repete o loop para cada elemento do vetor informado (o elemento atual é atribuído à variável especial $_). Veja a manpage perlintro.

O pacote interessante é Quadrado. Logo de cara temos a atribuição de @ISA. O operador qw «quebra» a string fornecida (entre / /) em um vetor, onde cada palavra (qw = queue of words, lista de palavras) é um elemento. É aconselhável usar qw sempre que for feita uma atribuição a @ISA.

Assim, Retangulo foi atribuído a @ISA de Quadrado, tornando este filho do primeiro.

O construtor é bastante simples, nada diferente do que foi visto até aqui – obs.: $lado está recebendo o primeiro parâmetro (segundo argumento, o primeiro é o escopo).

A operação mais diferente é a atribuição de $self, que chama o construtor do pacote (classe) pai. Repare que, da forma como está sendo feito, as duas chaves (altura e largura) receberão o valor de $lado (definição de quadrado, lembra?).

Para testar, salve este trecho de código como formas.pm, então crie um arquivo teste.pl:

#!/usr/bin/perl

use formas;

print "Qual o tamanho do lado do quadrado? ";
chomp($_ = <>);

my $sq = new Quadrado($_);
$sq->desenhar;

exit;


Execute então o script:

$ perl teste.pl
Qual o tamanho do lado do quadrado?


Informe um valor (5, ou 12, ou o que você quiser), pressione <Enter> e veja o resultado.


Espero que alguém tenha se empolgado para aprender Perl. =)

[]'s

Referências:

2006-09-13

Metaclasses: singleton

Brincando na aula de PRJ no ISTCC-P, surgiu o assunto singleton (a página em inglês é muuuuito mais completa), que é quando queremos que uma determinada classe tenha somente uma instância, e esta instância seja retornada na tentativa de se criar novas conexões.

Complicado? Que nada! Vou dar o mesmo exemplo que o professor deu: imagine que seu programa tenha uma conexão com um banco de dados. Cada vez que uma função do programa tente criar uma conexão, esta receberá a conexão que já foi criada.

Simples! Em Java isso é feito «bloqueando» o construtor da classe e usando um método para intermediar esta requisição:

class Conexao {
//Atributos
...
private static Conexao inst = null;

//Metodos
private Conexao() {
// O construtor é privado!
...
}

public static Conexao nova() {
// Este método fará as vezes do construtor
if (inst == null)
inst = new Conexao();
return inst;
}
...
}


Então, para criar uma conexão, não será usado new, mas o método nova():

Conexao conn = Conexao.nova();


Funciona. =)

Mas o grande problema aqui é que singleton não é transparente. É preciso estar atento e não usar new, que é a forma natural de se obter uma instância de classe. =P

Outras linguagens já têm uma abordagem diferente, possibilitando a criação de singleton transparente. As duas que comentarei aqui são Python e Ruby.

Ruby



Como Ruby não é meu forte, vou falar desta linguagem primeiro. É extremamente simples fazer singleton em Ruby:

requires 'singleton'

class Conexao
include Singleton
...
end


Bastou um include! Isto é incrivelmente simples, prático, mas tem um pequeno problema pra quem está tentando entender: como é que funciona?

Fica aí a dúvida pros rubyistas explicarem nos comentários. =)

Singleton em Python usando herança



Já Python é outra história. Como sou programador Python (La Batalema Pitonisto, lembram?) posso usar melhor esta linguagem para mostrar como as coisas funcionam.

Uma forma de fazer singleton em Python é usando herança. Assim podemos criar uma classe Singleton que implemente a funcionalidade (feature) desejada e herdar as demais classes desta.

Bem, em Python o construtor é __init__(), mas há um método que é chamado antes do construtor, que é __new__(), e funciona de forma muito parecida com new de Perl. Ele cria a nova instância e a retorna.

Na verdade, em Python quando fazemos:

a = X(b, c)


Por trás o que está acontecendo é:

a = X.__new__(X, b, c)
a.__init__(b, c)


Então podemos sobrescrever este método para termos singleton. A idéia é que, se já houver uma instância, o __new__() retorne esta instância em vez de uma nova:

class Singleton(object):

__inst = None

def __new__(cls, *args, **kw):
if cls.__inst is None:
cls.__inst = object.__new__(cls)
return cls.__inst

def __copy__(self): #(trecho novo de código!)
return self

def __deepcopy__(self, memo=None):
return self


Então temos o atributo privado de classe __inst que contém nada (None) na criação da classe, mas depois será uma referência à instância única.

O método __new__() está preparado para receber argumentos genéricos (*args, **kw), mas não fará nada com eles – é problema do __init__(). A função do __new__() é verificar se já existe a instância única, se não existe, cria, depois retorna ela.

Podemos usar a herança desta forma:

class Conexao(Singleton):
...


Ou seja, funciona exatamente como em Ruby.

Agora vamos à crítica!

O grande problema aqui é justamente a sobrescrita um método geralmente esquecido...

Isso pode gerar uma série de problemas quando surge a herança múltipla ou quando o método é novamente sobrescrito – ou foi também sobrescrito por outra classe pai.

Para contornar estes problemas uma boa saída é o uso de metaclasses.

Singleton em Python usando metaclasse



Em Python classes são objetos! São instâncias de type. Metaclasses são classes filhas de type, portanto suas instâncias também são classes.

Podemos criar uma metaclasse que tenha procedimentos que precedam o construtor e o chamem só se for preciso.

Assim as classes instância da metaclasse podem ser filhas de outras classes e possuir até herança múltipla sem se preocupar com sobrescrita de métodos – pois todo tratamento é realizado num escopo ainda mais protegido.

Numa metaclasse o método que precede o construtor das classes instância é – por motivos óbvios para programadores Python – __call__().

Vamos criar então a metaclasse! Vou chamar de unique em vez de singleton (Por quê? Porque eu gosto, ora pois!):

class unique(type):

def __init__(cls, name, base, dict):
super(unique, cls).__init__(name, base, dict)
cls.__inst = None
cls.__copy__ = lambda self: self #(nova linha!)
cls.__deepcopy__ = lambda self, memo=None: self #(nova linha!)

def __call__(cls, *args, **kw):
if cls.__inst is None:
cls.__inst = super(unique, cls).__call__(*args, **kw)
return cls.__inst


Esta metaclasse é bela! Vamos dissecá-la:

O construtor chama seu super (construtor da classe pai, type) e depois, a única coisa que faz de diferente é criar um atributo privado de classe __inst. Só que este atributo é «mais privado» do que os normais. =)

Aqui o escopo para o atributo privado não é a classe, mas unique (a metaclasse). Podem imaginar o nível de proteção disso? É como em C++, só que funciona. =)

Bem, o método __call__() faz exatamente o mesmo que __new__() no exemplo que usa herança, só que de forma mais eficiente. A implementação disso poderia ser:

class Conexao(object):

__metaclass__ = unique

...


Simples! E sem os problemas que poderiam vir com o uso de herança.

Mas aí vem um espírito de porco qualquer e pergunta:

Peraí! E se eu quiser usar também outra metaclasse, como autoprop?

Ahá! Não é tão complicado assim!

class Conexao(object):

__metaclass__ = type("__metaclass__", (autoprop, unique), {})

...


Complicou? É realmente... o uso de type não é tão intuitivo... =P

Mas há uma forma mais clara (aliás, eu prefiro) de fazer isso!

class Conexao(object):

class __metaclass__(autoprop, unique):
pass

...


Acho que é só isso! Comentários – ou melhor, complementos – por favor!

[]'s

Leitura recomendada:

2006-09-11

O Caçador de Crocodilos

Resolvi escrever sobre este assunto somente agora, pois somente agora consegui digerir o caso...

Steve Irwin, mais conhecido como o Caçador de Crocodilos, foi o diretor do Australia Zoo, fundado por seus pais Bob e Lyn Irwin em 1970.

A notícia de sua morte me foi dada da seguinte forma: «dá uma olhada no Google... aquele idiota dos crocodilos morreu»...

Tenho pena dessas pessoas que pensam assim. Vivem num mundo pequeno e sem sentido. Aquele «idiota» foi uma das pessoas mais admiráveis de nosso tempo, comparável a Jacques Cousteau.

Para dar uma pista da falta que Steve Irwin fará no mundo, segue uma citação dele:

Preservação ambiental é meu trabalho, minha vida, é tudo que sou. (Steve Irwin)


Vamos sentir saudades...

2006-09-01

Vírus

Algumas pessoas – de respeito até – dizem por aí que não existe vírus para GNU/Linux porque ele ainda não é tão usado quanto o Windows®, porém basta que o GNU/Linux se difundir mais para que comecem a aparecer os vírus.

Mas pensem só: por que então não há vírus para outros sistemas operacionais famosos?

Existem outros tipos de programa malicioso para GNU/Linux (e outros sistemas operacionais), como os worms, e com o aumento do uso do GNU/Linux consequentemente haverá o aumento desses tipos de programas. Mas não vírus.

O fato é o seguinte: não existe vírus pra GNU/Linux e nem nunca vai existir. Na verdade, «vírus» só existe pra Windows® e ponto.

Bem, é uma afirmação meio radical, não é? Mas ela tem base e vamos defendê-la neste artigo.

O Windows® possui um recurso em seu gerenciamento de processos que nenhum outro sistema operacional possui (pelo menos nenhum que eu conheça). O vírus é um mau uso desse recurso.

Mas que recurso é esse?

Quando um programa é executado, ele se torna um processo (task), ou seja, processo é um programa em execução. O sistema operacional mantém um controle de quais são os processos executados e o estado de cada um.

Cada processo possui pelo menos um fluxo de procedimentos, que é uma execução linear de comandos. Este fluxo é chamado thread. Alguns processos possuem mais de um fluxo – o que é chamado multithreading.

Só que multithreading é transparente para o sistema operacional! Quem gerencia os fluxos concorrentes é o próprio processo – inclusive o programador deve programar isso explicitamente.

Então cada processo possui um controle de quais são os fluxos atuais. Porém, reiterando, isso é transparente para o sistema operacional.

No caso do Windows®, a coisa é um pouco diferente.

No Windows® quem controla os fluxos dos processos é o cerne. É um recurso interessante, pois o sistema operacional pode iniciar novos fluxos em um processo ou interferir no processo encerrando alguns fluxos.

Agora, este recurso abre uma possibilidade de mau uso: um programa pode acessar o cerne e, através dele, se propagar para dentro de outros processos em execução.

Um vírus é isso! É um fluxo (thread) dentro de um processo que se replica para dentro de outros processos.

Em outros sistemas operacionais isso não é possível, pois o sistema nem sabe dos threads, então cada processo só possui threads que lhe pertencem. Já no Windows® o sistema pode interferir nos threads dos processos.

Não podemos de todo culpar a Microsoft por querer acrescentar recursos especiais – e inusitados – em seu sistema, de modo a criar um diferencial, mas que eles realmente pensaram mal, ah isso sim! E agora já é tarde para voltar atrás.

[]'s

PS: Valeu pela dica, Fábio!

2006-08-22

Adivinha quem é...

Dana Carvey Adivinha só que político brasileiro é este...

Vou dar uma dica: é de um partido que começa com P e termina com RONA...

[]'s

PS: A intenção aqui não é ofender ninguém! É uma brincadeira inocente!

2006-08-21

Eu odeio Informática

Parece estranha a afirmação, mas eu odeio Informática.

Não estou falando da «ciência» em si, mas da comunidade. Vejo que os informatas são divididos em alguns grupos e não me encaixo em nenhum deles, talvez por isso eu odeie Informática.

As pessoas tentam me classificar: quem está de um «extremo» diz que pertenço ao extremo oposto e vice-versa, mas não.

Posso definir alguns grupos:

Há aqueles que só pensam na melhor tecnologia. Há outro grupo que só pensa no retorno financeiro.

Há ainda os fanáticos, que defendem suas «filosofias» acima de qualquer bom-senso.

Todos pecam num ponto: crêem que os fins justificam os meios.

Que se dane se a economia nacional vai pro brejo, se o desemprego aumenta, se companhias nacionais vão à falência, se há escravização tecnológica... que se danem todos estes efeitos colaterais, o importante são os fins.

Olha só que horror isso!

Quando comento isso, sou comunista, fanático, reacionário... isso é tapar o sol com a peneira.

Não sei se me fiz entender – mesmo porque não estou com muito tempo para me explicar melhor –, mas gostaria de saber sua opinião.

Se você também «odeia Informática», deixe um comentário e explique por quê. Se não, deixe um comentário defendendo sua posição. Prometo que serei bonzinho e tolerante com a moderação. =)

[]'s

2006-08-18

Python 2.5-rc1

Saiu o release candidate 1 (rc1) do Python 2.5!!!

Pra quem programa em Python, a versão final – programada para 12 de setembro – é o evento mais esperado do ano. =)

As mudanças mais sensíveis são:

  • ASCII se torna o charset padrão – para usar outras codificações é preciso explicitar;
  • ElementTree (um parser pra XML), pysqlite (interface com SQLite3) e wsgiref (módulo para trabalhar com WSGI, um protocolo de desenvolvimento para web) se tornam módulos padrão do sistema;
  • adicionado um módulo para tratar senhas shadow;
  • unificados try-except e try-finally (tratamento de excessões) numa única estrutura;
  • algumas alterações legais no comportamento de import;
  • o esperadíssimo bloco de with_statement (salvando futuros códigos de uma série de chamadas a métodos close() =P)!


Um lista completa das mudanças pode ser encontrada no PEP-356.

[]'s
Rodrigo Cacilhas

2006-08-16

Reblogging: Code Jam

2006 Google International Code Jam Hoje vou fazer algo do qual não sou muito fã: vou dar uma de «reblogger». =)

O Silvio me enviou o seguinte endereço: Code your way to Gotham, falando do 2006 Google International Code Jam, uma espécie de maratona de programação promovida pelo Google, cuja a final este ano será realizada no centro de Manhattan.

O primeiro lugar levará US$10.000 e todos os finalistas receberão pelo menos US$750. Ainda por cima, obviamente o pessoal do Google estará procurando por novos talentos para contratação durante a competição.

Alguém se habilita?

[]'s

2006-08-15

Números complexos em Lua

Lua é uma poderosa linguagem de programação procedural, orientada a objetos, de tipagem dinâmica, baseada em tabelas associativas e semântica extensível, projetada e implementada no Tecgraf, Grupo de Computação Gráfica da PUC-Rio, em 1993.

Foi projetada para ser uma linguagem de extensão, para que fosse possível que os usuários reconfigurassem aplicações sem necessidade de recompilação das aplicações.

Atualmente é a linguagem de script mais usada para programação de jogos (em segundo lugar está Python).

Uma curiosidade de Lua é não haver um módulo de suporte a números complexos. Vamos então implementar um.

Metatabelas



Em orientação a objetos, o «tipo» de objeto é chamado classe. Lua trabalha com um conceito ligeiramente diferente: metatabelas e metamétodos.

Em Lua, os tipos definidos pelo usuário são sempre tabelas, cujos elementos podem ser quaisquer objetos de primeira ordem, inclusive funções (métodos) e outras tabelas.

Ainda é possível associar uma metatabela a uma tabela: esta metatabela define como a tabela deve comportar-se. Então a metatabela funciona de forma semelhante a classe em outras linguagens.

Os métodos da metatabela definem como a tabela reagirá às operações matemáticas, às comparações e até como responderá quando for solicitado o valor de um elemento elemento que não possui (podemos chamar isso de valores default).

Para as operações matemáticas temos os métodos __add() (adição), __sub() (subtração), __mul() (multiplicação), __div() (divisão) e __pow() (potência). Ainda há os métodos __unm() (inversão de sinal), __tostring() (conversão para string) e __concat() (concatenação).

As operações comparativas são __eq() (igualdade), __lt() (menor que) e __le() (menor ou igual). Alguém pode perguntar «e quanto a ‘maior que’ e ‘maior ou igual’?». A resposta é simples, se a > b então b < a, capicci? A mesma idéia é para diferença (a ~= b é o mesmo que not a == b).

Está confuso até aqui? Vai clarear assim que começarmos a criar nossa metatabela. Vamos então à criação de nossa metatabela: primeiro usaremos o construtor de tabela ({}) para criar a metatabela com alguns valores default e em seguida criaremos o construtor próprio para números complexos:

local cmt = { real = 0, img = 1 }
cmt.__index = cmt

function cmt:new(o)
o = o or {}
setmetatable(o, self)
return o
end


Números complexos são formados por duas partes, uma real e uma imagem, então criamos nossa metatabela assim.

A sintaxe cmt:new(o) é um açúcar sintático para cmt.new(self, o) e é usada para métodos.

O comando setmetatable(o, self) define que a metatabela de o será self e o elemento __index informa de onde a tabela deve retirar os valores default (para elementos não definidos), e será a própria metatabela.


No entanto queremos ter alguma flexibilidade ao criar um número complexo:
  • Se não for passado argumento, queremos que a função retorne i;
  • Se for passado um número (real), queremos retornar ele mesmo;
  • Se for passado um número complexo, queremos retornar uma cópia dele;
  • Se forem passados dois números (reais), queremos que o primeiro seja a parte real e que o segundo seja a imagem (se a imagem for zero, retorne somente a parte real).


Assim sendo, podemos criar a seguinte função:

local function new(...)
if arg.n == 0 then
-- nenhum argumento retorna i
return cmt:new { real = 0, img = 1 }
elseif arg.n == 1 and type(arg[1]) == “number” then
-- um argumento: numero real
return arg[1]
elseif arg.n == 1 and getmetatable(arg[1]) == cmt then
-- um argumento: numero complexo
return cmt:new { real = arg[1].real, img = arg[1].img }
elseif arg.n == 2 and
type(arg[1]) == “number” and type(arg[2]) == “number” then
-- dois argumentos reais
if arg[2] == 0 then
return arg[1]
else
return cmt:new { real = arg[1], img = arg[2] }
end
else
error “parse error”
end
end


Mostrando nosso número complexo



O primeiro método que definiremos será para exibir nosso número complexo.

Sem este método, o comando abaixo retornaria assim:

lua> print(numero)
table: 0x80725e0


E queremos que, na verdade retorne algo do tipo:

lua> print(numero)
3 + 2i


Então precisamos definir o método __tostring(). Mas não será tão fácil assim!

Imagine só: precisamos definir pelo menos oito casos diferentes:
  1. imagem = 0
  2. real = 0, imagem = 1
  3. real = 0, imagem = -1
  4. real ≠ 0, imagem = 1
  5. real ≠ 0, imagem = -1
  6. real = 0, imagem ≠ 0
  7. real ≠ 0, imagem > 0 e imagem ≠ 1
  8. real ≠ 0, imagem < 0 e imagem ≠ -1


Vamos então!

function cmt:__tostring()
local a, b = self.real, self.img
if b == 0 then
return tostring(a)
elseif b == 1 and a == 0 then
return “i”
elseif b == -1 and a == 0 then
return “-i”
elseif b == 1 and a ~= 0 then
return a .. “ + i”
elseif b == -1 and a ~= 0 then
return a .. “ - i”
elseif b ~= 0 and a == 0 then
return b .. “i”
elseif b > 0 and a ~= 0 then
return a .. “ + “ .. b .. “i”
elseif b < 0 and a ~= 0 then
return a .. “ - “ .. (-b) .. “i”
else
error “unexpected (a + bi) combination: contact author”
end
end


Uma funçãozinha útil



Para definir alguns parâmetros importantes precisamos definir o que acontece com o número quando tentamos invertê-lo ((a + bi)-1).

A operação matemática é multiplicar a fração resultante por uma expressão equivalente a 1, como (a – bi) / (a – bi).

Fazendo esta continha simpática, obtemos real a / (a2 + b2) e imagem -b / (a2 + b2).

Vamos criar nossa função:

local function inv(v)
if getmetatable(v) ~= cmt then
return v ^ (-1)
else
local a, b, q = v.real, v.img
q = a ^ 2 + b ^ 2
return new(a / q, -b / q)
end
end


Nesta função, a primeira coisa que fizemos foi verificar se o argumento é um número complexo. Se não for, a função retorna um dividido pelo argumento. Se for um número complexo, realiza o cálculo citado.

Repare na linha:

local a, b, q = v.real, v.img


Neste comando, a, b e q são definidos como variáveis locais. a recebe v.real, b recebe v.img e q recebe nil.

Inversão de sinais



Se temos um número complexo b, queremos que -b retorne um número complexo com os sinais do real e da imagem invertidos:

function cmt:__unm()
return new(-self.real, -self.img)
end


Igualdade



Vamos verificar igualdade. Quando Lua verifica a == b, sendo a e b tabelas, na verdade está verificando se a e b são exatamente a mesma tabela, não se seus elementos são iguais. O quer queremos quando comparamos dois números complexos é se representam o mesmo valor, ou seja, se seus reais são iguais e se suas imagens também são.

Precisamos então criar um método para tratar isso:

function cmt:__eq(v)
if getmetatable(v) == cmt then
return self.real == v.real and self.img == v.img
else
return self.img == 0 and v == self.real
end
end


Operações binárias



Agora definiremos as operações binárias, ou seja, que necessitam de dois operandos: adição (+), subtração (-), multiplicação (*), divisão (/), potência (^) e concatenação (..).

Em todos os casos verificaremos se o segundo elemento da operação é também um número complexo ou não.

  • Adição

function cmt:__add(v)
local a1, b1, a2, b2, a3, b3 = self.real, self.img
if getmetatable(v) ~= cmt then
a2, b2 = v, 0
else
a2, b2 = v.real, v.img
end
a3, b3 = a1 + a2, b1 + b2
return new(a3, b3)
end


Na declaração das variáveis locais, a1 recebe self.real e b1 self.img. Todas as demais variáveis recebem nil.

  • Subtração (nada mais do que soma com o sinal invertido)

function cmt:__sub(v)
return self + (-v)
end


  • Multiplicação (muito semelhante à adição)

function cmt:__mut(v)
local a1, b1, a2, b2, a3, b3 = self.real, self.img
if getmetatable(v) ~= cmt then
a2, b2 = v, 0
else
a2, b2 = v.real, v.img
end
a3, b3 = a1 * a2 – b1 * 2, a1 * b2 + a2 * b1
return new(a3, b3)
end


  • Divisão (divisão é a multiplicação onde o segundo termo é invertido)

function cmt:__div(v)
return self * inv(v)
end


  • Potência (aqui trataremos apenas expoentes inteiros, que não passam de multiplicações sucessivas)

function cmt:__pow(v)
if v == 0 then
-- expoente 0 retorna 1
return 1
elseif v == 1 then
-- expoente 1 retorna uma cópia de si mesmo
return new(self)
elseif v < 0 then
-- expoente negativo retorna inversao
return inv(self ^ (-v))
else
local aux, cont = new(self)
for cont = 2, v do
aux = self * aux
end
return aux
end
end


Repare a recursividade na linha:

return inv(self ^ (-v))


A potência chama novamente __pow() para self, mas desta vez com argumento -v.

  • Concatenação (fácil: retorna a contenação das strings!)

function cmt:__concat(v)
return tostring(self) .. tostring(v)
end


Vamos tornar isso tudo útil?



Até agora está tudo muito bonito, tratando números complexos e tudo mais... mas números complexos só são úteis se pudermos fazer duas coisas: 1converter números reais em complexos quando tentamos extrair a raiz de um número negativo e 2converter números complexos para reais por meio das operações básicas.

Bem, a segunda coisa nosso módulo já faz, falta a primeira! Para tanto, vamos criar uma função de raiz quadrada segura, que retorne um número complexo quando a base for negativa:

local function sqrt(v)
if type(v) ~= “number” then
error “value must be a number”
end
if v >= 0 then
return v ^ .5
else
return (0, (-v) ^ .5)
end
end


Também será útil termos uma função que retorne verdadeiro ou falso para verificar se um valor é um número complexo:

local function iscomplex(v)
return getmetatable(v) == cmt
end


Finalizando



Até aqui, tudo o que fizemos foi privado (local). Vamos então criar uma tabela pública onde colocaremos somente as partes que nos interessam:

complex = {
iscomplex = iscomplex,
new = new,
sqrt = sqrt,
i = new()
}


Vamos agora testar! Salve este arquivo como complex.lua no diretório atual e execute o interpretador lua. Execute os seguintes comandos e veja se funciona:

lua> require “complex”
lua> for c = -4, 7 do print(c, complex.i ^ c) end
-4 1
-3 i
-2 -1
-1 -i
0 1
1 i
2 -1
3 -i
4 1
5 i
6 -1
7 -i
lua> a = complex.sqrt(-9) + 2
lua> b = complex.new(3, 2)
lua> print(a, b)
2 + 3i 3 + 2i
lua> print(a + b)
5 + 5i
lua> print(a * b)
13i
lua> print(a / 2)
1 + 1.5i
lua> print(b ^ 2)
5 + 12i


Se tudo sair direitinho, parabéns! Acabou de fazer seu primeiro módulo de números complexos.

[]'s

2006-08-10

Evolução

Evoltion_(movie) Recomendação de filme (velho =D) bom! Quem não viu ainda, tente ver, quem já viu, vale a pena a reprise.

[]'s

2006-08-02

Doctor Who

Doctor Who foi uma série de ficção científica da década de 1960 produzida pela BBC de Londres.

O programa original durou de 1963 a 1989 e atualmente está passando remake no canal People+Arts às 22:00 na sexta-feira.

Simplesmente é a melhor série de ficção científica desde Star Trek!

Trata das viagens do último dos «senhores do tempo» como historiador, mas também procura e corrige desvios no fluxo do tempo.

Cristopher Eccleston – Ele gosta muito de visitar a Terra, onde eventualmente escolhe um «mono imbecil» – «stupid ape», como ele costuma chamar os seres humanos quando comentem erros estúpidos – para acompanhá-lo.

Recomendo ao leitor que, se possível acompanhe a série.

[]'s

2006-07-31

Desempenho de algoritmos

Nautilus Depois de muita propaganda aqui está o artigo sobre desempenho de algoritmos. =)

Os programadores sabem que sempre há mais de um jeito de obter o mesmo resultado, mas cada um vai apresentar um desempenho diferente.

Uma das melhores sequências para a comparação de diferentes implementações é a sequência de Fibonacci. Primeiro por ser possível usar diversos algoritmos clássicos, como recursão e reiteração, segundo porque é uma sequência que existe naturalmente, o que a torna fascinante em si.

Caso alguém não conheça a sequência de Fibonacci, ela começa assim:

1 1 2 3 5 8 13 21 34 55 89...

Os matemáticos gostam de começar de zero (0 1 1 2 3...), o que não está errado e funciona da mesma forma – aliás fica mais fácil de calcular –, no entanto se considerarmos a essência natural da sequência em vez de seu comportamento matemático (Leonardo Pisano Fibonacci era naturalista e viajante antes de matemático, mais ou menos como Wallace e Darwin), veremos que não é possível que o zero participe da sequência.

Cada elemento é igual à soma dos dois elementos anteriores, e os dois primeiros valores são 1 e 1.

Recursividade


A primeira implementação da sequência de Fibonacci – e também a mais óbvia – é o algoritmo recursivo (em inglês recursion).

Recursividade, recursão ou recorrência é quando uma determinada função faz chamada a ela mesma (com parâmetros diferentes!). O exemplo clássico é o fatorial:

— Qual o fatorial de cinco (5!)?
— É cinco vezes o fatorial de quatro (5 x 4!).
— E qual o fatorial de quatro?
— É quatro vezes o fatorial de três (4 x 3!).

Para que a recursividade funcione, é preciso haver um valor base, que funcione como parada para a recursão. No caso do fatorial, o valor base é 0! = 1, portanto:

5! = 5 x 4! = 5 x 4 x 3! = 5 x 4 x 3 x 2! = 5 x 4 x 3 x 2 x 1! = 5 x 4 x 3 x 2 x 1 x 0!
5! = 5 x 4 x 3 x 2 x 1 x 1 = 120

Fibonacci como faz duas recursões simultâneas, precisa de dois valores bases, que são o primeiro e o segundo elementos, que valem um (1). A partir daí já podemos montar nosso algoritmo recursivo:

def fib(n):
if n < 2:
return 1
else:
return fib(n - 2) + fib(n - 1)


Então, fib(0) = fib(1) = 1. Os demais valores são calculados somando-se os dois anteriores.

Ao se executar este algoritmo, podemos ver que seu desempenho cai exponencialmente em relação ao índice do elemento que se quer calcular, ou seja, é péssimo. O gráfico pode ser encontrado aqui.

Então por que pensar em recursividade?

Há algumas boas razões para isso. Primeiro porque é a maneira mais simples de resolver problemas que podem ser resolvidos desta forma; segundo porque ao se aplicar outro método chamado memoization seu desempenho aumenta assustadoramente, na mesma proporção em que aumenta a quantidade de memória requerida para resolvê-lo.

Reiteração


Reiteração (em inglês iteration) é o próprio algoritmo recursivo que foi aberto de forma a não alocar mais memória e tempo de processamento do que o necessário.

O grande problema da recursividade é que cada vez que a função se chama, ela aloca mais espaço de memória para as mesmas variáveis já em uso. Na reiteração as mesmas variáveis e a mesma memória alocada são usadas para todos os cálculos, reduzindo o tempo de processamento.

A implementação reiterativa é a seguinte:

def fib(n):
a, b, c = 0, 1, 1
while c <= n:
a, b, c = b, a+b, c+1
return b


Ao analisarmos o gráfico, podemos de cara ver que este é bem mais eficiente que o recursivo devido às grandezas dos valores no eixo y (tempo). Outra coisa que podemos reparar é que seu desempenho cai aritmeticamente (o tempo cresce em linha reta), favorecendo seu uso mesmo para índices mais altos.

Observação: devido à palavra inglesa, é comum alguns programadores dizerem iteração em vez de reiteração, o que é um neologismo desnecessário.

Memoização


Realmente não há uma palavra (não que eu conheça) em português para isso, então adaptei a palavra inglesa memoization.

Memoização é uma técnica de programação usada quando há muita memória disponível (comum hoje em dia mesmo em micros pessoais) e é necessária a efiência. Consiste em fazer cache de valores conhecidos para não que não seja necessário calculá-los novamente.

Para algoritmos não-recursivos, não faz muita diferença, a menos que se use diversas vezes os mesmos parâmetros. No entanto para algoritmos recursivos o ganho é enorme, apresentando muitas vezes desempenho superior ao equivalente reiterativo.

Então – caso haja memória suficiente – a memoização traz duas vantagens: 1é possível usar o mais simples dos algoritmos (recursivo) e 2pode haver ganho de desempenho. Aliás, em se tratando de desempenho, o gráfico do algoritmo memoizado é quase linear com fator zero (no exemplo, oscilando por volta de 26µs).

Em Python usamos para memoização um técnica chamada «decoração», onde criamos um decorador que altera a função da forma desejada. Na verdade o decorador é uma metafunção ou classe que recebe como argumento a função original e devolve a função modificada.

O decorador para memoização em Python pode ser desenvolvido assim:

class memoize:

def __init__(self, f):
self.__cache, self.__func = {}, f
self.__doc__ = f.__doc__
for e in dir(f):
if not e.statswith('_'):
setattr(self, e, getattr(f, e))

def __call__(self, *args, **kw):
i = repr((args, kw))
try:
r = self.__cache[i]
except KeyError:
r = self.__func(*args, **kw)
self.__cache[i] = r
return r


Agora, basta reescrever a função recursiva com o decorador adequado:

@memoize
def fib(n):
if n < 2:
return 1
else:
return fib(n - 2) + fib(n - 1)


Matriz característica


A sequência de Fibonacci – assim como sequências semelhantes – pode também ser entendida como o resultado de uma determinada matriz elevada ao índice desejado. Basta então implementar suporte a cálculo matricial e calcular potência de forma binária.

Python possui um módulo chamado NumPy que dá suporte a matrizes, mas usa algoritmos muito lentos, portanto fiz minha própria implementação de matrizes, limitada a tratar matrizes 2x2 (que é o caso da matriz característica da sequência em questão).

A função ficou então assim:

matriz = Matriz_2x2(1, 1, 1, 0)
def fib(n):
return pow(matriz, n)[0]


Este algoritmo só não ganha em desempenho para a memoização e a função fechada. Seu desempenho cai logaritmamente, se monstrando muito eficiente para altos índices.

Função Fechada


A partir dos autovalores da matriz característica ou do cálculo discreto da recorrência obtemos a função fechada da sequência.

A matriz característica da sequência de Fibonacci apresenta dois autovalores: \lambda_1\frac{1+\sqrt{5}}{2} e \lambda_2=\frac{1-\sqrt{5}}{2}.

A função fechada é igual à subtração das potências dos autovalores pelo índice desejado dividida pela subtração dos autovalores: \frac{\lambda_1^n-\lambda_2^n}{\lambda_1-\lambda_2}.

O único problema deste algoritmo é que ele calcula Fibonacci a partir de zero (0 1 1 2 3 5...), e queremos a sequência natural (a partir de 1: 1 1 2 3 5 8...), então temos de incrementar o argumento antes de poder tratá-lo:

def fib(n):
n += 1 # incrementa o índice
sqrt5 = pow(5., .5) # raiz quadrada de cinco
autovalor = (1 + sqrt5) / 2, (1 - sqrt5) / 2
resultado = (
pow(autovalor[0], n) - pow(autovalor[1], n)
) / (
autovalor[0] - autovalor[1]
)
return int(resultado + .5) # arredonda o resultado


Como não há recursão, reiteração, cache e nem cálculo matricial, apenas álgebra linear, este algoritmo é altamente eficiente, sendo praticamente linear de fator zero.

No exemplo, tem um tempo de execução médio de 15,7µs até por volta do índice 46, depois sobe para 16,8µs até o índice 119, máximo do teste.

Ultimate Fibonacci


O nome é só uma brincadeira! Não é uma solução definitiva em absoluto!

Com consumo mínimo de memória, os algoritmos mais eficientes são o reiterativo e a função fechada.

Na verdade, como há uma queda aritmética de desempenho, o algoritmo reiterativo é mais eficiente que os demais apenas para índices baixos – nos exemplos até por volta do índice 21. Acima disso, o melhor algoritmo é a função fechada.

Obs.: a memoização foi descartada pelo alto consumo de memória.

Então, a idéia é usar o algoritmo reiterativo até onde ele ainda é mais eficiente e daí por diante usar a função fechada. O gráfico ficou assim.

Conclusão


O objetivo deste artigo foi apenas apresentar os diversos algoritmos clássicos para implementação de conjuntos discretos, sequências matemáticas.

A recursividade é a forma mais fácil de se implementar uma sequência, no entanto a menos eficiente – a menos que você tenha disponível muita memória, então pode usar a memoização para conseguir uma maior eficiência com a implementação mais simples.

A reiteração é a forma aberta da recursividade e o algoritmo que os programadores buscam, muitas vezes apenas porque desconhecem outras soluções.

A matriz característica é uma boa solução, se a potência de matrizes for implentada de forma binária (veja o método __pow__() da classe Matriz_2x2), caso contrário se torna muito ineficiente. De qualquer forma, a partir dos autovalores da matriz característica, é possível calcular a função fechada, que resolve a sequência por álgebra linear, método altamente eficiente e que não sofre muita variação ao longo da projeção cartesiana.

Esses algoritmos ainda podem ter desempenhos diferentes não apenas de máquina para máquina, mas também de um compilador/interpretador para outro.

Os gráficos de desempenho dos algoritmos se encontram em:



Códigos

[]'s