Arquivo da categoria ‘Computação’

Detalhes… (Windows 7)

29 Outubro, 2009

Alguém pode me explicar o que raios é esse espaço em branco (onde estão os pontos de interrogação) nesta janela?Janela "Opções de pasta" com espaço vazio desperdiçado à direitaQuando eu vi isso no Windows Vista achei estranho. Pensei que fossem corrigir no Windows 7. Mas está a mesma coisa! Será que corrigem no Windows 8?

Se não fosse esse espaço em branco, nem seria necessário ter uma barra de rolagem horizontal na lista “Configurações avançadas”…

Na versão em inglês não tem esse problema:

Versão em inglês das opções de pasta, sem espaço em branco.

Dia das crianças

11 Outubro, 2009

Na véspera do dia da criança, estou jogando Street Fighter IV! Muito massa! Pra quem ainda jogava jogos de 10 ou 15 anos atrás como eu, é deslumbrante o visual de um jogo atual! Aliás, alguém tem alguma dica de jogo pra eu botar em uso a minha placa de vídeo nova? :-)

Interpolação de strings em Python

8 Outubro, 2009

Mas que coisa! Em Python é só o cara passar um tempo fora e quando chega de volta já mudou um monte de coisas na linguagem. Quando eu aprendi a linguagem (na versão 2.3, há uns 5 anos), só havia uma maneira de inserir valores no meio de uma string: com o operador %, que funciona parecido com o printf do C.

Mas agora fui baixar o Python 3.1 pra automatizar uma tarefa aqui e me deparei com 3 (3!!!!! dá pra acreditar???) métodos totalmente diferentes de fazer a mesma coisa. O (1) jeito antigo do operador %, com a sintaxe “%d”, “%s”, “%f”, etc.; (2) a classe Template, com a sintaxe “${nome_campo}”, e (3) o método format, com a sintaxe “{nome_campo}”

Onde eles estavam com a cabeça? O Python 3 não era pra limpar a linguagem de coisas incoerentes? Por exemplo o caso do print, que era um comando especial e foi transformado em função. Li e reli os PEPs (0292 e 3101) e, embora eles tentem explicar, não entendi qual é o motivo de precisar de 3 formas diferentes de formatar strings, cada uma com uma sintaxe diferente. Não era o Python que dizia “There should be one– and preferably only one –obvious way to do it.”? (retirado do import this). Eu justamente preferia Python a Perl e Ruby porque estas duas têm milhares de maneiras de fazer as mesma coisas, com minúsculas diferenças (ex.: && e and) que fazem a gente perder tempo com minúcias.

Bom, fora isso, Python parece continuar sendo uma linguagem com uma legibilidade muito boa, poderosa e de alta produtividade. Se alguém tiver algo mais a comentar sobre a linguagem (especialmente da versão 3 em diante, que tem mais novidades), posta aí!

Classificação de músicas do Windows Media Player 11

8 Outubro, 2009

Por que será que o Windows Media Player 11 classifica com 4 estrelas (de um máximo de 5 estrelas) músicas que só tocaram uma vez? Nas versões anteriores não era assim. Só num computador que uso que o Media Player classifica músicas novas com 2 ou 3 estrelas. Nos outros ele já põe 4 estrelas quando só tocou uma vez. Não dá pra entender. É um saco. :-(

Linux é difícil

22 Novembro, 2008

Eu postei uma vez um texto no blog dando um exemplo de como o Linux pode ser fácil de usar. Agora vou dar o exemplo contrário, e expressar a minha indignação.

Trata-se de um fato acontecido no mesmo computador descrito no outro post. A distribuição que veio já foi trocada por um Mandriva há bastante tempo. É uma das distribuições famosas e com fama de fácil de usar. Certo dia eu estava lá em Santa Maria e me pediram pra instalar a impressora (multifuncional) nova. No Windows do notebook (esse notebook era meu, mas eu dei ele pra minha mãe), foi fácil, embora um pouco demorado. É só colocar o CD, seguir as instruções e conectar o cabo quando indicado.

Já no Linux… A primeira coisa que eu pensei foi: “Certo, dizem que a HP tem um bom suporte a suas impressoras no Linux. Não estou com muita paciência, mas acho que não vai ser difícil”. Primeiro tentei usar as ferrramentas da distribuição Mandriva. Tudo muito bonito e organizado, mas… não tinha o modelo de impressora na lista! Pronto, começou… Fui procurar na internet. Li relatos de que esse modelo tinha funcionado automagicamente no Ubuntu. Aí eu já comecei a ficar meio irritado pelos esforços desperdiçados na comunidade Linux. Algo que funciona tranqüilamente numa distribuição, causa um monte de irritações ao usuário da outra até o dia que os mantenedores dela resolverem/conseguirem adicionar o recurso correspondente. Recurso esse que já existe em outra distribuição. Mas não basta existir na outra, pois sempre tem uns detalhezinhos que devem ser ajustados, portados ou redesenvolvidos…

Então tá. Segunda opção: o saite da HP. Procurei um pouquinho e achei o lugar dos drivers, num saite separado do principal. Havia várias distribuições suportadas, inclusive o Mandriva que estávamos usando. Baixei e segui as instruções. No parágrafo anterior eu tinha tentado um instalador gráfico e bonito do painel de controle do Mandriva. Agora estava eu de frente com um instalador em modo texto, que pergunta no início da instalação se ele detectou corretamente a distribuição que estou usando. Argh.

Dava pra notar que o instalador tinha sido bem trabalhado pra funcionar em várias distribuições. De novo eu fiquei pensando no desperdício de esforços pra fazer isso. O instalador detectou a distribuição, tentou instalar pacotes, detectou que eu estava com o package manager aberto e pediu que eu o fechasse. Ótimo. Me instruiu a cadastrar os repositórios de pacotes. Ótimo. Só que bah, a essa altura eu já tinha dado vários comandos na linha de comando. Eu não conseguiria imaginar um usuário comum fazendo isso. O usuário talvez nem soubesse que deveria procurar os repositórios de pacotes 64 bits. No fim, o cadastro de repositórios foi demorado e completamente não-intuitivo. Se não fosse o instalador me indicar o saite com as informações, eu nunca teria paciência de descobrir como cadastrar os repositórios certos (eu estava em Santa Maria só no fim-de-semana, não queria perder muito tempo). E, pelo que me lembro, os repositórios que estavam cadastrados antes não eram os recomendados. Devia ser por isso que várias atualizações estavam falhando.

Mas mesmo seguindo todas as instruções, algumas dependências não puderam ser resolvidas. Aí que foi a parte estressante. Foi um baita trabalho pra instalar manualmente algumas coisas, desativar recursos que eu não consegui instalar e finalmente convencer o instalador que as dependências estavam realmente ali, já que o nome de certos pacotes não era igual ao esperado (acho que por causa do “64″ no nome). Depois de agüentar várias mensagens de erro nada descritivas, como “O pacote não pôde ser instalado”, imprimi a página de testes, e quase fugi correndo pra que não me pedissem pra testar o escâner, hehe.

Isso tudo que eu estava usando uma distribuição suportada pelo instalador da HP! Imaginem se fosse uma daquelas desconhecidas que costumam vir pré-instaladas nos computadores! A conclusão a que eu cheguei é que tudo é muito bonito e fácil usando os recursos e repositórios de pacotes da distribuição. Precisou instalar algo que não esteja no repositório oficial, caímos no modo texto e na instalação a partir dos códigos-fontes, pois desse jeito é mais fácil desenvolver algo que seja compatível com múltiplas distribuições. E mesmo assim, nesse desenvolvimento deve ter sido gasto um bom tempo pra lidar com as particularidades de cada distribuição. Foi aí que entendi perfeitamente por que o saite dizia que a instalação no Linux não tinha suporte por telefone. Imaginem a dificuldade do atentende tentando guiar um usuário comum pela instalação em sabe-se-lá-qual-distribuição…

Essa história de cada distribuição inventar seu instalador, seu gerenciador de pacotes e de drivers é muito desperdício de esforço. Esse é exatamente o mesmo tipo de problema que eu enfrentava há uns 6 anos atrás, quando usava Linux com mais freqüência. Enquanto certos programas de código livre são maduros e estáveis, sempre nos deparamos com aquele programinha mal feito, incompatível com o resto do mundo e totalmente desconhecido fora do círculo de usuários da distribuição, criado de qualquer jeito como um tapa-buraco pelos próprios desenvolvedores da tal distribuição. E a cada 6 meses, temos uma nova e maravilhosa versão, corrigindo os bugs da anterior e criando novos, andando em círculos. Quando a gente acha que se acostumou com as idiossincrasias de uma versão, muda tudo.

Já vi gente desistir do Linux porque o programa desejado só tinha pacotes pra Red Hat e Debian. Para as outras distribuições, as instruções eram: compile o código fonte! Inaceitável.

loop

26 Fevereiro, 2008

Enquanto eu escrevia o post anterior, ainda acabei fazendo um mini-teste paralelamente.  Estão vendo o loop abaixo?

sub ecx, 1;
test ecx, ecx;
jnz loop_label;

Pois então, existe uma instrução em assembly que faz exatamente o mesmo que essas 3 instruções. Ela se chama loop.

loop loop_label;

Ela decrementa ecx e repete o loop enquanto ecx não for zero. Experimentei usar essa instrução, e o interessante é que em todos os testes ela foi mais lenta do que usar as 3 instruções separadamente! Isso acontecia nos dois processadores (Pentium 4 e Sempron). Não coletei resultados, mas a instrução loop demorava consistentemente alguns poucos ciclos a mais. Por isso optei pela versão mais rápida, com 3 instruções. Não que isso fosse fazer diferença nos resultados do post anterior, mas é interessante, né?

static_cast<int>(double_var);

13 Fevereiro, 2008

Pois então, continuando o assunto do assembly do post anterior…

Segundo lia em vários lugares, truncar um double para um int é uma operação lenta na arquitetura x86, apesar de ser uma operação básica das linguagens C e C++. O interessante é que arredondar um número é mais rápido porque as flags do processador ficam por default no modo de arredondamento (depois de muito pesquisar, parece que esse default é pra obter mais precisão nas operações em geral, pois não é só a conversão pra int que faz uso dessa flag).

Então achei que esse seria um bom motivo pra aprender um pouco mais de assembly, já que o ganho de desempenho parecia ser significativo e alternativas usando construções de alto nível são esparsas: tem o lrint do C99, tem bibliotecas externas com uma função round, como o cvRound da OpenCV que usam assembly, mas nada suficientemente bom (você instalaria uma biblioteca só pra ter uma função round? é melhor copiar a função direto). E faria as medidas com outra instrução assembly, a rdtsc.

As funções que eu usei são as seguintes (se acharem algum erro, me avisem; assembly não é minha especialidade, hehe):

Pra obter o tempo atual:

/* O VC++ 2002 não aceita long long, vai __int64 mesmo... */
__declspec(naked) unsigned __int64 get_rdtsc()
{

__asm
{

push ebx;
xor eax, eax;
cpuid;
rdtsc;
pop ebx;
ret;

}

}

Pra fazer 400 conversões de double pra int em assembly, com fld seguido de fistp:

__declspec(naked)
unsigned __int64 asm_double_to_int(...)
{

__asm
{

/* Preferi fazer toda a manipulação da pilha à mão (por isso o __declspec(naked)) */
push ebp;
mov ebp, esp;
sub esp, 12;

call get_rdtsc;
mov dword ptr [ebp-8], eax;
mov dword ptr [ebp-4], edx;

mov ecx, 100; // Loop counter
loop_label:
fld qword ptr [ebp+8];
fistp dword ptr [ebp-12];

fld qword ptr [ebp+8];
fistp dword ptr [ebp-12];

fld qword ptr [ebp+8];
fistp dword ptr [ebp-12];

fld qword ptr [ebp+8];
fistp dword ptr [ebp-12];

sub ecx, 1;
test ecx, ecx;
jnz loop_label;

call get_rdtsc;

/*
A subtração de valores 64 bits eu tirei daqui:
win32-assembly-cheat-sheet
*/

sub eax, dword ptr [ebp-8];
sbb edx, dword ptr [ebp-4];

mov esp, ebp;
pop ebp;
ret;

}

}

Pra fazer 400 casts de double pra int em C++, com o mesmo tipo de loop feito:

/* Com as variáveis volatile o otimizador não remove código inútil... */
unsigned __int64 cast_double_to_int(volatile double a)
{

int i = 100;
unsigned __int64 t1 = get_rdtsc();
volatile int j;
do{

j = static_cast<int>(a);
j = static_cast<int>(a);
j = static_cast<int>(a);
j = static_cast<int>(a);

}while(--i);
unsigned __int64 t2 = get_rdtsc();
return t2 - t1;

}

O código do main está abaixo. Testo o arredondamento de 2 números (5.5 e 1.125) repetindo cada teste 8 vezes.

int main(int argc, char ** argv)
{

const int reps = 8;
unsigned __int64 tempo;
cout << "Assembly (default rounding):\nTest 1:\n";
for(int i = 0; i < reps; ++i){

tempo =
asm_double_to_int(5.5);
cout << tempo << '\n';

}
cout << "Test 2:\n";
for(int i = 0; i < reps; ++i){

tempo =
asm_double_to_int(1.125);
cout << tempo << '\n';

}
cout << "---------------------------------\nC++ cast (truncating):\nTest 1:\n";
for(int i = 0; i < reps; ++i){

tempo =
cast_double_to_int(5.5);
cout << tempo << '\n';

}
cout << "Test 2:\n";
for(int i = 0; i < reps; ++i){

tempo =
cast_double_to_int(1.125);
cout << tempo << '\n';

}
return 0;

}

Então vamos aos resultados. Fiz os testes no Visual C++ .NET 2002 e no Visual C++ .NET 2005 Express. Não testei com o GCC porque a sintaxe de inline assembly é meio esquisita e eu não aprendi direito. Testei em duas máquinas. Uma é um Intel Pentium 4 Northwood 2.4 GHz e o outro é um AMD Sempron Mobile 3000. Os resultados são em número de ciclos, não em segundos. Quanto mais ciclos, mais lento, mas só se a comparação for feita com outra execução no mesmo processador. A compilação é em modo Release (isto é, com otimizações).

VC++ 2002, Pentium 4 2.4GHz:


Assembly (default rounding):
Test 1:
2440
1540
1424
1424
1460
1424
1424
1460
Test 2:
1460
1460
1424
1424
1460
1424
1424
1424
---------------------------------
C++ cast (truncating):
Test 1:
26440
26236
25904
25904
26440
26440
25472
26440
Test 2:
26440
26452
25904
26440
25472
26808
25580
25904

========================

VC++ 2005, Pentium 4 2.4 GHz:


Assembly (default rounding):
Test 1:
2460
1412
1428
1412
1420
1412
1420
1420
Test 2:
1412
1420
1412
1424
1412
1412
1420
1412
---------------------------------
C++ cast (truncating):
Test 1:
8760
6896
6904
6900
6904
6904
7560
6892
Test 2:
6900
7248
6900
6904
6904
6904
6904
6904

========================
========================

VC++2002, Sempron Mobile 3000:


Assembly (default rounding):
Test 1:
992
877
877
877
877
877
877
877
Test 2:
877
877
877
877
877
877
877
877
---------------------------------
C++ cast (truncating):
Test 1:
8281
7366
7330
7294
7294
7294
7294
7294
Test 2:
7294
7294
7294
7294
7294
7294
7294
7294

========================

VC++2005, Sempron Mobile 3000:


Assembly (default rounding):
Test 1:
773
708
708
689
689
689
689
689
Test 2:
716
689
689
689
689
689
689
689
---------------------------------
C++ cast (truncating):
Test 1:
4902
4678
4678
4647
4922
4922
4922
4922
Test 2:
4695
4922
4922
4922
4922
4922
4922
4922

Conclusões:

1 – Dá pra ver que o Pentium 4 precisa de mais ciclos pra fazer a mesma coisa, por isso os Pentium 4 tinham freqüências maiores, pra compensar isso. (eu disse “tinham” porque a geração do Pentium 4 já passou, hehe)

2 – O VC++2002 executa o truncamento chamando uma função que arredonda e, se necessário faz uma subtração pra transformar o arredondamento em truncamento. O VC++2005 chama uma função que trunca usando uma instrução SSE2 (cvttsd2si) do processador, que é bem mais rápida, como dá pra ver. O programa decide dinamicamente se o SSE2 está disponível e usa a instrução em caso afirmativo. Nos testes, os 2 processadores tinham SSE2.

3 – Não importa qual seja a técnica usada pelo compilador, nem o processador, arredondar em assembly sempre resulta em menos instruções e maior desempenho do que truncar com um cast. Isso é interessante de ser feito quando você precisa de um int a partir de um double e não importa se vai truncar ou arredondar. Por exemplo, quando coordenadas em double precisam ser convertidas pra coordenadas de pixels, a diferença é minúscula, ainda mais se for uma animação. Outro caso é a linguagem Lua, que usa bastante a conversão de double pra int (pois todas as variáveis numéricas em Lua são double) e por isso usa este tipo de truque pra fazer a conversão, já que tanto truncar quanto arredondar servem.

Se você tem um loop interno fazendo muito dessas conversões de double pra int, convém considerar usar uma funçãozinha em assembly com fld e fistp como esta:

inline int double_to_int(double val)
{

int t;
__asm
{

fld val;
fistp t;

}
return t;

}

4 – Naturalmente, o assunto não está esgotado, pois existem muitos outros tipos de processador pra comparar, compiladores (que resultados será que o GCC e o compilador Intel gerariam?) e maneiras de truncar doubles, como por exemplo: fazer como o VC++2002 (com arredondamento e alguns testes); como VC++2005 (com SSE2); setando as flags do processador pra truncar (ruim porque exige esvaziar o pipeline, se os compiladores não geraram isso, deve ser lento, hehe); ou usar a instrução fisttp do SSE3 (sei que o meu Pentium 4 não a suporta; não tenho muita idéia do seu desempenho). Pra arredondar também deve haver mais algumas alternativas… Além de tudo isso poderia fazer experimentos setando as opções do compilador pra usar “fast math” e ver que código o compilador gera, mas daí eu não ia terminar nunca este post!!

Assembly, OCaml e o memory dump

7 Fevereiro, 2008

Hoje eu ganhei uns bons pontos no meu nível nerd (isso é bom ou ruim?), porque estava mexendo com umas rotinazinhas em assembly e medindo o desempenho com a instrução rdtsc. Mas me deparei com um comportamento bizarro ao fazer a subtração dos tempos final e inicial que não consegui entender. E só acontecia compilando como Release, e com variáveis volatile (usei volatile pra tentar evitar que o otimizador removesse o código que eu estava tentando medir…). Nos outros casos funcionava tudo como esperado. Eu até ia postar os detalhes aqui, mas era muita coisa, preciso fazer mais uns testes antes. Se alguém quiser mais detalhes do que eu estava tentando fazer, é só deixar um recado! (duvido que alguém queira)

Pra quem estava mexendo com linguagens funcionais, ir pra assembly é um baita pulo. O OCaml é legal, mas faltam alguns detalhes pra ficar massa mesmo. Tem idéias legais como o pattern-matching e currying, e o nível de abstração é super-alto, mesmo compilando pra código nativo. A sintaxe é mais ou menos. A biblioteca padrão tem coisas interessantes, mas tem umas porcarias. Quem é que vai usar uma tabela associativa implementada com listas ligadas? Ainda bem que também tem hashtables. E como é que têm coragem de colocar um operador (@) que pode estourar a pilha com chamadas recursivas se bastaria fazer uma implementação iterativa (ainda bem que dá pra baixar separadamente uma biblioteca pra consertar isso). Tenho que conferir o F# também (da Microsoft) que é baseado em OCaml…

(este post foi completamente desorganizado, é mais ou menos um memory dump do que eu estou pensando agora…)

A irritante Brasil Telecom

23 Janeiro, 2008

Todo mundo sabe que cancelar um serviço por telefone é um saco. Ainda mais se for a Brasil Telecom. Mas realmente eles se superam no quesito irritar o consumidor até não poder mais.

Segunda-feira eu liguei pra eles pra cancelar o meu Turbo 600 que eu estava achando muito caro pra essa velocidade. Liguei pra lá e entrei na opção de cancelamento. Isso às 19h15min. Fiquei ouvindo musiquinha intercalada com “em breve você será atendido” até as 19h55min. Isso dá 40 minutos esperando sem ser atendido! Que raiva. Se eu tivesse ligado pra assinar algum serviço, aposto que seria mais rápido. Ou pra ouvidoria da Brasil Telecom, segundo fiquei sabendo, o atendimento lá provavelmente seria melhor.

Eu já esperava que me ofereceriam milhares de promoções pra que eu continuasse com o serviço. Mas eu não queria continuar, já estava de saco cheio de ser cliente de uma empresa que atende tão mal os clientes e cobra caro. Tentei recusar educadamente todas as ofertas. Algumas eram boas e outras eram ridículas (diminuiriam o preço mas também a velocidade), mas eu nem prestei muita atenção.

Quando então parecia que o cancelamento ia se efetivar, a mulher que estava me atendendo me transferiu pra outro atendente, que repetiu toda a oferta de promoções. A essa altura eu já estava no MSN falando mal da Brasil Telecom e fiz questão de mencionar ao cara a propaganda ruim que eu estava fazendo. E quando diziam que o serviço do concorrente não era tão bom assim, eu respondia: “Tá uma maravilha aqui, muito melhor do que o serviço de vocês” :-D

Então, agora parecia que o segundo atendente ia realmente cancelar a conta… Mas ele resolveu insistir mais uma vez e eu não me agüentei. Eu já estava havia 50 minutos no telefone e ele ia oferecer mais uma “promoçãozinha”! Gritei com o cara:

— PUTA QUE PARIU, CANCELA ISSO DE UMA VEZ, TCHÊ!!

Silêncio.

Silêncio.

— Aguarde um momento que eu vou transferir a ligação.

Bom, talvez agora eu esteja indo direto pro cancelamento, eu pensei. Que nada. A mulher resolveu oferecer mais promoções. Dessa vez não fiquei sendo simpático por muito tempo…

— CANCELA LOGO ESSA BOSTA OU EU VOU CHAMAR O PROCON (( chamar o Procon sempre funciona :-P )). Eu nunca tive que gritar com um atendente de vocês, mas dessa vez vocês tiraram toda a minha paciência. Eu estou há quase uma hora aqui e vocês não cancelaram ainda!

— O cancelamento será efetivado em 24 horas e na próxima conta virá o valor apenas dos dias usados no mês.

Isso que eu já tinha ouvido essa frase do atendente anterior, mas parece que o outro tinha entrado em loop e resolvido oferecer mais promoções. Dessa vez, parece que foi verdade e a saga terminou. Total de aproximadamente 1h no telefone. Quem me conhece talvez não consiga me imaginar gritando com o atendente, mas às vezes isso é necessário… :-( E parece que funcionou. :-)

E pra não dizer que não falei de coisas boas, a Logitech tem um ótimo atendimento ao cliente. Um mause que começou a dar problema dentro da garantia (garantia de 5 anos, deu problema no 3.º ano — um clique era interpretado como dois) foi trocado facilmente, a comunicação foi toda por e-mail e a entrega foi super rápida, mesmo sendo perto da época de natal/ano-novo. E a troca de um mause novo mas problemático (andava sozinho) da Microsoft chegou quarta! Demorou um pouco mais que a Logitech, mas chegou! :-)

Linguagens funcionais

11 Janeiro, 2008

Eu ouvia falar com certa freqüência das tais linguagens funcionais e tinha decidido que eu devia aprender uma delas pra ver se era tudo isso mesmo.

Eu já tinha lido algumas coisas sobre Lisp e Scheme, mas não conseguia captar a essência da coisa. Me parecia simplesmente uma linguagem com notação prefixada e muitos, mas muitos parênteses mesmo! E também gostavam de usar recursão!

Um tempo depois, acabei entrando em contato com a linguagem Lua e aprendi muita coisa com ela (muita coisa mesmo!). Por exemplo, aprendi o que é “tratar funções como valores de primeira classe”, pois é possível criar funções com simples atribuições: my_func = function()…end. Com isso também é possível redefinir funções, passá-las como parâmetros e retornar funções. Esse último caso é interessante porque eu já tinha visto em Scheme, mas não tinha entendido muito bem por causa da sintaxe. Em Scheme não existe a palavra-chave return (ela é implícita no final de funções) e uma função anônima é definida com a esquisita palavra lambda. Em Lua é muito mais simples: return function()…end. Assim eu finalmente entendi o funcionamento dos closures: funções retornadas pra fora do seu escopo (léxico) e que continuam podendo acessar as mesmas variáveis de antes, mesmo depois do retorno da função que criou tais variáveis (portanto, elas precisam ser movidas pra fora da pilha, mas isso é um detalhe de implementação). E com tail-recursion aprendi que um comando return f() pode ser pensado como um simples goto f() porque não é necessário manter o stack frame da função que faz o return.

Alguns mistérios resolvidos sobre linguagens funcionais por causa da linguagem Lua! Já deve ter acontecido com você de aprender alguma coisa numa linguagem e então passar a entender melhor outra linguagem que você achava que já conhecia, né? Mas eu ainda queria aprender mais um pouco. Tentei ver de novo Lisp e Scheme, mas fora algumas poucas coisas, eu já não estava mais interessado nelas. Então resolvi procurar outras linguagens e dei uma olhada em Haskell. Aprendi conceitos legais, que nunca tinha visto antes, como currying, mas não cheguei a usar a linguagem: me assustei com toda essa “pureza funcional” (se bem que era exatamente isso que eu estava procurando no começo), separando a parte funcional da parte de entrada e saída (fazer debugging com prints deve ser meio incômodo, se não impossível). Deixei pra lá e fui aprender Python. Mas fiquei com outra linguagem na cabeça: OCaml, por ser funcional mas com recursos imperativos e segundo vários saites ela compila pra código nativo bastante eficiente! Quando encontrei um programa interessante em OCaml, resolvi dar uma estudada melhor pra aprender a criar extensões… (continua num próximo post, provavelmente).