sábado, 30 de janeiro de 2010

Comparações fúteis

Há poucos dias, o Instituto Idea Brasil publicou uma pesquisa salarial que revelou as médias salariais para analistas de sistemas em diferentes capitais. Porto Alegre ficou em terceiro lugar, com R$2.457. Como era de se esperar, Rio de Janeiro (R$3.011) e São Paulo (R$2.574) ficaram à frente. Foram considerados apenas os postos com contrato celetista.

No Brasil, ainda temos poucas informações desse tipo. É uma pena, porque ajudam sobremaneira o trabalhador a avaliar sua condição. Nos Estêites, há uma quantidade enorme de informação. Pode-se saber o salário médio ou dos n% mais baixos ou mais altos para qualquer cidade e qualquer cargo.

Em 2009, a CNN classificou o trabalho de analista de sistemas como um dos 50 melhores nos Estêites. O salário médio anual foi de US$71.500. Os 10% melhor pagos ganharam, em média, US$95.900.

As comparações diretas não têm muito sentido. Afinal, a economia deles é muito maior e o custo de vida também. Mesmo assim, é possível fazer algumas comparacões indiretas interessantes.

O salário médio anual em Porto Alegre, é de R$32.759,18 (13 salários e adicional de férias). Não vou incluir FGTS, porque os gringos também ganham uns trocos quando são despedidos. Às vezes uns bons trocos.

Pois, a minha primeira comparação vai ser com a renda per capita. A deles é US$47.440; a do Rio Grande do Sul é R$16.689. A do Brasil é R$15.205. A de Porto Alegre é R$23.534. Os valores são de 2008, exceto o de Porto Alegre, que é de 2007.

Pois, um analista gringo ganha, então, 150% do PIB per capita. Um gaúcho, 215% do PIB per capita nacional, 196% do estadual e 138% do municipal.

E com o salário médio dos trabalhadores? Lá, o salário anual médio de um trabalhador com mais de 25 anos é US$32,140. Então, um analista ganha 222% dessa média. Aqui, o salário médio de um trabalhador é R$ 1.227 (do Brasil é R$1.350,33). Logo, um analista gaúcho ganha 200% do que ganha seu vizinho.

Comparados aos salários médios, não são ruins. Mas ainda penso que poderiam ser bastante melhores; Angola, que é muito mais pobre, paga US$3.000 mensais para brasileiros que lá vão trabalhar. O Brasil tem falta de cerca de 50 mil profissionais de TI. Mesmo assim, os salários não crescem e as pessoas preferem ir buscar trabalho no gelado Canadá ou na pobre Angola.

É claro que os nossos custos trabalhistas são altos, mas os dos Estêites não são negligenciáveis. Enquanto os encargos aqui, segundo os empresários, podem somar algo como 100%, lá chegam a 40% ou 50%, mas variam muito, conforme o estado. Então, o custo total de um analista gringo pode ser de até US$107.250 e o de um gaúcho é R$64.914. Para ser preciso, os cálculos que chegaram a mim até agora e que concluem que nossos encargos sobre a folha são 100% incluem as férias, o décimo-terceiro e até os fins de semana. Excluindo isso tudo (porque fazem parte do que o trabalhador recebe, não do que é pago ao governo), os encargos representam 53,93% do salário contratual ou 25,1% do total pago ao empregado. Então, o custo total médio para o empregador gaúcho é R$45.384,72.

Penso que há dois problemas. O primeiro é a falta de informação. Foi muito mais fácil encontrar os dados para a economia americana que para a brasileira e a gaúcha. Os nossos profissionais simplesmente não têm os dados que precisam para negociar seus salários. E os sindicatos não ajudam. O segundo é a falta de investimento em TI. Nossas empresas ainda não alcançaram um nivel de automação semelhante ao dos Estêites; nossos governos não criam cursos profissionalizantes suficientes; nossos colégios não informam sofre o mercado de trabalho (precisamos formar tantos advogados?); nossas universidades oferecem poucas vagas.

terça-feira, 26 de janeiro de 2010

Um Método Lúdico

O UML é limitado demais para representar o dia-a-dia do analista de sistemas. Então, resolvi criar meu próprio método, começando pela análise psicológica dos atores. A palavra "ator" foi sabiamente escolhida, porque alguns usuários realmente merecem um Kikito.


Certos usuários, os nervosos, têm medo de apertar qualquer tecla e não hesitam em ligar para o suporte e perguntar: o micro está perguntando se eu quero realmente apagar o arquivo, eu aperto "sim" ou "cancelar"?

Já notaram, no shopping, como o operador que valida o cartão do estacionamento aperta o botão até a pobre máquina cuspir o recibo? Aposto que o manual diz: aperte o botão 20 vezes para gerar o recibo.

Finalmente, tem atores que não dão a mínima para o sistema. Nem na fase de especificação e menos ainda para usá-lo. Afinal, os analistas de sistemas já nascem sabendo todas as regras de negócio. E se precisarem de um relatório, basta ligar para o suporte e pedir, oras.

Um Método Lúdico© faz piada de tudo o que o UML não resolve, embora também não resolva coisa alguma.

segunda-feira, 25 de janeiro de 2010

Gráficos em SQL

Produzir gráficos com poucos recursos é uma das diversões matemáticas mais interessantes. Com SQL, pode parecer até impossível. Mas depois que vi uns desenhos natalinos no Explain Extended, resolvi tentar.

Resolvi começar com um objetivo bem simples: criar um gráfico de barras. Comecei com duas consultas: um de 100 linhas para representar porcentagens e outra com os valores que quero mostrar.

A de porcentagens é simples:

select rownum as pct
from usuarios
where rownum<100

E a de dados deve retornar apenas uma linha. A consulta abaixo compara o número de usuários que têm senha com os que ainda não têm:

select sum(case when senha is null then 1 else 0 end) as coluna1,
sum(case when senha is not null then 1 else 0 end) as coluna2
from usuarios

Para fazer um gráfico de barras, só preciso juntar as duas. Posso fazer um cross-join, porque uma delas só tem uma linha mesmo. No exemplo abaixo, reduzi o número de linhas para 10:

select pct,
(case when coluna1/(coluna1+coluna2)<pct then ' ' else '*' end),
(case when coluna2/(coluna1+coluna2)<pct then ' ' else '*' end)
from (
select sum(case when senha is null then 1 else 0 end) as coluna1,
sum(case when senha is not null then 1 else 0 end) as coluna2
from usuarios) dados,
(select rownum/10 as pct
from usuarios
where rownum<10) linhas
order by pct desc

E o resultado é:

0,9
0,8
0,7
0,6 *
0,5 *
0,4 *
0,3 * *
0,2 * *
0,1 * *

Só falta achar uma utilidade para isso!

sexta-feira, 22 de janeiro de 2010

Desafio lógico

Não são poucos os donos de animais de estimação que conversam com seus pequenos e emulam suas falas. Falar é algo que os pelados fazem e miar e latir é o que os peludos fazem. Em raros momentos, algo realmente profundo pode surgir, sem que um dos lados perceba.

Gato - Mãe, por que os humanos antropomorfizam seus bichinhos?
Dona - Se tu estás perguntando isso, é porque não estás perguntando isso e eu não preciso responder!

quinta-feira, 21 de janeiro de 2010

Programação Incondicional VI

No último artigo dessa série eu criei uma função recursiva para calcular o fatorial sem usar ifs. Porque escrevi em Java, a solução acabou complicada. Decidi tentar em Perl que, sendo uma linguagem dinâmica, permite expressar melhor esse tipo de enjambração.

Comecei com o fatorial:

use integer;

sub fact {
my $n=shift;

(sub {1},
sub {
my $n=shift;
$n*fact($n-1);
})[-1*((1-$n)>>31)]->($n);
}

print fact($ARGV[0]);

Eu criei uma função que usa uma lista de referências a funções e decide qual executar conforme o índice -1*((1-$n)>>31). A função da posição zero apenas retorna o valor 1 e a função da posição 1 retorna $n multiplicado pela chamada recursiva à função fact().

A solução é bastante mais simples que a escrita em Java. E também tem uma estrutura que se parece com a notação matemática.



A estrutura para a série de Fibonacci é igual.

use integer;

sub fib {
my $n=shift;

(sub {shift},
sub {
my $n=shift;
fib($n-1)+fib($n-2);
})[-1*((1-$n)>>31)]->($n);
}

print fib($ARGV[0]);

E isso revela que talvez não seja difícil escrever uma função para gerar funções recursivas desse tipo...

Programação Incondicional V

Alguns ifs simples, como o exemplo abaixo, podem ser substituídos por expressões.

if(a<b){
x=c;
} else {
x=d;
}

Esse bloco pode ser trocado por ((((a-b) >> 31) & (c^d)) ^ d), que pressupõe que estejam sendo usados inteiros de 32 bits e que o shift seja com sinal (vale em Java e na maior parte dos Cs).

O que ele faz é relativamente simples:
  1. Subtrai b de a para produzir um número positivo se a for maior ou negativo se b for maior;
  2. Executa um shift de 31 posições para produzir 0 se o número for positivo ou -1 (0xFFFFFFFF) se for negativo;
  3. Executa um XOR de c com d e um AND do resultado com o valor do passo 2;
  4. O AND do último passo vai produzir 0 ou c XOR d;
  5. Se o valor do passo 4 for 0, o resultado final será d, porque 0 XOR d = d;
  6. Se o valor do passo 4 for c XOR d, o resultado final será c, porque c XOR d XOR d = c.

Isso pode ser usado para tirar ifs de recursões. As recursões sempre têm um if para determinar quando parar, como no exemplo abaixo:

public int fact(int n) {
if(n=1) {
return 1;
} else {
return n * fact(n-1);
}
}

O que se pode fazer nesse caso é escrever duas funções: uma sem recursão e uma com recursão. No exemplo abaixo, uso uma lista de funções e escolho qual usar conforme o valor de x.

public abstract class Factorial {

protected abstract int fact(int x);

private static class Factorial0 extends Factorial {
@Override
protected int fact(int x) {
return 1;
}
}

private static class FactorialN extends Factorial {
@Override
protected int fact(int x) {
return x*getFunc(x-1).fact(x-1);
}
}


private static Factorial[] funcs=new Factorial[] {
new Factorial0(), new FactorialN()
};

protected static Factorial getFunc(int x) {
return funcs[-1*(((1-x)>>31))];
}

public static int factorial(int x) {
return getFunc(x).fact(x);
}

public static void main(String... args) {
System.out.println(Factorial.factorial(Integer.parseInt(args[0])));
}

}

A expressão -1*(((1-x)>>31)) é uma versão simplificada da expressão original, porque só é preciso retornar 1 quando x>1 ou 0 se x<=1. A expressão (1-x)>>31 sempre retorna 0 ou -1, então a multiplicação por -1 transforma isso em 0 ou 1, que podemos usar para indexar a lista de funções.

É mais complicado, mas não tem ifs!

segunda-feira, 18 de janeiro de 2010

Nostalgia seletiva

Meus colegas estavam ontem reclamando que os programadores mais novos parecem ter déficit de atenção; escrevem código sem planejar e não se preocupam com o desempenho. Eu brinquei que eles estão é ficando velhos.

Concordei que seria mesmo interessante que a faculdade de informática começasse com um bom micro de 8 bits. Poucos recursos ajudam a criar caráter. Mas também fiquei com medo que alguém ainda mais velho propusesse que o primeiro semestre se passasse a cartões perfurados.

Afinal, as máquinas de 8 bits já tinham linguagens interpretadas. Não era preciso compilar BASIC. Então, o desenvolvimento já podia se dar por tentativa e erro, mesmo sem uma IDE gráfica.

O BASIC que eu aprendi (BBC Basic) permitia misturar código de máquina e até criar funções cujo assembly seria remontado a cada invocação:

3590 DEFPROCnmiread
3600 nmi$="read"
3610 VDU 21
3620 FOR opt% = 0 TO 2 STEP 2
3630 P%=&D00 :REM NMI address
3640 [
3650 OPT opt%
3660 PHA
3670 LDA statusregister%
3680 AND #&1F
3690 CMP #&03 \data ready & busy bits set
3700 BNE exit
3710 LDA dataregister%
3720 .save
3730 STA &FFFF
3740 INC save+1
3750 BNE exit
3760 INC save+2
3770 .exit
3780 PLA
3790 RTI
3800 ]
3810 NEXT
3820 VDU 6
3830 ENDPROC
Essa procedure é de um programa para ler disquetes com FAT12. É a procedure que executa a leitura. O programa envia um comando para a controladora e essa procedure monta o código que vai receber a resposta através de uma interrupção. A interrupção é tratada pelo código que estiver no endereço &D00 (linha 3630). Há um laço que inicia na linha 3620 (e que termina na linha 3810), porque o código precisa de uma montagem de duas passadas (o comando da linha 3750 faz referência a um endereço posterior).

Quanto aos jovens, acho que há muitos que já se preocupam com o desempenho de seus programas. Há muitos que conhecem a fundo o Linux, por exemplo. Outros sabem escrever aplicações de alto desempenho para a web. Os problemas de hoje, assim como as ferramentas, são diferentes e não faz mal termos idéias novas para atacá-los.

Só por segurança, é bom manter um programador grisalho na equipe.

domingo, 17 de janeiro de 2010

Comparações inconvenientes

O Brasil é mesmo uma terra de grandes paradoxos. Tivemos uma ditadura militar cujo objetivo manifesto era o de evitar que idéias de esquerda levassem nossa nação para o mal. Acabamos com uma economia fechada.

Na década de 1980, eu achava incrível a variedade de produtos que havia no Uruguai. Lá podiam ser vistos carros de todas as marcas. Nas lojas de eletrônicos havia uma infinidade de produtos de todo o mundo. E a qualidade era visivelmente superior.

Mais tarde descobri que a nossa economia era mais fechada que a dos países do leste europeu. Sim, os comunistas faziam mais comércio que nós. E criavam mais tecnologia também.

Mesmo um país pequeno como a Tchecoslováquia produziu mais computadores pessoais que o Brasil. Eles tinham cerca de 15 milhões de habitantes e produziram dezenas de diferentes computadores domésticos.

Os micros nacionais realmente bons começaram a aparecer depois de 1985: o TK-3000, os MSX e o Unitron Mac 512 (que infelizmente acabou proibido). Mesmo assim, eram só cópias.

Parece-me que nossa política econômica tinha muita retórica e pouca substância; temo que isso não tenha mudado muito.

sexta-feira, 15 de janeiro de 2010

Programador tem que ser preguiçoso

Um bom programador tem que ser preguiçoso. A idéia não é minha, é do Larry Wall.

Eu concluo que a maior parte dos programadores de Java são péssimos. Eles adoram empilhar frameworks sobre frameworks e configurar tudo com milhares de linhas de XML.

Até os anúncios para Java são umas monstruosidades: é imprescindível conhecimento de JDK1.6, JPA, EJB3, RMI, Spring, Struts2, Eclipse, JUnit, Hibernate, OC4J, Groovy, Swing, Comet, XML, XSLT, XSD; desejável conhecimento de RUP, UML, CMM, ITIL; bônus para quem souber inglês.

E o que é pior: tem muitos que conhecem tudo isso e não hesitam em usar tudo em todos os projetos. Tem sistemas com mais frameworks que telas. E mais XML que Java.

Nunca teria surgido no mundo Java o que surgiu no Reino do Camelo: Perl Golf. O objetivo desse novo esporte é o de resolver um problema com o menor número de caracteres. A disputa começa com uma solução qualquer e a cada iteração uma solução menor tem que ser apresentada. A última e menor solução é a ganhadora.

Eu decidi praticar um pouco de Java Golf num projeto que herdei. Era uma monstruosidade com 700KB de Java e XML. Outros 700KB estavam na camada de apresentação. Era escrito com ADF (Oracle Application Development Foundation).

O resultado final foi 95KB de Java e XML, 228KB de apresentação e 20KB de PL/SQL. Não recorri a truques ou a frameworks mágicos. Só escrevi o que precisava ser escrito. Nem mais, nem menos.

Troquei tudo por Struts2 e Tiles, mesmo correndo o risco de ser acusado de falsa preguiça. Mas como eu vou ter que continuar mantendo o sistema, acho que garanti um futuro mais tranqüilo.

quinta-feira, 14 de janeiro de 2010

Programação Incondicional IV

O null é uma fonte interminável de ifs. Uma maneira de eliminá-los é simplesmente não usar nulls, mas isso exige muita disciplina e não é prático para aplicar a código legado.

Por outro lado, algumas formas de codificar ajudam a simplificar o código e a torná-lo mais legível. Nos laços, por exemplo, é comum ter que testar se uma referência é nula:

if(a!=null) {
 for(int i=0; i<a.length; i++) {
  System.out.println(a[i]);
 }
}

Basta tirar o if e incluir a condição no for:

for(int i=0; a!=null && i<a.length; i++) {
 System.out.println(a[i]);
}

Se estou usando descendentes de List, costumo criar uma subclasse (esse truque aprendi com um programador de C#) e nela coloco uma instância estática vazia (e aproveito para criar um array vazio também):

public class PessoaList extends ArrayList<Pessoa> {
 public static final PessoaList EMPTY_LIST=
   Collections.unmodifiableList(new PessoaList(0));
 public static final Pessoa[] EMPTY_ARRAY=new Pessoa[0];
}

Com isso, cada método que retornar uma lista vazia de Pessoa pode usar uma dessas referências. Não economiza muita memória, mas economiza ifs e tempo do coletor de lixo.

quarta-feira, 13 de janeiro de 2010

Objetivos impossíveis

Um dia eu estava sem tarefas no escritório e decidi ler o Granma (jornal cubano), só por diversão.

O proxy da empresa respondeu que estava me bloqueando porque eu estava tentando ver pornografia. A url é "granma.cu". Talvez a retórica socialista seja demais para a sensibilidade do proxy.

Para testar o proxy, tentei o "nerve.com", que não chega a ser pornográfico, mas certamente não é adequado para o ambiente de trabalho. Passou sem problemas.

Tem certos objetivos que já nascem mortos.

segunda-feira, 11 de janeiro de 2010

Windows em cartucho

Não conheço nenhum micro de 32 bits que utilize cartuchos. Poucos de 16 bits os tinham, mas muitos de 8 bits usavam essa forma de armazenamento.

Incrivelmente, já houve até um PC que usava cartuchos. O PCjr usava o 8088 e, portanto, era uma máquina de 16 bits.

Os cartuchos têm uma vantagem enorme: o código está instantaneamente disponível para execução. Não é preciso esperar que o programa seja carregado para a memória.

Seria ótimo ter o Windows em cartucho: não seria preciso esperar tanto para iniciar a máquina.

Há, no entanto, um problema sério. Os programas estão muito maiores e, consequentemente, têm muito mais bugs. É impossível lançar um produto do tamanho do Windows 7 e não ter que publicar remendos para corrigir, pelo menos, os problemas de segurança.

Por outro lado, o cartucho poderia conter apenas o cerne do sistema. As configurações, atualizações e drivers seriam gravados no disco rígido, ou numa memória flash dentro do próprio cartucho.

Talvez seja só um pouco de saudosismo, mas era bom poder iniciar o micro num segundo.

quinta-feira, 7 de janeiro de 2010

Evolução II

Esta semana um biólogo americano publicou o resultado surpreendente de sua pesquisa: 8% de nosso genoma veio de um vírus. Um dia ele infectou um antepassado nosso e nunca mais saiu do nosso código genético, assim como as mitocôndrias invadiram células pré-históricas e decidiram ficar para sempre.

O artigo vai ser publicado na revista Nature, conforme foi noticiado pela Universidade do Texas em Arlington (UTA).

Quando tempo, em média, será que uma máquina Windows passa executando código de vírus?