quinta-feira, 17 de dezembro de 2009

Programação Incondicional

Não faltam metodologias, plataformas e tecnologias, mas resolvi criar mais uma filosofia de programação mesmo assim. A programação incondicional (ifless programming ou unconditional programming) não é um novo radicalismo. É apenas uma tentativa de reduzir bugs através do seu maior vetor: o if.

Todas as linguagens de programação possuem ifs (exceto Prolog). Parecem inevitáveis, mas, na maior parte dos casos, é possível eliminar muitos ifs sem dificuldades. Vou mostrar como.

Uma técnica muito simples (e talvez a mais antiga encarnação da programação incondicional) é o array de ponteiros para funções. É tão simples, que pode ser usada em Assembly, como um array de endereços.

Em C, posso declarar um array de funções que recebem dois ints e retornam int assim:

int (*p[4])(int x, int y);

Posso invocar qualquer uma das funções assim:

resultado=(*p[op]) (i, j);

E com isso evito a seguinte seqüência particulamente perniciosa de ifs:

if(op==0) {
resultado=somar(i, j);
} else if(op==1) {
resultado=subtrair(i, j);
} else if(op==2) {
resultado=dividir(i, j);
} else ...

Perl oferece uma das sintaxes mais simples para declarar estruturas de dados; posso declarar o array e as funções de uma só vez de maneira bastante compacta.

my @ops = (
sub { $_[0]+$_[1] },
sub { $_[0]-$_[1] },
sub { $_[0]/$_[1] },
sub { $_[0]*$_[1] },
);
my $resultado=$ops[2]->(3,2);

Java, infelizmente, não possui funções, mas elas podem ser simuladas com subclasses. Como no Perl, é possível criar as subclasses e o array ao mesmo tempo, mas a sintaxe é muito mais complicada.

Operador[] ops=new Operador[] {
new Operador() {
public int eval(int i, int j) {
return i+j;
}
},
new Operador() {
public int eval(int i, int j) {
return i-j;
}
},
//etc, etc, etc...
};
int resultado=ops[2].eval(3,2);

Usar um hash (array associativo) é só um caso especial dessa técnica. Nenhuma linguagem permite um exemplo tão compacto como Perl:

my %ops = (
'+' => sub { $_[0]+$_[1] },
'-' => sub { $_[0]-$_[1] },
'/' => sub { $_[0]/$_[1] },
'*' => sub { $_[0]*$_[1] },
);
my $resultado=$ops{'+'}->(3,2);

E esta foi a introdução à programação incondicional!

4 comentários:

  1. Isto aqui: sub { @_[0]+@_[1] }
    não seria assim? sub { $_[0]+$_[1] }

    Ou seja, da mesma forma que $ops[2] acessa o array @ops.

    Aliás, forinti é "for int i"?

    Ainda não descobri o que é alquerubim :-P

    ResponderExcluir
  2. Bem notado, já corrigi. Mas é interessante como o código funcionou mesmo com o erro. Acontece que em Perl, um array avaliado em contexto escalar vira o valor do último elemento. @_[0] é o splice de um elemento (o 0-ésimo), que no contexto escalar vira o valor do único elemento. @_[1,5] retornaria um array com os elementos de 1 a 5. Funcionava, mesmo sem ser o que eu pretendia.

    Sim, forinti nasceu de uma longa jornada programando Java.

    E Alquerubim é uma aldeia em Portugal, perto do rio Vouga, de onde vieram meus avós.

    ResponderExcluir
  3. É, eu sabia que funcionava dos dois jeitos, com @ ou $, mas tinha aprendido (na decoreba mesmo) que o certo era com $. Sabes se há casos em que o uso do @_[0] causaria um comportamento inesperado?

    Claro, se eu tivesse pesquisado no Google eu saberia o que é Alquerubim, mas achava que fosse um jogo de palavras e nem pesquisei hahaha

    ResponderExcluir
  4. Boa pergunta. Tem umas funções que fazem coisas diferentes conforme o tipo do parâmetro, mas se o valor vai sempre ser imediatamente usado num contexto escalar, não deve acontecer nada.

    ResponderExcluir