Comparação entre Lua e JavaScript

Sexta-feira assisti a uma palestra sobre a linguagem Lua. Enquanto assistia, não pude deixar de notar a existência de paralelos entre as tabelas de Lua e os objetos de Javascript. Este artigo não procura ilustrar todas as diferenças entre as linguagens, apenas as que se referem a tabelas, objetos e a orientação a objetos a partir de protótipos.

Saiba mais

Publicidade

Ubuntu, você traiu o movimento

The nice thing about standards is that you have so many to choose from. – Andy Tanenbaum

Anarquia

De maneira geral, o ecossistema do software livre é anárquico. Falo isso não como crítica ­– tampouco como elogio –, mas sim como um fato. Como exemplo, veja os gerenciadores de pacotes: cada um com seu formato de arquivo e sua(s) interface(s) (gráfica ou não), cada qual com suas nomenclaturas e padronizações. Isso acontece quando a “comunidade” não consegue escolher um campeão e a utilização de um ou de outro torna-se uma questão de gosto.

Esse é um caso extremo, onde vários programas de funções similares disputam entre si o cinturão de Melhor Gerenciador de Pacotes™, e não há a menor interoperabilidade entre eles. Claro, existem ferramentas que convertem um .deb em um .rpm, mas esses programas só tiram proveito do fato que dois pacotes para um mesmo programa terão de armazenar de alguma forma os mesmos arquivos e metadados. À parte da conversão, um gerenciador funciona sem levar em consideração a existência de outros gerenciadores – ele não tentará descobrir se tal pacote está instalado naquele sistema, a não ser que ele mesmo o tenha instalado. Por isso, ter um sistema que conte com dois ou mais gerenciadores de pacote é pedir por problemas.

Mas nem sempre as coisas precisam ser assim complicadas…

Meritocracia

Às vezes aparece um campeão. Até a chegada do Chrome, o Firefox carregava o cinturão de Melhor Navegador Livre™. Isso não significa que não existiam alternativas, apenas que entre Konqueror, Epiphany e lynx, o Firefox era o favorito de nove entre dez pessoas.

Mas nem sempre as coisas são assim simples…

Diplomacia

Outra possibilidade é o surgimento de um protocolo de interoperabilidade. Não existe órgão capaz de verificar e obrigar todo desenvolvedor a seguir esses padrões, mas quando os padrões são sensatos, sua desobediência faz com que os usuários do programa infrator o olhem torto.

Talvez um dos melhores exemplos disso seja a variável XDG_CONFIG_HOME, que diz onde arquivos de configuração devem ser criados. Por padrão, essa variável tem como valor ~/.config e é aí que todos os programas deveriam criar seus arquivos de configuração. Quando algum programa não segue esse padrão, os usuários xingam muito no Twitter e na mailing list do projeto.

Mas nem sempre a vida é assim bela… Quando surge um padrão que não é bem aceito, o problema não é resolvido e continuamos na anarquia.

Notificações

Notificações deveriam ser um caso de diplomacia. Estabelecido o padrão de que a forma de comunicação entre processos é usando o d-bus (dando fim ao dcop utilizado pelo KDE), bastava apenas criar uma API padrão para a exibição de notificações. Essa API foi criada e, pasme, bem aceita. Sendo assim, agora eu posso criar um programa que use notificações (um gerenciador de Downloads que avise quando terminou de baixar seus pacotes, por exemplo) e ele irá exibir as notificações padrões do ambiente em que está, seja no KDE, no Gnome ou numa casinha de sapê.

Bem, talvez eu tenha faltado com a verdade quando disse que a API fora bem aceita. Visando ser a Apple do software livre, a Canonical achou por bem desenvolver um outro daemon de notificações, para unificar a experiência entre os desktops. Afinal, <sarcasmo>o Gnome, o KDE e o XFCE são tão parecidos, só faltava isso mesmo</sarcasmo>.

Superficialmente, esse novo daemon segue a API criada; porém, alguns parâmetros são ignorados. Por quê? Porque os desenvolvedores da Canonical se acham mais espertos que você, e que o controle deve ficar nas mãos deles. Se escrevi um pequeno verificador para minha caixa de entrada, qual o problema de que a duração da notificação seja de apenas um segundo? É mais do que suficiente para que eu possa reconhecer o ícone e saber do que se trata. No entanto, a Canonical acha melhor ignorar o parâmetro que determina o tempo de duração de uma notificação e usar um tempo absurdamente grande para esse tipo de notificação.

A solução? Um patch que acrescenta doze linhas ao código original. Não dói aplicá-lo e recompilar o programa, mas é um incômodo saber que esse problema poderia estar resolvido há dois anos.

Funções anônimas em C

Quem conhece funções anônimas (também conhecidas por expressões lambda) de outras linguagens, talvez já tenha sentido sua falta quando resolvendo certos problemas em C ou em C++.

A eventual chegada do padrão C++0x promete resolver esse problema, finalmente trazendo as funções lambda à linguagem. No entanto, isso se refere somente ao C++; mesmo o vindouro padrão C1X não faz referência a uma eventual chegada da ferramenta ao bom e velho C.

Porém, se seu projeto só será compilado usando o compilador GNU, uma boa parte de seus problemas pode ser resolvida com a utilização de um macro e de duas extensões. O uso dessas extensões significa que seu código deixará de seguir o padrão ISO e, portanto, deixará de ser portável. Se isso é um problema ou não, você que deverá julgar.

Saiba mais

Um pequeno serviço D-Bus em Python

Dentre os métodos para se fazer comunicação entre processos no Linux, o D-bus é provavelmente o que vem ganhando maior destaque recentemente. Aqui vou mostrar como implementar um serviço simples que poderá ser chamado de outros processos, demonstrando sua utilização a partir do Emacs; no entanto, nada impede que os métodos desse serviço sejam chamados de outra linguagem em que existam bindings para o D-Bus (o próprio Python, C/C++, C#, Perl, Ruby, PHP, Haskell, …). Aliás, é possível que em alguns casos seja mais simples fazer a integração entre processos escritos em linguagens diferentes através do D-Bus que usar foreign functions.

#!/usr/bin/env python2

import dbus
import dbus.service
from dbus.mainloop.glib import DBusGMainLoop
import gobject

class Greeter(dbus.service.Object):
    def __init__(self, Name=""):
        bus_name = dbus.service.BusName('org.local.greeter',\
                                            bus=dbus.SessionBus())
        dbus.service.Object.__init__(self, bus_name, '/%s' % Name)

    @dbus.service.method('org.local.greeter')
    def hello(self, Name="world"):
        print "Hello, %s!" % Name
        return "Hello, %s!" % Name 

DBusGMainLoop(set_as_default=True)
myGreeters = [Greeter("A"), Greeter("B")]

myLoop = gobject.MainLoop()

try:
    myLoop.run()
except KeyboardInterrupt:
    myLoop.quit()
    print "Bye!"

Começamos o arquivo importando os módulo necessários. Em vez de importar o módulo gobject, alguns tutoriais usam o módulo gtk. A função dos dois será a mesma: implementar o loop principal do serviço (tanto que o loop principal do gtk é implementado sobre o loop principal do gobject, apenas com alguns tratadores a mais). Como não iremos usar nenhum componente gráfico neste exemplo, não há a necessidade de se usar o gtk.

Em seguida criamos a classe que implementará o serviço, a classe Greeter. No construtor (__init__), definimos o nome do serviço — no caso, org.local.greeter —, e dizemos que ele é um Session Bus em vez de um System Bus (este reservado para serviços de mais baixo nível, como o HAL e o NetworkManager. No construtor também iniciamos o objeto, informando o nome que definimos anteriormente e lhe dando um caminho (path). O caminho é útil em casos onde se que o mesmo serviço dê acesso a vários objetos parecidos — por exemplo, o Emacs poderia dar acesso via D-Bus a cada um de seus buffers através do caminho /Emacs/Buffers/<Nome do Buffer>.

Depois, definimos um método. Note a presença do decorador (@dbus.service.method('org.local.greeter')). De certa forma, esse decorador estabelece que o método a seguir é “público”, ou seja, que pode ser acessado por processos externos. Para termos uma melhor organização do código, podemos criar outros métodos que não queremos que sejam acessíveis externamente; nesses casos, basta não usar o decorador. O método em si é bastante simples: ele imprime uma mensagem no terminal em que o serviço estiver rodando e retorna para o processo que o chamou essa mesma mensagem. Na prática, geralmente desejaremos que o serviço retorne alguma coisa em vez de imprimir, mas deixei assim para mostrar que existem as duas possibilidades.

Então temos o código para fazer o serviço executar. Criamos um novo DBusGMainLoop e o definimos como Loop padrão. Seguindo a documentação, atualmente não faz sentido criar mais de um loop, já que ainda não há suporte para isso (ao menos não no Python). Na linha seguinte são criados dois objetos da classe Greeter, cada um com um path diferente, /A e /B; fiz assim para demonstrar como isso é utilizado. E então criamos o loop principal e o executamos.

Do lado do cliente — no caso, o Emacs —, o código fica:

(require 'dbus)
(dbus-list-known-names :session)
(dbus-call-method :session "org.local.greeter"
                           "/A"
                           "org.local.greeter"
                           "hello"
                           "Emacs")

Caso se queira chamar o objeto /B, basta trocar o path na função dbus-call-methor.


Comparado ao COM do Windows, eu considero o D-Bus mais “limpo”. Mesmo quando se implementa um serviço COM em Python, sempre fico com a impressão de que certas coisas poderiam ser abstraídas de maneira melhor (CLSIDs, por exemplo).

Ainda assim, embora o D-Bus seja uma alternativa à altura (ou até melhor) que o COM, ele não substitui o DCOM. Nada impede que a comunicação entre processos seja feita entre diferentes máquinas, o problema é que, ao menos por enquanto, o D-Bus não implementa nenhum sistema de segurança — tanto de autenticação de usuário como de criptografia de dados.

Fonte

Calculando quadrados com somas

Esses dias, enquanto lia mensagens na UseNet [1], deparei-me com a seguinte sequência:

1² = 1 + 2.0 = 1
2² = 2 + 2.1 = 4
3² = 3 + 2(2+1) = 9
4² = 4 + 2(3+2+1) = 16
5² = 5 + 2(4+3+2+1) = 25

ou, genericamente:

n^2 = n + 2\sum_{i=0}^{n-1} i

A primeira coisa que me perguntei foi se isso é válido para qualquer n. Fiz um programa rapidinho em Haskell para calcular para mim:

square :: Integer -> Integer
square x = x + (2 * (foldl (+) 0 [1..(x-1)]))

test :: [Integer] -> Bool
test [] = True
test (x:xs) = if (square x) == (x*x)
              then test xs
                   else False

Testei de 1 a algum número bem grande. 100k ou 1M, não me lembro bem. O resultado foi True, significando que a equação é válida para pelo menos um número grande de ns. Ainda assim, fica a pergunta: é válida para qualquer n? E, a questão fundamental, por quê?

A prova por indução nos garante que sim, essa equação é válida para qualquer n, e também dá uma ideia de porque é válida. Se alguém conseguir fazer uma figura com a interpretação geométrica desse resultado, seria interessante.

\left(n+1\right)^2 = n^2 + 2n + 1 \\ \left(n+1\right)^2 = \left(n + 2\sum_{i=0}^{n-1} i\right) + 2n + 1 \\ \left(n+1\right)^2 = n + 1 + 2n + 2\sum_{i=0}^{n-1} i \\ \left(n+1\right)^2 = \left(n+1\right) + 2\left(n + \sum_{i=0}^{n-1} i\right) \\ \left(n+1\right)^2 = \left(n+1\right) + 2\sum_{i=0}^{(n+1)-1} i

[1] Sim, ela ainda existe e é bastante ativa.

Curiosidade: milhas, quilômetros e Fibonacci

Vejam a seguinte tabela. Isso lembra alguma coisa?

Milhas Quilômetros
3 5
5 8
8 13
13 21

Exatamente, são os números de Fibonacci. Claro que essa tabela de conversão não é exata, mas é uma aproximação muito boa ao resultado correto. No entanto, não há surpresa nenhuma; se calcularmos a razão entre dois números de Fibonacci consecutivos, teremos \frac{u_{k+1}}{u_k} = \varphi = 1.6180... , sendo \varphi a proporção áurea. Por acaso, esse valor é muito próximo à relação entre milhas (terrestres) e quilômetros; uma milha é igual a 1,609344 quilômetros.

Folha de São Paulo: malícia ou incompetência?

Ultimamente tenho notado uma tendência da Folha de São Paulo em responsabilizar tudo que acontece na Interwebs como fruto de “piratas virtuais”. Eu estaria disposto a considerar isso simples incompetência, não fosse a confusão que isso gera e a frequência com que acontece.

Aos iniciados nas artes da informática, essa “confusão” da imprensa tem uma desgostosa sensação de déjà vù. Com o início da propagação dos computadores pessoais, surgiram os hackers. Em inglês, o sufixo -er significa ”aquele que faz”, semelhante ao nosso “-eiro”: sapateiro, costureiro, … Já hack nada mais é que a nossa velha conhecida gambiarra. O motivo pelo qual hoje associamos a palavra hacker a malfeitores virtuais foi justamente a incompetência, ou talvez malícia, da mídia, que usava essa inocente palavra “aquele-que-faz-gambiarras” como sinônimo para ”aquele-que-quebra”, os crackers. Hoje em dia, corrigiu-se o problema usando a terminologia “white hat” e “black hat”. Os hackers bonzinhos são os de chapéu branco e os malvados são os de chapéu preto. Claro que na vida nada é tão simples assim e existem os “grey hats”, “red hats”, “blue hats”, “polka-dot hats”, …

Confusão similar faz a Folha ao usar o mesmo termo para designar quem infringe direitos autorais e hackers, seja lá qual for a cor de seu chapéu. É importante que o nome “pirata” esteja o mais livre possível de preconceitos em uma época em que o Partido Pirata sueco chegou ao parlamento e que o embrião de um partido pirata brasileiro começa a se formar. Mais importante ainda se nos lembrarmos de que o que está em jogo não é apenas o direito ou não de se baixar arquivos da internet, mas também uma legislação que englobe e entenda a Internet. Segundo a constituição brasileira, por exemplo, qualquer cidadão tem direito de se expressar livremente, salvo em anonimato (artigo 5º, inciso IV); uma demonstração de que mesmo algumas das leis básicas não são válidas na rede.

A Folha, ao tratar ambos como a mesma coisa, confunde e desinforma ao invés de cumprir seu papel como fonte de informação.

Gerador de Markov

Gerador de Markov

Um dos sites mais úteis que existem é o Gerador de Lero-lero. Não tenho certeza se o algoritmo usado é um Gerador de Markov mesmo, mas se não for, a idéia é bem parecida.

Gerador de Markov é um programa que analisa um texto procurando as combinações de palavras mais comuns e então gera um texto usando as mesmas combinações em um texto que é estatisticamente similar ao anterior. Como essa análise será feita depende da implementação. Eu escolhi usar combinações de 3 palavras, ou seja, uma palavra do texto gerado depende das duas anteriores a ela. Variando a quantidade de palavras usadas em cada combinação, altera-se o texto resultante: se eu escolhesse combinações de uma palavra, o texto gerado seria completamente aleatório, se eu escolhesse combinações de dez palavras, o texto resultante ficaria mais parecido com o original.

A implementação de um Gerador é relativamente simples, clique em “Ler o resto do artigo” para ter acesso ao código em Common Lisp. O código está bem dividido e cada função tem uma função clara (evidentemente).

Primeiramente, criamos uma hash-table que conterá as combinações de palavras. Usamos uma hash-table por ela ser mais eficiente. Se usássemos listas ou árvores, teríamos de fazer buscas toda vez que o Gerador fosse adicionar uma nova palavra ao texto. Logo no começo também, inicializamos o gerador de números aleatórios.

Para que possamos analisar as combinações de palavras, precisamos primeiro dividir a string em uma lista de strings. A função “space-position” se ocupa de descobrir onde fica o próximo “divisor de palavras”, ou seja, um espaço, um tab ou uma quebra de linha. O que vier antes será o delimitador. Se houver vários espaços em sequência, nós teríamos uma lista com algumas strings vazias, mas o “cond” na função “split-string” detecta isso com “(= pos 0)” e evita que isso aconteça.

Logo em seguida vem algumas funções auxiliares. “Last-two” retorna os dois últimos elementos de uma lista qualquer. Precisaremos dela mais tarde. “Random-elt” e “random-key” retornam um elemento aleatório de uma lista e uma chave aleatória de uma hash-table respectivamente. “List-triples” retorna os trios que podem ser formados de uma lista. Note que ela não gera todos os arranjos possíveis, apenas as combinações sequenciais. Imagine que existe um bloco com espaço para três “coisas”. Esse bloco percorre a lista sequencialmente gerando os trios. Assim, a lista ‘(1 2 3 4 5) seria percorrida como: ‘((1 2 3) 4 5), ‘(1 (2 3 4) 5) e ‘(1 2 (3 4 5)). Portanto, (list->triples ‘(1 2 3 4 5)) retornaria ‘((1 2 3) (2 3 4) (3 4 5)). “String->symbol” retorna um símbolo com o mesmo nome que o conteúdo da string passada como argumento. Não me pergunte porque eu preferi usar símbolos ao invés de strings mesmo. Talvez o gerador ficaria melhor assim, já que ele saberia diferenciar quando uma palavra está iniciando uma frase pela letra maiúscula, ou talvez isso não mudasse lá grande coisa.

“Insert-triple-into-table” é a função que faz o trabalho sujo: ela é a responsável por inserir os trios gerados por “list->triples” na hash-table que críamos no começo do programa. Ela é feia, não é uma função que me orgulho de ter escrito, mas ela funciona. Perceba que cada entrada na hash-table inicial é, por sua vez, outra hash-table. Essa segunda hash-table guarda uma lista com as possíveis palavras que podem ser usadas.

“Insert-string-into-table” organiza o trabalho de separar uma string em uma lista de strings, transformá-las em símbolos, gerar os trios e inserí-los na hash-table.

“Insert-file-into-table” foi uma função que deu trabalho escrever. Inicialmente, ela seria a função responsável por abrir um arquivo, salvá-lo como uma string e então passar para “insert-string-into-table”. Porém, a forma com que fiz isso era péssima, o consumo de memória ia para as alturas e o tempo de execução também. Em uma das tentativas, reimplementei boa parte da “insert-string-into-table” em “insert-file-into-table”, mas acabei deixando as duas funções mesmo assim, já que as duas são úteis. Depois, consegui implementar esta função segundo a idéia original com um desempenho muito melhor.

Finalmente, a parte interessante: “generate” gera um texto com no máximo o número específicado de palavras. Não há garantias de que ele gerará o número máximo, já que algumas combinações podem fazer com que ele não consiga gerar mais nada.

Só para me exibir, resolvi colocar alguns trechos que o Gerador criou tendo como base o texto deste artigo (um artigo recursivo):

“logo no começo também, inicializamos o gerador de markov mesmo, mas se não for, a idéia é bem parecida.”

“gerador de markov é um gerador de markov”

“se houver vários espaços em sequência, nãs teríamos uma lista e uma chave aleatória de uma lista e uma chave aleatória de uma lista com algumas strings vazias, mas o “cond” na função “split-string” detecta isso”

Eu o tenho usado nas conversas de MSN com sucesso. Ele detecta que a outra pessoa falou “oi :)” e gera o texto “olá =]” ou variação. Depois ele detecta a combinação “tudo bom?” e responde “sim, e contigo?”. Depois ele passa o comando para mim. OK, isso não é sério… ainda. Preciso aprender a fazer plugins para o Pidgin ainda (o bitlbee seria mais interessante nesse aspecto).

Saiba mais

Streams (listas infinitas) em C

Ultimamente tenho feito duas coisas: lido sobre Haskell e não postado no blog (além das outras coisas do dia-a-dia, claro).

Programas em Haskell são preguiçosos. Isso significa que quando uma variável é definida, ela só será computada quando seu valor for necessário. Assim, se fizermos um programa similar ao de baixo, fatorial de 1000 nunca será calculado, já que o seu valor não é necessário para calcular 1+1.

x = factorial 1000
putStrLn $ show (1+1)

Extrapolando essa mesma ideia* para outras definições, podemos definir uma lista de infinitos uns:

uns = repeat 1

Ou então com todos os números naturais:

naturais = [1..]

Com um pouco mais de perspicácia, é possível criar listas contendo todos os números de Fibonacci, ou então todos os fatoriais:

fibs = 1:1: (zipWith (+) fibs (tail fibs))
facs = 1:(zipWith (*) facs [1..])

Se quisermos 10 elementos de cada, fazemos:

dezFibs = take 10 fibs
dezFacs = take 10 facs

Essa implementação é bastante eficiente : se precisamos do fatorial de 10 e logo em seguida o de 11, não precisamos calcular 11×10×9×…×1, basta calcular 11×10!, que já havia sido computado. Melhor ainda : se uma hora precisarmos do fatorial de 7, ele já estará calculado, pronto para ser utilizado.

Agora sim a parte interessante, em que C entra nessa história. Embora minha implementação não seja nem de longe tão interessante como a utilizada pelo Haskell, ela serve de base para mostrar como uma implementação de stream ou listas infinitas pode ser feita em C.

A grande diferença entre listas e streams é que não trabalharemos com a idéia de um ponteiro para o próximo elemento, mas sim com uma expressão que ao ser executada nos dará o próximo elemento. Ou seja, ao invés de um ponteiro para outro elemento do mesmo tipo, nós temos um ponteiro para uma função.

typedef struct {
    int i;
    int (*cont)(int);
} stream;

Isso altera a forma como acessamos o próximo elemento. Não podemos mais utilizar o famoso ptr = ptr->prox porque estaremos apontando para uma função. Por isso, abstraí o processo de chamar a função com o valor da stream em uma função chamada proximo, que retorna uma nova struct com seu valor i atualizado pela função cont, o que dá a impressão de que avançamos na lista.

stream proximo(stream inicio)
{
    stream tmp;

    tmp.i = (*inicio.cont)(inicio.i);
    tmp.cont = inicio.cont;

    return(tmp);
}

Para exemplificar o uso de streams, criei a função pega que é similar à função take no Haskell, mas que no caso não retorna os valores, só os imprime.

void pega(int elementos, stream inicio)
{
    int i;
    stream atual = inicio;

    for(i = 0; i < elementos; i++)
    {
        printf("%d\n",atual.i);
        atual = proximo(atual);
    }
    printf(".\n.\n.\n\n");
}

Por fim, criei duas streams e duas funções auxiliares (que falta fazem expressões lambda). O código completo se encontra abaixo:

#include <stdlib.h>
#include <stdio.h>

typedef struct {
    int i;
    int (*cont)(int);
} stream;

stream proximo(stream inicio)
{
    stream tmp;

    tmp.i = (*inicio.cont)(inicio.i);
    tmp.cont = inicio.cont;

    return(tmp);
}

int soma_um(int i)
{
    return(i+1);
}

int repete(int i)
{
    return(i);
}

void pega(int elementos, stream inicio)
{
    int i;
    stream atual = inicio;

    for(i = 0; i < elementos; i++)
    {
        printf("%d\n",atual.i);
        atual = proximo(atual);
    }
    printf(".\n.\n.\n\n");
}       

int main(int argc, char *argv[])
{
    stream naturais, identidade;

    naturais.i = 1;
    naturais.cont = &soma_um;

    identidade.i = 1;
    identidade.cont = &repete;

    pega(10, naturais);
    pega(10, identidade);
    return 0;
}

*eu escrevi “idéia” mas aparentemente os verificadores ortográficos já aderiram todos às novas regras ortográficas… fica assim mesmo.

C: Hash tables, arrays associativos, dicionários…

Não sei quantos nomes existem para descrever esta forma de organizar séries de dados na memória, esses três do título foram as que eu consegui pensar em 5 segundos. :P

Enfim, comecemos pela forma mais simples de armazenar séries de dados na memória, arrays, seguiremos para listas, árvores, e então chegaremos às hash tables. Se tudo que você quer é um exemplo de implementação, vá ao fim do artigo.
Saiba mais