terça-feira, 22 de dezembro de 2009

Seaside

Squeak Seaside é um arcabouço (framework) para desenvolvimento de aplicações web para Smalltalk. É descrito pelo próprio autor como um arcabouço herético, por quebrar todas as regras dos gurudons atuais.

Pessoalmente gosto muito de coisas heréticas ou subversivas. Sou um apaixonado por subversão. =D

Olhando a documentação, podemos ver que Seaside é suportado pelas máquinas virtuais Cincom VisualWorks, GNU Smalltalk, VA Smalltalk, Squeak e seus derivados Pharo e GLASS.

GNU Smalltalk


A máquina virtual recomentada é Squeak, da qual gosto muito e que é ótima para aplicações desktop, no entanto Squeak é uma plataforma muito pesada, consome muitos recursos da máquina, por isso, para Seaside, optei por uma plataforma mais leve: GNU Smalltalk.

GNU Smalltalk sofre porém de um mal extremamente irritante: a sintaxe da linguagem é muito irregular – para ser eufémico.

Teoricamente GNU Smalltalk suportaria a sintaxe correta de Smalltalk-80, mas não funciona bem assim… ele é muito chato e levanta erros de sintaxe para códigos perfeitamente corretos, mas que diferem do que o GNU Smalltalk espera de Smalltalk-80. Então é preciso experimentar algumas formas diferentes – mas regulares – de fazer as coisas antes de encontrar uma que funcione (às vezes ele implica até com nomes de argumentos).

Vou usar aqui a sintaxe Smalltalk-80 precisa – e que funciona no GNU Smalltalk –, mas colocarei o mesmo código na sintaxe peculiar do GNU Smalltalk ao final deste artigo apenas por curiosidade.

Preparação da imagem


Para criar a imagem para nosso exemplo usei o comando:
bash$ gst-load -iI seaside.im Seaside Seaside-Development Seaside-Examples


Isso cria uma imagem seaside.im com os pacotes Seaside (Seaside em si), Seaside-Development (ferramentas de desenvolvimento) e Seaside-Examples (exemplos).

Usei então o exemplo de componente da própria documentação: MyCounter.st.
  • O método de classe #canBeRoot diz se o componente pode ser registrado como uma aplicação autónoma ou não.
  • Na inicialização (#initialize) o atributo count é zerado.
  • O método #states retorna uma coleção de estados que podem ser retrilhados (backtracked).
  • O método #renderContentOn: é usado para renderizar a página.
  • Ao final a mensagem #registerAsApplication: registra o componente como aplicação.


O código fica então (se copiar e colar, substituia por ^):
" See: http://www.gnu.org/software/smalltalk/manual/html_node/Seaside.html "

Seaside.WAComponent subclass: #MyCounter
instanceVariableNames: 'count'
classVariableNames: ''
poolDictionaries: ''
category: 'Seaside Examples'
!

!MyCounter class methodsFor: 'testing'!

canBeRoot
↑true
!

!

!MyCounter methodsFor: 'initializing'!

initialize
super initialize.
count := 0.
!

!

!MyCounter methodsFor: 'accessing'!

states
↑{ self }
!

!

!MyCounter methodsFor: 'rendering'!

renderContentOn: html
html heading: count.
html anchor
callback: [ count := count + 1 ];
with: '++'.
html space.
html anchor
callback: [ count := count - 1 ];
with: '--'.
!

!

MyCounter registerAsApplication: 'mycounter'.


Para carregar o componente na imagem usei o comando:
bash$ gst -I seaside.im -S MyCounter.st


Rodar a aplicação


O seguinte comando levanta a máquina virtual no modo servidor:
bash$ gst-remote -I seaside.im --daemon


Depois disso você poderá ver a máquina virtual ouvindo na porta 12345/TCP:
bash$ netstat -ln4 | grep 12345
tcp 0 0 0.0.0.0:12345 0.0.0.0:* LISTEN


É possível obter o PID do processo da máquina virtual com seguinte comando:
bash$ gst-remote --pid


Porém o Seaside não está rodando. Para rodar o serviço web use o comando:
bash$ gst-remote --start=Seaside


É possível também iniciar a máquina virtual já rodando o Seaside com o seguinte comando:
bash$ gst-remote -I seaside.im --daemon --start=Seaside


Por padrão o serviço web Seaside estará escutando na porta 8080/TCP.

Para acessar a aplicação que acabamos de criar use o endereço http://localhost:8080/seaside/mycounter.

Parar a aplicação


Para parar a aplicação use o comando:
bash$ gst-remote --stop=Seaside


Repare que, apesar do serviço web ter parado, a máquina virtual continua rodando. Para pará-la:
bash$ gst-remote --kill


Para comparação


A título de comparação, segue o código usando a sintaxe bizarra e esdrúxula peculiar do GNU Smalltalk:
Seaside.WAComponent subclass: MyCounter [
| count |

<category: 'Seaside Examples'>
<comment: nil>

MyCounter class >> canBeRoot [
<category: 'testing'>
^true
]


initialize [
<category: 'initializing'>
super initialize.
count := 0
]

states [
<category: 'accessing'>
^{ self }
]

renderContentOn: html [
<category: 'rendering'>
html heading: count.
(html anchor)
callback: [count := count + 1];
with: '++'.
html space.
(html anchor)
callback: [count := count - 1];
with: '--'
]
].

MyCounter registerAsApplication: 'mycounter'.


[]'s
Cacilhas, La Batalema

domingo, 6 de dezembro de 2009

Mais sobre reiteração

Glider A reiteração ou iteração é um dos mais efetivos algoritmos para para processamento de sequências, mas sua eficiência não se limita a processamento de conjuntos prontos, essa técnica de algoritmo também pode ser usada para processamento de amostragens em plena coleta.

Vamos a um algoritmo bem simples, a média aritmética:
mean_x=sum_x/n


O algoritmo reiterativo pode ser expresso da seguinte forma:
(defun mean ((a-list list))
(/
(apply #'+ a-list)
(length a-list)))


Agora imagine que você tem o registro contínuo do comportamento de um dado específico ao longo do tempo e gostaria de manter sua média sem preocupação como os valores em si (acho um caso pouco provável, mas…).

Se reparando bem, há dois dados importantes para o cálculo da média aritmética: a própria média e a quantidade de elementos. Basta que a função receba esses valores além dos novos valores que serão usados para atualizar a média anterior.

Assim é possível armazenar apenas a média atual e a quantidade de elementos avaliados:
(defun update-mean ((the-mean hash-table) (a-list list))
(let ((*count* (+ (gethash 'count the-mean) (length a-list))))
(setf (gethash 'mean the-mean)
(/
(+
(apply #'+ a-list)
(* (gethash 'mean the-mean) (gethash 'count the-mean)))
*count*))
(setf (gethash 'count the-mean) *count*)
the-mean))


Então, além de receber a lista com os dados mais recentes (segundo parâmetro), a função recebe como primeiro parâmetro um hash com os dados de média anteriores.

O formato inicial do hash deve ser:
#s(hash-table :test fasthash-eql (count . 0) (mean . 0))


Ou seja, 'mean zero (0) e 'count zero (0). O hash será atualizado e retornado:
(setq *mean*
(make-hash-table
:initial-contents (list
(cons 'count 0)
(cons 'mean 0))))


Na próxima falarei em variância on-line.

[]'s
Cacilhas, La Batalema

quarta-feira, 4 de novembro de 2009

Ideias poderosas por trás de uma piada

Baal Ricardo Bánffy fez uma piada de 1º de abril no ano passado lançando um suposto framework em Lisp chamado Kanamit.

No entanto a parte da piada é somente o terceiro tópico do texto e o Bánffy aproveitou o ar de piada para rasgar o verbo e dizer umas verdades sem medo.

Como concordo quase 100% com o texto, decidi reproduzi-lo aqui de forma séria:
Eu sempre gostei de escrever aqui sobre linguagens «exóticas». Já houve um tempo em que Python era exótico. Hoje Python é mainstream. Ruby, idem. Há até suporte a Rails no NetBeans 6 e para Python no Visual Studio. Não dá pra ser muito mais mainstream do que isso.

Nem todas as linguagens exóticas que eu conheci deram certo. Smalltalk, por exemplo, continua sendo aquela linguagem muito sofisticada, anos-luz à frente do Java, mas que quase ninguém usa. Smalltalk tem ideias poderosas demais. A ideia de rodar dentro de uma máquina virtual, de todo o código poder ser examinado e modificado «ao vivo», do compilador incremental, do class browser, do late-binding, das mensagens... Tudo isso era demais pras cabecinhas da maioria dos programadores que só conseguem entender Visual Basic ou PHP e olha lá.

Mas «Visual Basic e PHP resolvem todos os meus problemas», dizem eles. Claro. Se tudo o que eles conhecem se resume a isso, eles não imaginam sequer que existam outros tipos de problema. É uma limitação de capacidade expressiva: eles não têm as ferramentas intelectuais necessárias para expressar as classes de problemas que outras ferramentas expressam e resolvem.

Não se pode pensar em uma lâmpada fluorescente se tudo o que você conhece são pedras lascadas e peles de urso.

Uma outra linguagem muito além do seu tempo é o Lisp. Além do seu tempo porque, se ela é uma coisa poderosa hoje comparada ao que temos hoje, imaginem em 1960 quando ela foi inventada…

Por muito tempo, havia implementações de Lisp para computadores desktop, mas, com processadores de 16 bits e menos de um megabyte de RAM, essas máquinas serviam apenas como brinquedos. Você podia fazer um loop e imprimir 10 vezes «fulano é bobo», mas não muito mais do que isso.

Naquele tempo, para se rodar programas em Lisp, você precisaria de uma Lisp machine, um computador dedicado, com um processador único capaz de coisas que nenhum outro processador da época – ou de hoje – era capaz de fazer. Eram incrivelmente caras.

Mas as implementações de brinquedo eram o bastante para abrir as cabeças dos jovens computólogos (antes deles se chamarem assim). Lisp e Scheme (um dialeto de Lisp) são usados como ferramenta didática em todos os bons cursos de ciência da computação. Se no seu curso não tem, pare de perder tempo e peça seu dinheiro de volta. Não sei quanto é a mensalidade, mas é certo que o curso vale menos.

E, claro, hoje em dia, temos computadores amplamente capazes de rodar boas implementações de Lisp. Meu notebook consegue emular uma Lisp machine da Texas Instruments (uma Micro-Explorer) mais depressa do que ela era originalmente.


Artigo original: O Kanamit Web Framework.

[]'s
Cacilhas, La Batalema

quarta-feira, 21 de outubro de 2009

Mais sobre programação funcional

Paradigma funcional

Aproveitando o ensejo do artigo anterior sobre programação funcional, gostaria de puxar mais este artigo sobre três algoritmos tradicionais: sequência de Fibonacci, fatorial e quick sort.


Fatorial


Fatorial é um dos algoritmos recursivos mais tradicionais da Matemática. É também um dos algoritmos mais simples:
0!=1 n!=n*(n-1)!


Sua implementação tanto em Haskell quanto em Common Lisp descreve exatamente esse algoritmo.

Em Haskell:
factorial :: Integer -> Integer
factorial 0 = 1
factorial n = n * (factorial (n - 1))


Em Common Lisp:
(defun factorial ((n integer))
(if (zerop n)
n
(* n (factorial (- n 1)))))


Já em Scheme a coisa pode complicar… se o interpretador (uso guile) tiver sido compilado com suporte a bignum, tudo bem, se não, é preciso fazer uma pequena mágica, multiplicando os resultados do maior para o menor:
(define (factorial n)
(define (loop k l)
(if (zero? k)
l
(loop (- k 1) (* k l))))
(loop n 1))


Sequência de Fibonacci


A Sequência de Fibonacci é uma progressão natural iniciando por 1, 1 e progredindo ao passo de que cada elemento é a soma dos dois anteriores:
a0=1 a1=1 an=a(n-2)+a(n-1)


É uma sequência recursiva em árvore binária, o pesadelo dos programadores. =D

Mas como já foi muito estudada, há diversas formas de implementá-la sem usar código recursivo, como algoritmo reiterativo, potência de matrizes e função fechada.

A seguinte implementação em Haskell utiliza recursão e lista infinita:
fib :: [Integer]
fib = 1 : 1 : zipWith (+) fib (tail fib)


Já em Common Lisp foi uma boa ideia usar reiteração, muito mais elegante do que seria possível em qualquer linguagem imperativa:
(defun fib (index)
(check-type index (integer 0 *))
(loop
for a = 1 then b
and b = 1 then (+ a b)
repeat index
finally (return a)))


Quick sort


O algoritmo de ordenação quick sort é a exceção das exceções: um algoritmo de ordenação recursivo em árvore binária, o que deveria ser o pior dos casos, no entanto é um dos métodos de ordenação mais rápidos e eficientes que conhecemos.

O princípio é o seguinte: toma-se um elemento qualquer da lista, tradicionalmente o primeiro, então separa-se a lista em duas, uma de elementos menores que o tomado e outra de elementos maiores. Aplica-se a mesma ordenação quick sort a cada uma das novas listas e concatena-as com o elemento tomado no meio.

Em Haskell isso pode ser descrito assim:
qsort :: Ord a => [a] -> [a]
qsort [] = []
qsort (x:xs) = qsort lesser ++ [x] ++ qsort greater
where
lesser = [e | e <- xs, e < x]
greater = [e | e <- xs, e >= x]


Em Common Lisp o princípio é o mesmo, mas com aquele monte de parêntesis característicos de Lisp:
(defun qsort ((a-list list))
(if (< (length a-list) 2)
a-list
(let ((x (pop a-list)))
(concatenate 'list
(qsort
(loop for e in a-list
if (< e x)
collect e))
(list x)
(qsort
(loop for e in a-list
if (>= e x)
collect e))))))



**

Espero que este artigo tenha sido interessante e cause curiosidade aos leitores sobre programação funcional.

[]'s
Cacilhas, La Batalema

sexta-feira, 16 de outubro de 2009

Oddwording de novo

Poliedro Em maio de 2007 publiquei um artigo sobre a implementação do algoritmo oddwording em diversas linguagens de programação.

Para relembrar, segue o código em Haskell:

module Main where

import IO


main = mainloop


mainloop :: IO ()
mainloop = do
ph <- readString
if ph == ""
then
return ()
else do
let nph = oddword ph
putStrLn nph
mainloop


readString :: IO String
readString = do
putStrLn "Digite uma frase: "
putStr "> "
hFlush stdout
s <- getLine
return s


oddword :: String -> String
oddword s = listToStr $ invodd $ strToList s


strToList :: String -> [String]
strToList "" = []
strToList s = (take spi s) : (strToList (drop (incr spi) s))
where spi = nextspace s


incr :: Int -> Int
incr = (+ 1)


nextspace :: String -> Int
nextspace "" = 0
nextspace (x:xs) =
if x == ' '
then
0
else
1 + (nextspace xs)


listToStr :: [String] -> String
listToStr [] = ""
listToStr (x:xs) = x ++ " " ++ (listToStr xs)


invodd :: Ord a => [[a]] -> [[a]]
invodd [] = []
invodd (x:xs) = x : (inveven xs)


inveven :: Ord a => [[a]] -> [[a]]
inveven [] = []
inveven (x:xs) = (inv x) : (invodd xs)


inv :: Ord a => [a] -> [a]
inv [] = []
inv (x:xs) = (inv xs) ++ [x]

<update ressaca>
Havia esquecido de forçar o flush de STOUT na função readString. Corrigido!
</update>

No entanto ficou faltando uma linguagem excepcionalmente importante, Lisp.

Lisp (LISt Processing) é uma família de linguagens funcionais concebida por John McCarthy em 1958, baseada no cálculo lambda. As principais linguagens da família Lisp são Common Lisp, Scheme e Emacs Lisp.

O código acima será reimplementado abaixo em Common Lisp.

Observação: os blocos de código deverão ser escritos em arquivo na ordem inversa em que aparecem aqui, ou seja, digite cada bloco no arquivo em linhas acima do bloco digitado anteriormente.

Ciclo principal


O ciclo principal será a função mainloop. Sua lógica de funcionamento é a seguinte:
  1. Pegar uma string digitada na entrada padrão;
  2. Se a string for vazia, sair do programa;
  3. Aplicar o algoritmo oddwording;
  4. Exibir na tela o resultado;
  5. Recomeçar o ciclo.


A função fica assim:
(defun mainloop ()
(loop do
(format t "~&~a~%"
(oddword (*exit-if-empty* (*read-string*))))))


Lendo uma linha da entrada padrão


Para ler uma linha da entrada padrão, devemos exibir um prompt, ler a entrada digitada pelo usuário e remover os espaços em branco do começo e do fim:
(defun *read-string* ()
(format t "~&Digite uma frase:~%> ")
(string-trim " " (read-line)))


Saindo caso a string esteja vazia


Caso nada tenha sido digitado, o programa encerra. Para isso é preciso avaliar a string, encerrando o processo se apropriado, caso contrário retornar a string:
(defun *exit-if-empty* ((a-string string))
(if (string= a-string "")
(exit)
a-string))


Descrição do algoritmo em alto nível


Podemos agora definir em alto nível o algoritmo oddwording:
  1. Transformar a string em uma lista de palavras;
  2. Inverter somente o conteúdo dos elementos de índice ímpar da lista;
  3. Transformar novamente a lista de palavras em uma string.

(defun oddword ((a-string string))
(list-to-str (invodd (str-to-list a-string))))


String para lista


Para transformar uma string em uma lista, basta quebrada em cada ocorrência de espaço (#\Space):
(defun str-to-list ((a-string string))
(loop
for i = 0 then (1+ j)
as j = (position #\Space a-string :start i)
collect (subseq a-string i j)
while j))


Lista para string


Para transformar uma lista de palavras em uma string única, é preciso concatenar todos os elementos da lista, sem esquecer de acrescentar espaços entre eles:
(defun list-to-str ((a-list list))
(string-trim " "
(apply #'concatenate 'string
(loop for e in a-list
collect (concatenate 'string e " ")))))


Inversão dos elementos ímpares


Só faltou inverter os elementos de índice ímpar da lista. Nada mais simples: pegar cada elemento, se o índice for ímpar, inverter:
(defun invodd ((a-list list))
(loop
for x in a-list
and i from 0
collect (if (oddp i)
(reverse x)
x)))


Fazendo funcionar


Para que tudo aconteça, no final do arquivo acrescente uma chamada à função principal:
(mainloop)


E execute o arquivo com o comando clisp (aconselho o uso dos parâmetros -K full e -q).


**

Lisp tem uma abordagem extremamente elegante e clara, aproveita o paradigma funcional perfeitamente e seus programas podem ser retroalimentados recursivamente para obter-se um comportamento de aprendizado, apreciado no desenvolvimento de IA.

[]'s
Cacilhas, La Batalema

<update 2009-10-20>
Adicionada tipagem aos argumentos das funções e removido código redundante.
</update>

terça-feira, 6 de outubro de 2009

IDEs em ambiente GNU/Linux

Poliedro Semana passada escrevi um artigo sobre alguns ambientes de desenvolvimento integrado e editores de texto, alguns deles RAD. No entanto o artigo ficou muito longo, daí tive a ideia de quebrá-lo em três partes.

Publiquei em meu blog Reflexões de Monte Gasppa e Giulia C., mas agora, pensando bem, acho que cabe pelo menos uma referência aqui no Kodumaro.

As três partes do artigo são:
  1. Editores de texto
  2. IDEs focadas em projeto
  3. RAD


A quem se interessar, boa leitura!

[]'s
Cacilhas, La Batalema

sexta-feira, 21 de agosto de 2009

Dev in Rio 2009

Gente,

Notícia fresquinha no blog do Henrique Bastos:

Dev in Rio 2009: EU VOU!


Finalmente o Rio de Janeiro ganhou um grande evento de tecnologia focado no que é mais importante: Pessoas!

É com muito orgulho que apresentamos o Dev in Rio 2009, uma conferência bombástica sobre desenvolvimento de software que acontecerá no próximo dia 14 de setembro no Centro de Convenções SulAmérica, no Rio de Janeiro!

O Dev in Rio 2009, contará com palestrantes nacionais e internacionais que falarão sobre Java, Ruby e Ruby on Rails, Python, Django, Open Source, Joomla!, Métodos Ágeis, e muito mais. Nomes como Fábio Akita, Vinícius Manhães Teles, Jacob Kaplan-Moss, Guilherme Silveira, Nico Steppat, Ryan Ozimek e Jeff Patton agitarão um dia inteiro de muita tecnologia e diversão. Veja a programação detalhada no site da Dev in Rio 2009.

Mas não é só isso. Como eu disse no início, este é um evento focado em pessoas. Toda a organização do evento está voltada para promover ao máximo a interação e integração entre os presentes.

Por isso, o Dev in Rio 2009 contará com uma Arena DojoRio onde participantes e palestrantes poderão programar lado à lado, experimentando diversas linguagens e técnicas, buscando juntos as melhores formas de desenvolver software.

O evento está sendo organizado por mim (Henrique Bastos) em parceria com o meu amigo Guilherme Chapiewski, contando com todo o apoio dos membros da PythOnRio, DojoRio e #Horaextra.

A realização está sendo coordenada pelas nossas experientes amigas da Arteccom. E tê-las ao nosso lado já garante que este será um evento para marcar o circuito carioca.

Você não pode perder esta incrível oportunidade de participar desse mega evento, interagir com grandes nomes do cenário mundial de tecnologia, conhecer pessoas interessantes que compartilham a paixão pelo desenvolvimento de software, e ainda por cima começar sua semana só na terça-feira. Inscreva-se já!

Nos vemos por lá!

[]’s!


[]'s
Cacilhas, La Batalema

quarta-feira, 1 de julho de 2009

Ponteiro opaco

GCC Muitas vezes programadores de C++ e Java confundem encapsulamento com ocultação, o que não é a intenção da orientação a objetos.

No entanto, o contrário da ocultação, a exposição, também não é desejável para o encapsulamento, pois causa uma confusão entre interface e implementação.

Em C++, a declaração de uma classe força a exposição de sua estrutura privada. Um exemplo bem simples:
class X {
public:
X(int);
~X();

int get(void) const;

private:
int value;
}


Neste exemplo insanamente simples, é possível ver a estrutura privada da classe X, o que no caso não é um problema muito sério, mas em casos ligeiramente mais complexos, é possível corromper o encapsulamento.

Uma forma de resolver o problema é usando um design pattern¹ chamado ponteiro opaco ou Pimpl.

A ideia é que a classe de interface seja apenas uma moldura (wrapper) para a classe de implementação. A classe de implementação é declarada no corpo da classe de interface, mas nada é exposto ali.

A classe de implementação vai ser finalmente implementada no arquivo onde os métodos da classe de interface são implementados. Os métodos da classe de interface então simplemente chamam os métodos da classe de implementação.

Voltemos ao exemplo… o arquivo de cabeçalho de nossa classe (x.h por exemplo) passa a ser:
class X {
class Ximpl;

public:
X(int);
~X();

int get(void) const;

private:
Ximpl *pimpl;
}


Como é possível ver, nada da implementação está exposto.

Então a classe de implementação é finalmente definida no arquivo de implementação (x.cc):
class X::Ximpl {
public:
Ximpl(int);
~Ximpl();

int get(void) const;

private:
int value;

}


O resto do código fica:
X::X(int value): pimpl(new X::Ximpl(value)) {
// Nada mais a fazer
}

X::Ximpl::Ximpl(int value): value(value) {
// Nada mais a fazer
}

X::~X() {
delete this->pimpl;
}

X::Ximpl::~Ximpl() {
// Nada mais a fazer
}

X::get(void) const {
return this->pimpl->get();
}

X::Ximpl::get(void) const {
return this->value;
}


É claro que é um pattern extremamente desconfortável, pois é preciso implementar cada método duas vezes, uma para a classe de interface, outra para a classe de implementação… e há sim formas melhores de fazer isso.

Mas Pimpl funciona, é uma saída viável e pode salvar sua vida. =D

[update 2009-08-01]Sugestão de leitura de 0xc0de: Compilation Firewalls.[/update]

[]'s
Cacilhas, La Batalema


¹ Design patterns: antes que me crucifiquem por usar esta expressão fora do mundinho pequeno de Java, a definição de design pattern é «um jeito formal de documentar a solução para um problema de desenvolvimento», o que define corretamente ponteiro opaco.

domingo, 28 de junho de 2009

Variaridade

GCC Uma função variádica variária – em inglês variadic, variable arity, aridade¹ variável – é aquela que suporta uma quantidade variável de parâmetros.

Muitas linguagens suportam funções variárias, aliás de forma bem simples. Python usa o operador * para indicar quantidade variável de parâmetros, Lua usa o operador ... e Common Lisp o operador &rest.

Outras linguagens podem ser ainda mais simples, como por exemplo Perl, onde toda função é variária e os parâmetros são recebidos na lista @_, tradicionalmente capturados por shift.

Java não suporta funções verdadeiramente variárias devido a sua limitação forçada de tipagem, no entanto é possível simular com o uso de Object e casting («vazamento» na falta de uma tradução melhor):
void myFunction(Object... args) {
// Código do método

}


Funções variárias em C/C++


C/C++ usa o cabeçalho stdarg.h para suporte a funções variárias.

O exemplo apresentado na Wikipédia é bastante simples: uma função printargs que recebe uma quantidade arbitrária de números inteiros, encerrando com -1 (ou qualquer número negativo em nosso exemplo), e os exibe na saída padrão.

Precisamos incluir dois cabeçalhos em C++, cstdarg para suporte a funções variária e cstdio para exibir o resultado:
#include <cstdio>
#include <cstdarg>


Ou, em C:
#include <stdio.h>
#include <stdarg.h>


A assinatura da função fica assim:
void printargs(int arg1, ...) {


Para acesso aos parâmetros múltiplos é usado um objeto va_list:
    va_list args;


A função va_start() inicializa o objeto. Ela recebe dois parâmetros: o objeto va_list e o nome do último argumento antes da lista:
    va_start(args, arg1);


Portanto é preciso haver pelo menos um argumento antes da lista variável. Podemos então exibir o primeiro parâmetro, recebido no argumento arg1:
    printf("%d", arg1);


A função va_arg() retorna o argumento seguinte. Ela recebe como parâmetros o objeto va_list e o tipo do parâmetro da lista a ser recuperado.

O tipo precisar ser plenamente promovido, ou seja, ponteiro, inteiro, ponto flutuante ou precisão dupla. Outros tipos, como char precisam sofrer casting, como por exemplo (não faz parte do código de printargs):
char c = static_cast<char>(va_arg(va, int));


Continuando o código, podemos agora reiterar sobre os resultados de va_arg() até encontrarmos o valor de parada negativo:
    int arg;
while((arg = va_arg(args, int)) >= 0)
printf(" %d", arg);


Após o fim da reiteração dos parâmetros obtidos do objeto va_list através de va_arg(), é preciso encerrar o objeto:
    va_end(arg);
printf("\n");
}


Caso você queira restringir o formato de entrada da função, isso é possível usando __attribute__ em sua declaração. Por exemplo, se a função log() recebe o nível de log como primeiro parâmetro, uma string de formatação como segundo parâmetro (2) e os parâmetros variáveis a partir do terceiro (3), com formato similar ao da função printf(), isso é feito com a seguinte assinatura:
extern void
log(int level, cons char *fmt, ...)
__attribute__((format(printf, 2, 3)));


As opções para format() são printf, scanf, strftime e strfmon. Veja Declaring Attributes of Functions da documentação do GCC.

[]'s
Cacilhas, La Batalema


¹Aridade: em Matemática é o número de operandos de uma função.

quinta-feira, 14 de maio de 2009

Um «Olá Mundo!» pequenininho

tcl.jpg Como este blog estava muito parado, resolvi postar um «Olá Mundo!» com janela de duas linhas em Tcl/Tk:
wm title . Hello
grid [ button .bt -text { Olá Mundo! } -command exit ]


Se preferir, pode ser criado um script autoexecutável acrescentando o hash-bang:
#!/bin/sh
#\
exec wish -f "$0" ${1+"$@"}

wm title . Hello
grid [ button .bt -text { Olá Mundo! } -command exit ]


[]'s
Cacilhas, La Batalema

quinta-feira, 19 de março de 2009

Sobre APIs

Umas das coisas mais importantes em programação, sem a qual ninguém consegue desevolver, é API.

API – Interface de Programação de Aplicativos – é o conjunto de recursos de que o programador dispõe para desenvolver suas aplicações.

Este artigo faz uma comparação – na verdade, mais uma demonstração – de cinco APIs muito usadas: IEEE 1003, STL, OpenStep, GLib e Java.

Para a demonstração será usado um trecho de código para ler a primeira linha de um arquivo.

Por que não a API do Windows?


Antes de começar, é preciso esclarecer por que não a API do Windows.

O objetivo de uma API é oferecer um ambiente homogêneo de programação, para que o programador não seja obrigado a reaprender a mesma coisa diversas vezes. Para facilitar isso, muitas APIs ainda podem ser usadas em diversas linguagens diferentes, outras em apenas uma.

No entanto uma API que funciona apenas em um único sistema operacional – ou ainda no grupo de sistemas operacionais de uma única empresa – não está fazendo seu trabalho, por mais usado que esse(s) sistema(s) possa ser.

Assim a API do Windows não faz mais do que se espera da interface de um sistema operacional.

É claro que é necessário que o Windows possua uma API, mesmo que seja exclusivamente para conexão de outras APIs mais universais.

IEEE 1003 (POSIX)


O padrão POSIX, definido pela IEEE, oferece uma interface completa para sistemas compatíveis com UNIX. Alguns exemplos são IBM AIX, Solaris, GNU (e daí também Linux), BSD, Mac OS X, MS Xenix e muitos outros.

O trecho de código em C para ler a primeira linha de um arquivo:
FILE *fd = fopen(filename, "r");

if (fd) {
while (!eof(fd)) {
char buf;
fread(&buf, sizeof(char), 1, fd);
printf("%c", buf);
if (buf == '\n')
break;
}

fclose(fd);
}


Observação1: é preciso incluir o cabeçalho stdio.h ou, em C++, cstdio:
#include <stdio.h>


Observação2: a variável filename é do tipo const char *.

Observação3: foi preciso procurar ocorrência do carácter \n pois POSIX não ofereça uma função de alto nível para ler uma linha.

Observação4: o padrão POSIX é completamente procedimental – as demais APIs citadas aqui são orientadas a objetos –, sendo um pouco desajeitado para algumas operações.

STL


A linguagem de programação C++ possui uma biblioteca padrão própria bastante completa chamada STL – Standard Library.

Para o exemplo, é preciso incluir o cabeçalho fstream para a manipulação de arquivo e o cabeçalho iostream para a exibição dos dados na tela:
std::ifstream fd(filename);

if (fd.is_open()) {
char buf[1024];
fd.getline(buf, 1023);

std::cout << buf << std::endl;
fd.close();
}


Observação1: como citado acima, é preciso incluir pelo menos o cabeçalho fstream ao manipular arquivos:
#include <fstream>


Observação2: a variável filename precisa ser do tipo const char *std::ifstream não suporta std::string.

OpenStep


A empresa NeXT de Steve Jobs, ora adquirida pela Apple, abriu a API de seu sistema operacional em 1992 num acordo com a SUN Microsystems, sob o nome de OpenStep, o que permitiu, além da criação de aplicações de terceiros para NeXTSTEP, também o desenvolvimento de outros sistemas operacionais compatíveis com sua API e APIs de programação para sistemas já existentes, tornando-os compatíveis com OpenStep.

Exemplos de sistemas compatíveis com essa API são Mac OS X e GNUstep (portável para Windows e qualquer plataforma compatível com POSIX).

A API OpenStep funciona exclusivamente em Objective-C, apesar de haver alguns portes para outras linguagens, como C++, Smalltalk e Guile Scheme.

Vamos ao exemplo em Objective-C:
id fd = [NSFileHandle fileHandleForReadingAtPath: filename];

if (fd != nil) {
char buf = 0;
[fd seekToEndOfFile];
unsigned long long eof = [fd offsetFile];
[fd seekToFileOffset: 0];

while (([fd offsetInFile] < eof) && (buf != '\n')) {
[[fd readDataOfLength: 1] getBytes: &buf length: 1];
printf("%c", buf);
}

[fd closeFile];
}


Observação1: é preciso importar o cabeçalho Foundation.h:
#import <Foundation/Foundation.h>


Observação2: a variável filename precisa ser do tipo NSString.

Observação3: eu não conheço um método para ler uma linha de um arquivo em OpenStep, portanto usei um esquema similar ao usado acima em POSIX.

GLib


A GLib começou como parte do toolkit gráfico Gtk+ na versão 2.0, tendo evoluído para se tornar uma API multiplataforma orientada a objetos completa e independente da interface gráfica.

Você pode usar GLib em qualquer sistema POSIX e em Windows.

A maior curiosidade da GLib é que ela deixa explícita o equívoco – ou a falácia – de quem crê que não seja possível programar orientado a objetos em uma linguagem não orientada a objetos: é escrita em C e completamente orientada a objetos.

Aliás a base da GLib é o GObject que, curiosamente, não é compatível com a estrutura de orientação a objetos de C++ e outras linguagens clássicas, apesar de haver portes para C++ e outras.

O exemplo a seguir é um trecho de código em Vala, linguagem criada expecificamente para a GLib:
var file = File.new_for_path(filename);

if (file.query_exists(null)) {
try {
var fd = new DataInputStream(file.read(null));
stdout.printf("%s\n", fd.read_line(null, null));
fd.close(null);
} catch (Error e) {
// Ignora exceções
}
}


[update 2009-03-19]Faltou o close.[/update]

Observação1: é preciso «usar» GLib:
using GLib;


Observação2: a variável filename é do tipo string – equivalente em C a GString e gchar *.

Observação3: é preciso usar o pacote (--pkg) gio-2.0, que importa GIO, recursos de I/O da GLib.

Java


Em sua linguagem de programação, a SUN Microsystems optou por criar uma API própria (bem complicada e verborrágica em relação às demais, diga-se).
BufferedReader fd = null;
try {
fd = new BufferedReader(new FileReader(filename));
System.out.println(fd.readLine());

} catch (Exception e) {
// Ignora exceções

} finally {
try {
if (fd != null)
fd.close();
} catch (IOException e) {
// Ignora exceções
}
}


Observação1: as classes usadas estão no pacote java.io:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;


Observação2: a variável filename é do tipo String.


**

Fica aqui a dica de três APIs interessantes.

[]'s
Cacilhas, La Batalema

quarta-feira, 11 de março de 2009

Quem vai pra Vala?

live.gnome.org Dessa vez o pessoal do Gnome acertou a mão. Estou falando da linguagem de programação Vala.

Até hoje, quem queria programar em C# era obrigado a usar ou um sistema operacional problemático, ou uma ferramenta monstruosamente pesada e ineficiente.

Não mais.

O projeto Gnome lançou há algum tempo a linguagem Vala, fortemente baseada na sintaxe do C#, mas voltada para aplicações Gnome. Quem manja de C# ou maja de Java e já viu C# não terá dificuldade em lidar com Vala.

Instalação


Segundo a página do projeto, Vala foi feito para plataforma Gnome. Surgiu aí meu primeiro problema: uso Slackware e o Volkerding decidiu que Slackware não terá mais suporte a Gnome! Em sua total substituição, há o Xfce.

Simplesmente não há um pacote oficial de Gnome para Slackware – e os não-oficiais que já experimentei simplesmente detonaram o sistema todo.

Ainda mais: e quem usa outros SOs? Se não tiver Gnome já era?

Qual não foi minha agradável surpresa quando descobri que Vala compilou e instalou sem problemas no Slackware 12.2?! Sua dependência verdadeira é GTK+ 2.x, que você encontra em praticamente qualquer distribuição GNU/Linux ou BSD.

E Windows? Também tem!

Usando


Um «Olá Mundo» em Vala é bastante simples (tanto quanto Java, pelo menos):
using GLib;

public class HelloWorld : Object {
public static int main(string[] args) {
stdout.printf("Olá Mundo!\n");
return 0;
}
}


Ao contrário do .NET, não há código escondido do programador. Por exemplo:
using GLib;

public class HelloWorld : Object {

private Gtk.Window window;


public HelloWorld() {
window = new Gtk.Window(Gtk.WindowType.TOPLEVEL);
window.set_default_size(300, 200);
window.destroy += Gtk.main_quit;
window.add(new Gtk.Label("Olá Mundo!"));
}


public void show() {
window.show_all();
Gtk.main();
}


public static int main (string[] args) {
Gtk.init(ref args);
var sample = new HelloWorld();
sample.show();

return 0;
}
}


Antes que alguém reclame, peço desculpas pelos códigos mal orientados a objetos, mas não quis ter muito o que explicar.
[update]Ajeitei pra ninguém ficar me chateando.[/update]

Maturidade


Estranhamente a linguagem já está bastante madura na 0.5.7. Não encontrei quebras inusitadas nem erros bizarros.

[update 2009-04-01]
Encontrei uma quebra inusitada: Vala não lida bem com closures e escopo dinâmico. Ela se perde um pouco gerando alguns erros inesperados.

Outro erro inconveniente está na API com C: o bind de método destruidor não suporta parâmetros, o que pode ocorrer em alguns casos da GLib – lembre-se do que não há métodos destruidores reais em C, mas sim funções de desalocação.
[/update]


A única coisa que tenho a reclamar muito é a documentação precária, bem longe das documentações de API bem organizadas da SUN Microsystems e da Microsoft.

IDE


Por enquanto há uma IDE chamada Val(a)IDE – só que essa já desarmou na minha cara algumas vezes – e a promessa de uma extensão para o Eclipse – que ainda não apareceu na página da própria extensão.

Segundo a página official, há também extensões para MonoDevelop (Deus me livre!) e para GEdit.

**

É isso aí! Quando eu ganhar um pouco mais de intimidade com a linguagem, escrevo mais alguma coisa. Por enquanto foi só divulgação. Um tutorial meia-boca pode ser encontrado aqui.

[]'s
Cacilhas, La Batalema

quarta-feira, 4 de março de 2009

Calculadora científica com o Vim

Desta vez vou reblogar

O voyeg3r publicou um artigo curtinho e muito legal sobre como criar uma calculadora científica no Vim usando Python.

O artigo pode ser encontrado no vivaotux.

Boa leitura!

[]'s
Cacilhas, La Batalema

quinta-feira, 29 de janeiro de 2009

Propriedades e acessores

Em Java e em C++ há um bom motivo para que os atributos sejam sempre privados: preservar a interface das classes.

Por exemplo, imagine que temos uma classe que representa o registro de uma pessoa. Nela temos a idade. Sendo um número inteiro, poderia ser um atributo público:
Person person;
person.age = 23;
std::cout << person.age << std::endl;


Faz sentido. Agora imagine que mais tarde descobrimos que é preciso fazer um tratamento do valor recebido – para estar numa faixa aceitável, por exemplo…

Sem alterar a interface – e efetuar uma refatoração total de todo código onde ocorre instância – é impossível.

Então, em Java e C++ é importante usar métodos acessores e manter os atributos privados. Dessa forma qualquer alteração futura pode ser feita na classe sem alterar o restante do código:
Person person;
person.setAge(23);
std::cout << person.getAge() << std::endl;

Propriedades

Já em Python isso não faz sentido!

Em Python, além de atributos e métodos, há as propriedades. Então atributos podem ser públicos e, caso seja necessário adicionar alguma lógica na leitura ou gravação do atributo, basta substituí-lo por uma propriedade, sem prejudicar a interface.
class Person(object):

    def __init__(self):
        self.name = ""
        self.age = 0


Substituindo por propriedades:
class Person(object):

    def __init__(self):
        self.name = ""
        self.__age = 0

    def _get_age(self):
        return self.__age

    def _set_age(self, age):
        if 0 <= age < 150:
            self.__age = age

    age = property(_get_age, _set_age)


E a interface permanece imutável!

Aviso: não use propriedade quando não for necessário! Use atributo público. Um pouco de desempenho não faz mal a ninguém, mesmo em scripts.
Aviso²: testei esse código no prompt e não funciona, mas funciona em script. Se alguém puder descobrir o que está acontecendo, agradeço.

[update 2009-08-26]Usando IPython funcionou corretamente, portanto não sei o que causou o erro. =P

Assim assim, valeu Thiago Coutinho por tê-lo identificado (acho que ainda ocorre durante a interpretação em linha do CPython tradicional).[/update]

Usando metaclasse

Uma forma de implementar automaticamente autopropriedades foi sugerida por Guido van Rossum em Unificando Tipos e Classes (original).

Usaremos uma metaclasse:
class autoprop(type):

    def __init__(cls, name, base, dict):
        super(autoprop, cls).__init__(name, base, dict)
        props = {}
        for method in dict:
            if method.startswith("_get_") \
                or method.startswith("_set_") \
                or method.startswith("_del_"):
                props[method[5:]] = True
        for prop in props:
            fget = getattr(cls, "_get_" + prop, None)
            fset = getattr(cls, "_set_" + prop, None)
            fdel = getattr(cls, "_del_" + prop, None)
            setattr(cls, prop, property(fget, fset, fdel))

A ideia por trás dessa metaclasse é identificar métodos iniciados por _get_, _set_ ou _del_ e criar propriedades a partir daí. O método iniciado por _get_ representa a leitura, _set_ representa a escrita e _del_ representa a remoção.

Nossa classe então ficaria assim:
class Person(object):
    __metaclass__ = autoprop

    def __init__(self):
        self.name = ""
        self.__age = 0

    def _get_age(self):
        return self.__age

    def _set_age(self, age):
        if 0 <= age < 150:
            self.__age = age

Usando decoradores

Mas realmente, ao contrário da metaclasse para singleton, usar metaclasse para autopropriedade é como usar uma escopeta para espantar uma mosca.

Vamos usar decoradores!

O decorador @property pode ser usado para criar propriedades. Funciona muito bem e deixa o código bastante limpo:
class Person(object):

    def __init__(self):
        self.name = ""
        self.__age = 0

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, age):
        if 0 <= age < 150:
            self.__age = age

A função deve ter o nome da propriedade. O método de escrita deve chamar-se fset, o de leitura fget e o de remoção fdel. A parte mais feia desse código é o retorno. =)

**

Fica aqui a dica!

[]'s
Cacilhas, La Batalema

quarta-feira, 28 de janeiro de 2009

Interface fluente

Outro pattern interessante é conhecido como carrying return (não confundir com carriage return, carácter com código ASCII 13).
[update 2009-01-29]Esse era o nome dado há uns anos atrás, quando conheci esse pattern. Porém ele não ganhou espaço entre os programadores na época e poucos continuaram usando (como eu). =P

Hoje em dia ele voltou com o nome Fluent Interface, Interface Fluente.

A propósito, troquei o título deste artigo para usar uma linguagem mais nova e mais fácil de ser reconhecida.

Valeu Walter e Lucas![/update]


Há outros nomes, mas não consigo encontrar no São Google, pois não é um pattern muito usado.

Parecido com Smalltalk


Imagine que você tem uma classe Java que recebe muitos parâmetros na instanciação:
Person person = new Person(
aString, aDate, anotherString, aLoad, aPerson, anotherPerson
);


A quantidade de argumentos pode explodir ao extremo. Python pode apresentar maior visibilidade quanto ao significado dos parâmetros:
person = Person(
name=aString,
birthdate=aDate,
id=anotherString,
load=aLoad,
father=aPerson,
mother=anotherPerson
)


Smalltalk, uma linguagem bem mais antiga do que Python ou Java, já tinha uma abordagem bem mais elegante:
person := Person new.
person
setName: aString;
setBirthdate: aDate;
setId: anotherString;
setLoad: aLoad;
setFather: aPerson;
setMother: anotherPerson.


É possível conseguir um código similar em Java usando acessores tipo setter:
Person person = new Person();
person.setName(aString);
person.setBirthdate(aDate);
person.setId(anotherString);
person.setLoad(aLoad);
person.setFather(aPerson);
person.setMother(anotherPerson);


Finalmente


Mas é possível obter um código ainda mais elegante usando o pattern em questão.

A ideia é simples: em vez dos acessores setter não terem retorno (void), basta eles retornarem this:
class Person {
private String name;
private Date birthdate;
private String id;
private Load load;
private Person father;
private Person mother;

public Person() {
name = null;
birthdate = null;
id = null;
load = null;
father = null;
mother = null;
}

public String getName() {
return name;
}

public Person setName(String name) {
this.name = name;
return this;
}

public Date getBirthdate() {
return birthdate;
}

public Person setBirthdate(Date birthdate) {
this.birthdate = birthdate;
return this;
}

public Load getLoad() {
return load;
}

public Person setLoad(Load load) {
this.load = load;
return this;
}

public Person getFather() {
return father;
}

public Person setFather(Person father) {
this.father = father;
return this;
}

public Person getMother() {
return mother;
}

public Person setMother(Person mother) {
this.mother = mother;
return this;
}
}


Agora podemos usar uma estrutura de código similar à de Smalltalk:
Person person = new Person()
.setName(aString)
.setBirthdate(aDate)
.setId(anotherString)
.setLoad(aLoad)
.setFather(aPerson)
.setMother(anotherPerson);


[]'s
Cacilhas, La Batalema

terça-feira, 27 de janeiro de 2009

Singleton

Um design pattern bastante conhecido na engenharia de software é singleton.

Singleton é quando uma classe possui apenas uma instância e não se deseja que em uma mesma aplicação haja mais de uma.

Exemplos de classes desejadamente singleton são pools e carregadores.

Em C++ a saída para criar uma classe singleton é tornar protegido seu método construtor e criar um método getInstance para retornar a instância única:
class Loader {
protected:
Loader();

public:
~Loader();

static Loader& getInstance();
}


O método getInstance deve guardar uma referência estática para a instância:
static Loader& Loader::getInstance() {
static Loader *instance = 0;
if (!instance)
instance = new Loader();
return *instance;
}


Java utiliza a mesma abordagem, já outras linguagens, como Perl, Python e Ruby têm uma abordagem bem mais elegante.

Singleton em Python


A ideia de singleton em Python – Perl e Ruby – é usar o próprio construtor da classe para obter a instância.

Por exemplo, em vez de (Java):
Image character = Loader.getInstance().getImage("char.png");


Teremos (Python):
character = Loader().getImage("char.png")


Há três formas de implementar singleton em Python: 1implementando diretamente na classe, 2herdando um classe pai ou 3usando metaclasse (o mais divertido!).

Implementação direta na classe


Você pode implementar uma classe singleton diretamente, usando os metamétodos __new__ (construtor real) e __init__ (construtor de inicialização).

A classe ainda precisa ter um atributo de classe para armazenar a instância.

A ideia mais simples é implementar __new__ para devolver a instância em vez de uma nova a cada vez:
class Loader:

__instance = None
__initialized = False


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


Repare no atributo __initialized… isso é porque ainda temos um problema: o construtor de inicialização (__init__) será chamado novamente a cada tentativa de obter a instância.

Então precisamos verificar se a inicialização já foi executada:
    def __init__(self, *args, **keyw):
if not self.__initialized:
self.__initialized = True
# Restante do método...


Pronto! Nossa classe já é singleton.

Herança


Podemos usar uma classe pai que implemente unicidade e estendê-la:
class Singleton:

__instance = None
__initialized = False

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

def __init__(self, *args, **keyw):
if not self.__initialized:
self.__initialized = True
self._init(*args, **keyw)


Assim, para implementar uma classe singleton:
class Loader(Singleton):

def _init(self, *args, **keyw):
# Este será o construtor...


A vantagem desta abordagem é sua capacidade de reaproveitamento: é possível reutilizar a classe Singleton como classe pai de cada nova classe singleton.

No entanto traz dois inconvenientes: primeiro os métodos podem conflitar com métodos de outras classes pai em herança múltipla; segundo é preciso prestar atenção à implementação estranha (uso de _init em vez de __init__) e ter cuidado com a sobrescrita de métodos.

A solução para tornar isso mais transparente é usar uma metaclasse.

Metaclasse


Metaclasse é um classe cujas instâncias são classes. Vamos criar uma metaclasse cujas classes sejam singleton:
class singleton(type):

def __init__(cls, name, base, dict):
super(singleton, cls).__init__(name, base, dict)
cls.__instance = None
cls.__copy__ = lambda self: self
cls.__deepcopy__ = lambda self, memo=None: self

def __call__(cls, *args, **keyw):
if cls.__instance is None:
cls.__instance = \
super(singleton, cls).__call__(*args, **keyw)
return cls.__instance


Agora vem a beleza da metaclasse: para criar uma classe singleton basta fazer:
class Loader:
__metaclass__ = singleton


Nada mais! Todo o resto da classe pode ser implementado sem preocupações.

A única dúvida que pode ocorrer é: e se a classe tiver outra metaclasse?

É fácil resolver! Por exemplo: imagine uma classe singleton que implemente autopropriedades:
class Loader:

class __metaclass__(autoprop, singleton):
pass


**

Está aqui outra dica! Mais sobre singleton pode ser encontrado nas Reflexões de Monte Gasppa e Giulia C..

[]'s
Cacilhas, La Batalema

Mostrando o status e o branch de repositórios git no console

Com uma função de shell script e a ajuda da variável PS1 podemos fazer uma configuração bem interessante: exibir no próprio console em qual branch estamos e qual o estado dele.

Um exemplo de como fica:
Git no terminal

Esse é um screenshot do meu desktop, mostrando o repositório do tracks (um aplicativo interessante para quem pretende usar a metodologia GTD). Em amarelo, o caminho do repositório. Entre os colchetes, o branch no qual estamos, e finalmente, um * indicando se o repositório contém modificações ainda não confirmadas no repositório.

Para configurar, basta adicionar ao seu .bashrc:

function parse_git_dirty {
[[ $(git status 2> /dev/null | tail -n1) \
!= "nothing to commit (working directory clean)" \
]] && echo "*"
}

function parse_git_branch {
git branch --no-color 2> /dev/null | \
sed -e '/^[^*]/d' \
-e "s/* \(.*\)/[\1$(parse_git_dirty)]/"
}

export PS1='\u@\h \[\033[1;33m\]\w\[\033[0m\]$(parse_git_branch)$'


Visto originalmente aqui.

[]'s
- Walter Cruz

sábado, 24 de janeiro de 2009

Pyglet

Há alguns módulos em Python de interface com a biblioteca de mídia SDL, sendo o mais conhecido PyGame.

PyGame é legal, lembra a mim a antiga API gráfica do Colour BASIC do MSX (eita saudosismo!).

No entanto o Kao Félix apresentou recentemente um outro módulo menos conhecido, chamado Pyglet, que, a primeira vista, achei mais pythónica. =D

Gostaria então de sugirir dois tutoriais do Kao Félix:


Instalando Pyglet


Se você possui o EasyInstall, basta executar:
bash$ sudo easy_install -U pyglet


Primeira aplicação


Resolvi fazer minha primeira aplicação segundo as dicas do Kao Félix, mas adaptando a meu jeito próprio de programar.

Iniciaremos com o hash-bang, as peculiaridades de Python e os imports dos submódulos de Pyglet que vamos precisar:
#!/usr/bin/env python
# coding: UTF-8

from __future__ import division
__metaclass__ = type

from pyglet import app, clock, resource, sprite
from pyglet.window import key, Window


Carregando os recursos iniciais


Esta é classe singleton para carregar os recursos de mídia que precisaremos.

Fiz o seguinte: baixei a bola do Kao, mas pode também baixar daqui mesmo – preferível, para não sobrecarregar o link dele.

O arquivo de som está no pacote do KDE, mas você pode baixar daqui.

Nessa classe singleton, implementaremos o método __new__ (apenas para singleton) e __init__ para carregar as mídias.

Para carregar uma imagem usamos resource.image(). Para áudio, resource.media():
class Loader:
"""Singleton class for loading resources"""

__inst = None
__initialized = False

def __new__(cls, *args, **keyw):
if not cls.__inst:
cls.__inst = object.__new__(cls)
return cls.__inst

def __init__(self):
if not self.__initialized:
self.__initialzed = True

self.images = {}
self.sounds = {}

image = resource.image("char.png")
image.anchor_x = image.width // 2
image.anchor_y = image.height // 2
self.images["ball"] = image

self.sounds["pop"] = resource.media(
"pop.wav",
streaming=False
)


O parâmetro streaming=False indica que o arquivo de áudio deve ser completamente carregado para a memória – e não tratado como um fluxo (stream).

Gerenciando o teclado


Uma nova classe será usada para gerenciar o teclado. O construtor armazenará uma referência para a janela principal e o método handle_input() gerenciará as teclas:
class KeyHandler(key.KeyStateHandler):
"""Class for handling key events"""

def __init__(self, window):
super(KeyHandler, self).__init__()
self.window = window

def handle_input(self, dt):
speed = 320 * dt
dx, dy = 0, 0

if self[key.RIGHT]:
dx = 1
elif self[key.LEFT]:
dx = -1

if self[key.UP]:
dy = 1
elif self[key.DOWN]:
dy = -1

self.window.ball.x += dx * speed
self.window.ball.y += dy * speed


Repare que nosso objeto window terá um atributo ball, que é um sprite.

Agora nosso sprite


Vamos criar agora uma classe que estende sprite.Sprite, a classe de sprite de Pyglet.

O único método que implementaremos será o construtor, que buscará em Loader a imagem que desejamos:
class Ball(sprite.Sprite):
"""Sprite that's a ball"""

def __init__(self, *args, **keyw):
super(Ball, self).__init__(
Loader().images["ball"],
*args, **keyw
)


Janela principal


Finalmente a aplicação!

Por um mal hábito adquirido programando Tkinter, tenho o hábito de usar a mesma classe para aplicação e janela principal, porém você não precisa fazer esse bacalhau isso!

No entanto vou mostrar aqui da forma como estou habituado.

O construtor:
  • ajustará o título da janela (set_caption());
  • criará uma nova bola (classe Ball);
  • registrará o manipulador de eventos de teclas (push_handlers());
  • e registrará o método handle_input() do manipulador de eventos de teclas para execução periódica (clock.schedule_interval()).

class MainApplication(Window):
"""Main window and application"""

def __init__(self):
super(MainApplication, self).__init__()
self.set_caption("Teste de movimento")

self.ball = Ball(x=300, y=240)

handler = KeyHandler(self)
self.push_handlers(handler)
clock.schedule_interval(handler.handle_input, 1/60)


Manipulando outros eventos


Os métodos de Window que manipulam eventos têm nome iniciado por on_, seguido pelo nome do evento. Por exemplo: on_close, on_draw, on_key_press, on_key_release.

Vamos implementar on_close() para tocar o som ao sair e on_draw() para exibir o sprite:
    def on_close(self):
Loader().sounds["pop"].play()
super(MainApplication, self).on_close()

def on_draw(self):
self.clear()
self.ball.draw()


Fazendo funcionar


Para terminar, precisamos instanciar a aplicação e chamar o ciclo principal de Pyglet.

Veja que esse tipo de programação onde um módulo toma o controle e programamos os métodos que atenderão eventos caracteriza um framework.
if __name__ == "__main__":
window = MainApplication()
app.run()


Conclusão


Vemos aqui um ótimo módulo – ou melhor, framework – para criação de aplicações gráficas usando as bibliotecas SDL e OpenGL.

[]'s
Cacilhas, La Batalema

sábado, 10 de janeiro de 2009

Atributos privados onde?

Os programadores de Java e C++ pregam vigorosamente a favor de métodos e atributos privados, como se isso fosse uma grandíssima vantagem.

É um recurso… faz sentido em seu contexto, mas não traz vantagem alguma por si só – ao contrário da evangelização dos fanáticos.

De qualquer forma, o C++ não bloqueia tanto o acesso do programador quanto é pregado.

Por exemplo, voltemos lá atrás no artigo Portanto GDBM para Lua: havia uma classe GdbmObject com um descritor de arquivo de banco de dados privado.

Digamos que, por alguma razão bizarra, queiramos ter acesso direto ao descritor.

Mas o atributo é privado!

Vamos ao bacalhau:
class GdbmObjectCorrupting {
public:
bool closed;
GDBM_FILE fd;
}


Repare que os atributos públicos aqui têm o mesmo tipo e o mesmo nome dos atributos privados da classe original. Isso garantirá que os símbolos gerados sejam idênticos – e este é o truque.

Agora imagine que temos a instância obj de GdbmObject, da qual queremos acessar os atributos privados. A mágica é:
GdbmObjectCorrupting *aux =
reinterpret_cast<GdbmObjectCorrupting *>(obj);


Pronto! Já está feito o bacalhaua a mágica!

No escopo de aux, aux->fd dá acesso direto ao descritor de obj, assim como aux->closed dá acesso ao booleano closed de obj.

Agora, este artigo é uma prova de conceito! Não aconselho ninguém a fazer isso. Aliás o Kodumaro desaprova fortemente tal prática: reitero, é apenas uma prova de conceito.

[]'s
Cacilhas, La Batalema