Fazer download em pdf ou txt
Fazer download em pdf ou txt
Você está na página 1de 39

Captulo 3

Uma Introduo Programao em Lua


Roberto Ierusalimschy
Abstract
Lua is a scripting language widely used in several areas, from desktop applications, such as Adobe
Photoshop Lightroom, to software for embedded systems. It is the leading language for scripting
games and it is part of Ginga, the standard middleware for the Brazilian Digital TV system. Lua is also
frequently used in security, being the scripting language embedded in tools like Wireshark, snort, and
nmap.
This text presents the Lua language emphasizing its unconventional mechanisms. The goal is to
introduce the language together with some non-conventional programming techniques, such as higher-
order functions, coroutines, and APIs between languages. The text assumes some programming
maturity from the reader and some knowledge about the C language, for the discussion about the
LuaC API.
Resumo
Lua uma linguagem de script amplamente usada nas mais diversas reas, desde grandes
aplicativos para desktops, como o Adobe Photoshop Lightroom, at software para sistemas embarca-
dos. Lua a linguagem mais usada atualmente para scripting em jogos, e parte do padro Ginga
para o Sistema Brasileiro de TV Digital. Lua tambm muito usada na rea de segurana, sendo a
linguagem de script embutida em ferramentas como Wireshark, snort e nmap.
Este texto apresenta a linguagem Lua com nfase nos seus mecanismos menos convencionais.
O objetivo introduzir a linguagem e ao mesmo tempo apresentar algumas tcnicas de programao
no convencionais, como o uso de funes de mais alta ordem, co-rotinas e APIs entre linguagens.
Espera-se do leitor alguma maturidade na rea de programao e conhecimento da linguagem C,
para a discusso da API entre Lua e C.
3.1. Introduo
O objetivo deste texto introduzir o leitor programao na linguagemLua.
Assumimos que o leitor (voc) possui uma certa maturidade em programao
com alguma linguagem qualquer.
1
R. Ierusalimschy
Programar em Lua no muito diferente de programar em outras lingua-
gens dinmicas, mas diferente. Cada linguagem apresenta caractersticas
prprias, e um bom programador sabe explorar as caractersticas particulares
de cada linguagem. Neste texto, vamos procurar enfatizar as particularidades
de Lua, aspectos que tornama programao em Lua diferente da programao
em outras linguagens dinmicas. Em particular, em Lua, temos como impor-
tantes diferenciais o uso de tcnicas de programao funcional, o uso ubquo
de tabelas como estruturas de dados para os mais variados ns, o uso de co-
rotinas e a comunicao com cdigo escrito em C.
Bom, ento programar em Lua no to diferente de programar em outras
linguagens dinmicas. Mas anal, o que uma linguagem dinmica? Como
ocorre frequentemente em computao, esse termo no possui uma denio
precisa e universalmente aceita. Mas existe um certo consenso de que lingua-
gens dinmicas apresentam as seguintes caractersticas:
Interpretao dinmica: isso signica que a linguagem capaz de executar
trechos de cdigo criados dinamicamente, no mesmo ambiente de exe-
cuo do programa. Como exemplos dessa facilidade temos a funo
loadstring em Lua e a funo eval em Scheme/Lisp e Perl.
Tipagem dinmica forte: tipagem dinmica signica que a linguagem faz veri-
cao de tipos em tempo de execuo do programa. Linguagens com
tipagemdinmica em geral no possuem declaraes de tipos no cdigo
e no fazem vericao de tipos em tempo de compilao. Tipagem
forte signica que a linguagem jamais aplica uma operao a um tipo
incorreto.
Gerncia automtica de memria dinmica (coleta de lixo): isso signica que
no precisamos gerenciar memria explicitamente no nosso programa;
em especial, no h necessidade de um comando para liberar memria
aps seu uso.
Em geral, linguagens dinmicas so interpretadas, e no compiladas para
cdigo nativo da mquina; mas essa uma caracterstica das implementaes
dessas linguagens, no das linguagens em si. Obviamente, as caractersti-
cas acima favorecem uma implementao via um interpretador e dicultam a
construo de compiladores.
Dessas caractersticas, a interpretao dinmica a mais exclusiva de lin-
guagens dinmicas. Obviamente, em qualquer linguagem Turing-completa po-
demos escrever um interpretador para a prpria linguagem, mas os trechos de
cdigo interpretados no sero executados no mesmo ambiente do programa
interpretador. Por exemplo, podemos escrever um interpretador para C em C,
mas os programas interpretados no tero acesso s variveis e funes de-
claradas no programa compilado onde o interpretador est sendo usado.
Apesar de no ser uma caracterstica exclusiva de linguagens dinmicas, a
gerncia automtica de memria um mecanismo importante dessa lista, por
haver uma enorme diferena entre programarmos em uma linguagem com e
2
Programao em Lua
em uma linguagem sem gerncia automtica de memria. Mesmo na progra-
mao em ponto grande (programming in the large) a gerncia automtica de
memria tem um impacto signicativo, ao simplicar as interfaces entre com-
ponentes. (Como um exerccio, pegue a API de qualquer biblioteca C de porte
razovel e verique quanto de sua complexidade devida gerncia de me-
mria.)
Na verdade, existe umcontnuo entre linguagens estticas e dinmicas. Por
exemplo, Java uma linguagemmuito mais dinmica do que C, pois apresenta
gerncia automtica de memria, um certo grau de tipagemdinmica e um me-
canismo embrionrio de interpretao dinmica (por meio da carga dinmica
de classes, que por sua vez podem ser criadas dinamicamente). Mesmo entre
as linguagens reconhecidamente dinmicas existem diferenas. Por exemplo,
nem todas as linguagens dinmicas tm gerncia automtica de memria so-
bre mdulos ou classes.
Lua se destaca de outras linguagens dinmicas por ser uma linguagem de
script. Uma linguagem de script uma linguagem projetada para controlar e
coordenar componentes geralmente escritos em outra linguagem. As primei-
ras linguagens de script foram as linguagens de shell do Unix, usadas para
conectar e controlar a execuo de programas. Apesar de vrias linguagens
dinmicas poderem ser usadas para script, poucas foram projetadas para essa
nalidade. Lua seguiu um caminho criado por Tcl [Ousterhout 1990], onde a
linguagem estruturada como uma biblioteca C com uma API que permite
tanto cdigo na linguagem chamar funes escritas em C como cdigo C cha-
mar funes escritas na linguagem. Lua se destaca de outras linguagens de
script por sua simplicidade, portabilidade, economia de recursos e desempe-
nho [Ierusalimschy et al. 2007].
3.2. Como usar Lua
A linguagem Lua conta com uma nica implementao principal, mantida
pelos autores da linguagem no site www.lua.org, mas essa implementao
conta com diversas distribuies mantidas por desenvolvedores independen-
tes.
Em muitos usos reais de Lua, o interpretador distribuido embutido na apli-
cao nal. Anal, um dos principais objetivos de Lua exatamente esse tipo
de uso. Nesses casos, detalhes de como usar Lua so dependentes da apli-
cao. Esses detalhes incluem que editor usar, onde e como armazenar os
programas, como executar um programa, etc. Neste texto, como no estamos
visando nenhuma aplicao particular, vamos usar o interpretador indepen-
dente (stand alone) de Lua.
Para mquinas Windows, uma tima opo de instalao a distribuio
Lua for Windows (LfW).
1
Essa distribuio um pacote completo para Win-
dows, incluindo no apenas o interpretador Lua com suas bibliotecas padro,
1
http://luaforwindows.luaforge.net/
3
R. Ierusalimschy
mas tambm um editor e vrias bibliotecas extras populares.
Para mquinas Linux no h uma receita pronta, dada a diversidade de
distribuies de Linux. Compilar Lua em uma mquina Linux muito simples
e rpido. Algumas distribuies j vm com Lua instalado por default. Outras
oferecem pacotes prontos: por exemplo, em Ubuntu e Debian, basta instalar
o pacote lua5.1, que o interpretador com as bibliotecas padro. Vrias
bibliotecas externas tambm so oferecidas como pacotes extras. Em qual-
quer caso, o interpretador independente de Lua um programa de linha de
comando, para ser executado por meio de um terminal.
Para mquinas Mac OS X, existe a opo de compilar diretamente o fonte,
desde que a mquina j tenha as ferramentas de desenvolvimento em C insta-
ladas. O processo simples e rpido como no Linux. Outra opo usar um
gerenciador de pacotes; por exemplo, tanto o MacPorts quanto o Fink oferecem
pacotes prontos para Lua.
Uma vez instalado, muito fcil usarmos o interpretador. Lua no tem o
conceito de uma funo main; qualquer comando passado ao interpretador
imediatamente executado. O exemplo a seguir um programa completo para
imprimir

2:
print(2^(1/2)) --> 1.4142135623731
O operador ^ o operador de exponenciao em Lua. Lua trabalha sempre
com nmeros reais em ponto utuante e a exponenciao funciona para ex-
poentes fracionrios (e negativos tambm). Em geral, usamos a notao -->
para indicar o resultado de um comando. Como em Lua dois traos -- iniciam
um comentrio que vai at o nal da linha, podemos incluir aquela indicao
no programa.
Ao usar Lua via um terminal, voc tem pelo menos quatro maneiras de
executar esse pequeno programa:
Voc pode usar a opo de linha de comando -e:
$ lua -e "print(2^0.5)"
(Estou assumindo que $ o prompt do terminal.)
Voc pode entrar com o programa em modo interativo:
$ lua
> print(2^0.5)
(O > o prompt do interpretador Lua em modo interativo.)
Voc pode escrever esse programa em um arquivo e execut-lo via linha
de comando:
$ lua nome-do-arquivo
4
Programao em Lua
Voc pode escrever esse programa em um arquivo e execut-lo via modo
interativo, por meio da funo predenida dofile:
$ lua
> dofile("nome-do-arquivo")
Podemos dispensar os parnteses em chamadas de funo onde o nico ar-
gumento uma string literal. Assim, voc pode reescrever o exemplo anterior
como a seguir:
> dofile"nome-do-arquivo"
Quando voc chama o interpretador, ele cria um estado Lua que persiste
at o m da sua execuo. Assim, todos os efeitos colaterais de cada comando
se propagam para os prximos comandos, mesmo que eles sejam executados
como programas em separado. Veja o exemplo a seguir:
$ lua
> x = 1
> print(x) --> 1
Cada uma das linhas executada como um trecho (chunk, em ingls) sepa-
rado, mas o valor da varivel global x se mantm aps o primeiro trecho ter
terminado.
3.3. Alguns Exemplos
primeira vista, Lua uma linguagem imperativa, razoavelmente con-
vencional. Como j discutimos, no vamos perder muito tempo descrevendo
essa parte mais convencional da linguagem. Uma boa parte voc vai apren-
der apenas vendo os exemplos; se precisar de maiores detalhes, consulte
o manual de referncia [Ierusalimschy et al. 2006] ou o livro Programming in
Lua [Ierusalimschy 2006].
Seguem alguns exemplos de funes simples em Lua, para voc se famili-
arizar com o bsico da linguagem.
Soma dos elementos de um array
function add (a)
local sum = 0
for i = 1, #a do sum = sum + a[i] end
return sum
end
Algumas observaes sobre o cdigo acima:
Como a linguagem tem tipagem dinmica, no h tipos nas declaraes
de variveis, parmetros, etc.
5
R. Ierusalimschy
A palavra reservada local declara uma varivel local, cujo escopo vai
da declarao at o m do bloco mais interno que contm a declarao.
No exemplo, sum visvel at o m da funo add.
A expresso #a retorna o comprimento do array a. Como arrays em Lua
comeam no ndice 1, o comprimento tambm o valor do ltimo ndice.
O comando for vai repetir seu corpo com o valor da varivel i vari-
ando de 1 at o comprimento do array (#a). A varivel de controle i
declarada pelo prprio comando for e s visvel dentro do seu corpo.
Todas as estruturas de controle tm um terminador explcito. Tanto o
corpo da funo quanto o corpo do for terminam com a palavra reser-
vada end.
Soma das linhas de um arquivo
A funo a seguir recebe o nome de um arquivo texto, que deve conter uma
lista de nmeros, e retorna a soma desses nmeros:
function addfile (filename)
local sum = 0
for line in io.lines(filename) do
sum = sum + tonumber(line)
end
return sum
end
Essa funo semelhante do exemplo anterior, com exceo do for. No
exemplo anterior, usamos um for numrico, que iterage sobre uma progresso
aritmtica de nmeros. Neste exemplo, usamos um for genrico, que usa um
gerador (io.lines, no caso, fornecida pela biblioteca padro de Lua) para
gerar os valores da iterao.
Note tambm o uso da funo tonumber, para converter o numeral lido
(uma string) para um nmero. Lua faz esse tipo de converso automaticamente
sempre que uma string usada em uma operao aritmtica, mas considera-
mos mais educado efetuar a converso explicitamente.
Casamento de prexos
Um problema comum emvrias reas , dada uma lista de palavras, decidir
se uma dada string prexo de alguma das palavras da lista. Por exemplo, mui-
tos sistemas de linha de comando permitem que entremos com um comando
digitando apenas os primeiros caracteres do nome do comando.
Uma soluo usual para esse problema construir uma tabela de prexos,
que mapeia todos os prexos de cada palavra na lista para a palavra completa.
Dada essa tabela, o problema original resolvido com uma simples consulta.
6
Programao em Lua
function buildPrefixTable (list)
local t = {}
for _, name in ipairs(list) do
for len = 1, #name do
local prefix = string.sub(name, 1, len)
if t[prefix] then
t[prefix] = true -- colisao
else
t[prefix] = name
end
end
end
return t
end
Figura 3.1. Funo para construir uma tabela de prexos.
A funo na Figura 3.1 recebe uma lista (array) de palavras e retorna sua
tabela de prexos. Nesse cdigo temos novamente vrias novidades:
A expresso {} cria uma tabela vazia, que atribuida varivel local t.
O lao externo usa o gerador ipairs, que percorre todos os ndices
e valores do array dado (list). Os ndices so atribudos primeira
varivel (que nomeamos _, j que no estamos interessados no seu
valor), e os valores segunda varivel (name).
A funo string.sub retorna uma substring de uma dada string (name,
no caso). Assim como arrays, caracteres em strings so indexados a
partir de 1. Em particular, a chamada como feita no exemplo vai retornar
um prexo de name com comprimento len.
Na condio do if, usamos o fato de que o valor nil, que o valor
de campos no inicializados em uma tabela, equivalente a falso em
qualquer condio. Assim, o que est sendo testado se o campo da
tabela com chave prefix j foi preenchido anteriormente.
No caso do teste dar positivo (isto , a tabela j ter um elemento com a
dada chave), a funo coloca o valor true na posio j ocupada. Como
este valor no uma string, ele serve como uma marca para colises.
2
Aps a construo da tabela de prexos, seu uso bem simples. Dado um
2
Outra opo seria colocar na posio do conito uma lista com todas as possveis pala-
vras para o dado prexo.
7
R. Ierusalimschy
prexo, o cdigo a seguir retorna a palavra completa ou d um erro adequado:
function complete (t, prefix)
local w = t[prefix]
if type(w) == "string" then return w
elseif w == true then error("ambiguous prefix")
else error("invalid prefix")
end
end
A funo predenida type, quando aplicada a qualquer valor, retorna uma
string com seu tipo. Os valores de retorno possveis so "nil", "number",
"string", "boolean", "table", "function", "thread" e "userdata".
J vimos, pelo menos brevemente, os tipos number, string, table e nil (que o
tipo do valor nil). Iremos abordar os tipos function e thread mais a frente. O
tipo userdata usado para representar objetos externos a Lua (e.g., arquivos).
Isso deixa faltando apenas o tipo boolean.
Como em outras linguagens, o tipo boolean em Lua tem apenas dois va-
lores, true e false. Mas os valores booleanos no tm exclusividade para
testes. Em qualquer teste da linguagem(if, while e mesmo operadores lgi-
cos) os valores nil e false resultam em um teste negativo, e qualquer outro
valor (incluindo true, mas tambm 0, a string vazia, etc.) resulta em um teste
positivo. Antes de sua verso 5.0, Lua nem tinha um tipo booleano. A principal
motivao para a incluso desse tipo na linguagem foi permitir a distino en-
tre variveis com valor falso (false) e variveis no inicializadas (e portanto
com valor nil). Um teste como if not x d positivo nos dois casos, mas um
teste como if x == false s d positivo se o valor de x for false.
Em Lua, assim como em vrias outras linguagens dinmicas, booleanos
no tm exclusividade como resultado de operadores lgicos. O operador or
retorna sempre o valor do primeiro operando que dene o valor nal da disjun-
o. A seguir listamos algumas expresses e seus respectivos resultados:
5 or 7 --> 5
nil or 7 --> 7
nil or false --> false
false or nil --> nil
De forma anloga, o operador and retorna o valor do primeiro operando que
dene o valor nal da conjuno:
"a" and "b" --> "b"
nil and "alo" --> nil
nil and false --> nil
false and nil --> false
O operador not, entretanto, sempre retorna um booleano. Em particular, a ex-
presso not not x normaliza o valor de x para um booleano correspondente.
8
Programao em Lua
Um exemplo de uso dessas propriedades a expresso a seguir, que cal-
cula o mximo entre x e y:
x >= y and x or y
Voc est convidado a explicar como ela funciona.
3.4. Programando com Funes
Funes em Lua so valores dinmicos de primeira classe. Em Lua, no
declaramos funes, de forma esttica. Funes so criadas dinamicamente,
durante a execuo de um programa.
Considere o exemplo a seguir:
> (function (a,b) print(a+b) end)(10, 20)
Esse comando cria uma funo para imprimir a soma de seus dois parmetros,
e imediatamente chama essa funo com argumentos 10 e 20. Aps a execu-
o do comando, no h mais referncias para a funo, e portanto os recursos
usados por ela (memria) sero eventualmente reciclados pelo coletor de lixo.
De modo geral, a sintaxe function (<pars>) <body> end representa
uma expresso que, ao ser executada, cria uma nova funo. Ela equiva-
lente a notao (lambda (<pars) <body>) da linguagemScheme e a uma
abstrao no -clculo.
Como funes so valores de primeira classe, podemos fazer com elas
tudo que fazemos com outros valores, como nmeros ou strings. Em particular,
podemos armazenar seu valor em uma varivel global:
> foo = function (a,b) print(a+b) end
> foo(10, 20)
Agora, podemos usar a funo armazenada emfoo quantas vezes quisermos.
Como bastante comum querermos armazenar funes em variveis glo-
bais para uso futuro, Lua oferece um acar sinttico para a atribuio acima.
Podemos escrev-la na forma a seguir:
> function foo (a,b) print(a+b) end
Essa sintaxe exatamente a que estivemos usando at agora para declarar
funes. Ela passa uma aparncia de normalidade linguagem, por ser bas-
tante semelhante declarao de uma funo em outras linguagens. Lembre-
se, no entanto, que ela nada mais do que acar sinttico. O signicado
9
R. Ierusalimschy
dessa declarao criar uma funo dinamicamente e atribu-la a uma vari-
vel global.
3.4.1. Mltiplos Retornos
Outro aspecto pouco convencional de funes em Lua que elas podem
retornar mltipos valores. Considere o exemplo a seguir:
function foo (x) return x, x+1 end
print(foo(3)) --> 3 4
Nesse exemplo, a funo foo retorna dois valores, x e x+1. Na segunda
linha, todos os retornos de foo so passados como argumentos para a funo
print.
Outra forma de se acessar os mltiplos retornos de uma funo com uma
atribuio mltipla:
a, b = foo(10)
Nessa atribuio, a recebe o primeiro valor retornado por foo e b recebe o
segundo.
Em uma atribuio mltipla, Lua no exige que o nmero de valores seja
igual ao nmero de variveis. Se houver mais valores, os valores extras so
descartados; se houver mais variveis, as variveis extras recebem o valor
nil. O mesmo ocorre em uma chamada de funo, em relao aos argu-
mentos fornecidos na chamada e os parmetros esperados pela funo: se
chamamos uma funo com mais argumentos que o necessrio, os argumen-
tos extras so descartados; se houver argumentos faltando, eles assumem o
valor nil.
De maneira geral, em todos os lugares que Lua aceita uma lista de ex-
presses (passagem de parmetros, atribuio mltipla, construo de listas,
retornos mltiplos) podemos usar uma funo com mltiplos retornos como l-
timo (ou nico) elemento da lista. Todos os valores retornados se juntam ao
nal dos valores das outras expresses. Entretanto, quando uma chamada de
funo no a ltima expresso da lista, apenas um valor de retorno dela
usado, mesmo que ela retorne vrios valores. Por exemplo, na atribuio
a, b, c = foo(10), foo(20)
a recebe o valor 10, b recebe o valor 20 e c recebe 21 (segundo retorno de
foo(20)). O segundo retorno da primeira chamada a foo, 11, descartado.
Essa regra garante um mnimo de ordemna correspondncia entre expresses
e valores. Por exemplo, em uma chamada como f(g(x),y), a regra garante
que o argumento y ser o segundo argumento, mesmo que g(x) retorne mais
de um resultado.
10
Programao em Lua
O exemplo a seguir ilustra uma funo que recebe uma string e retorna
como resultados todos os prexos da string:
function prefixes (s, len)
len = len or 0
if len <= #s then
return string.sub(s, 1, len),
prefixes(s, len + 1)
end
end
print(prefixes("alo")) --> a al alo
Na verdade, a funo recebe uma string e um nmero len, e retorna todos os
prexos da string com tamanho maior ou igual a len. Esse segundo parmetro
tem um valor default de zero; com isso, quando chamamos a funo com ape-
nas um parmetro (a string), recebemos de volta todos os seus prexos com
tamanho maior ou igual a zero, ou seja, todos.
A primeira linha da funo implementa o valor default para len. Quando
chamamos a funo pela primeira vez, passando apenas a string, o parmetro
len recebe o valor nil. Assim, o resultado do or na primeira atribuio 0,
que se torna o novo valor do parmetro. Nas chamadas recursivas, len recebe
um valor numrico; portanto, o resultado do or o prprio valor de len, e a
atribuio no altera seu valor.
O resto da funo razoavelmente simples. Se o limite de tamanho for
maior que o comprimento da string, no h nada a ser retornado. Caso con-
trrio, a funo retorna o prexo com tamanho len, calculado com a funo
string.sub (que j vimos anteriormente), seguido dos prexos com tama-
nho maior que len, calculados por meio da chamada recursiva.
3.4.2. Regies Geomtricas
Alm de serem valores de primeira classe, funes em Lua oferecem es-
copo lxico. Isso permite empregarmos em Lua vrias tcnicas de progra-
mao funcional, como por exemplo o uso de funes para representao de
dados. Para darmos uma pequena ilustrao sobre essas tcnicas, vamos de-
senvolver aqui um exemplo simples.
O exemplo a seguir baseado em [Hudak and Jones 1994]. A ideia de-
senvolver um sistema para representao de regies geomtricas. Uma regio
um conjunto de pontos, e a nica operao relevante no nosso sistema
pertinncia: dado um ponto e uma regio, saber se o ponto est dentro da
(pertence ) regio. Para desenharmos uma regio, podemos percorrer a rea
de desenho testando a pertinncia de cada pixel. O sistema deve ser capaz
de representar uma ampla gama de regies geomtricas, e deve ser capaz de
representar unio, interseo e complemento de regies genricas.
11
R. Ierusalimschy
Para desenvolver esse sistema, podemos comear pesquisando quais es-
truturas de dados se adequam melhor ao problema. Ou podemos subir o nvel
de abstrao e ignorar as estruturas de dados, representando uma regio di-
retamente por meio de sua funo caracterstica. Mais especicamente, uma
regio pode ser representada por uma funo que, dado um ponto, retorna ver-
dadeiro se e somente se o ponto pertence regio. Como exemplo, a funo
a seguir representa uma regio circular com centro (1.0, 3.0) e raio 4.5:
return function (x, y)
return (x - 1.0)^2 + (y - 3.0)^2 <= 4.5^2
end
Essa funo bastante simples: ela apenas verica se a distncia do ponto
dado ao centro do crculo menor ou igual ao raio.
Podemos ser mais abstratos e denir uma funo para criar regies circu-
lares:
function circle (cx, cy, r)
return function (x, y)
return (x - cx)^2 + (y - cy)^2 <= r^2
end
end
Essa funo apenas retorna uma funo que representa a regio circular com
centro e raio dados.
Para o funcionamento correto desse tipo de funo, fundamental que a
linguagem oferea visibilidade lxica. No nosso caso, a funo que representa
o crculo deve ser capaz de acessar as variveis locais funo externa (cx,
cy e r) mesmo aps esta ter retornado.
Para adicionarmos novos formatos, como reas retangulares ou triangula-
res, no h necessidade de nenhuma modicao no sistema; basta escre-
vermos as funes adequadas. Mas o mais interessante como podemos
modicar e combinar regies. Por exemplo, dada qualquer regio g, a funo
a seguir cria uma nova regio que o complemento de g:
function complement (g)
return function (x, y) return not g(x, y) end
end
Unio, interseo e diferena de duas regies so igualmente simples; vide
Figura 3.2. Alm de operaes sobre conjuntos, tambm podemos denir v-
rios outros tipos de transformaes grcas. Por exemplo, a funo a seguir
12
Programao em Lua
function union (g1, g2)
return function (x, y)
return g1(x, y) or g2(x, y)
end
end
function inter (g1, g2)
return function (x, y)
return g1(x, y) and g2(x, y)
end
end
function diff (g1, g2)
return function (x, y)
return g1(x, y) and not g2(x, y)
end
end
Figura 3.2. Unio, interseo e diferena de regies.
translada uma gura:
function translate (g, dx, dy)
return function (x, y)
return g(x + dx, y + dy)
end
end
Observe como todas as funes que denimos necessitam visibilidade lxica,
ao retornarem funes que necessitam acesso a valores externos para funcio-
narem corretamente.
Como comentamos anteriormente, podemos visualizar as regies geradas
percorrendo a rea de desenho e testando cada pixel. Para ilustrar o processo
de uma forma simples, vamos escrever uma funo para gerar um arquivo PBM
(portable bitmap) com o desenho de uma dada regio.
Arquivos PBM tmumformato bastante simples
3
: emsua variante de modo
texto, ele comea com umcabealho de uma linha contendo a sequncia "P1";
em seguida h uma linha com a largura e a altura do desenho, em pixels. Final-
mente, h uma sequncia de algarismos com os valores de cada pixel da ima-
gem (1 para preto, 0 para branco), separados ou no por brancos ou quebras
de linha. A funo na Figura 3.3 gera o arquivo para uma regio dada, mape-
ando os pixels da rea sendo desenhada [1, M] [1, N] para o espao virtual
3
Esse formato tambm bastante ineciente, mas aqui nossa nfase a simplicidade.
13
R. Ierusalimschy
local M, N = 500, 500 -- tamanho do desenho
function plot (f)
io.write("P1\n", M, " ", N, "\n")
for i = 1, N do -- percorre as linhas
local y = (N/2 - i)
*
2/N
for j = 1, M do -- percorre as colunas
local x = (j - M/2)
*
2/M
io.write(f(x, y) and "1" or "0")
end
io.write("\n")
end
end
Figura 3.3. Funo para desenhar uma regio no formato PBM.
Figura 3.4. Desenho da diferena de duas regies circulares
de desenho [1, 1] [1, 1]: A iterao externa percorre as linhas do desenho,
que correspondem coordenada vertical; para cada linha a iterao interna
percorre suas colunas, que correspondem coordenada horizontal. Observe o
uso idiomtico dos operadores lgicos para transformar o resultado da funo
caracterstica (um booleano) no caractere apropriado (0 ou 1).
A Figura 3.4 ilustra o resultado do comando a seguir:
plot(diff(circle(0, 0, 1), circle(0.3, 0, 1)))
3.4.3. Desenho de Polgonos
Um exemplo interessante do uso de funes em Lua vem da biblioteca
NCLua, que a interface entre Lua e o middleware Ginga, da TV Digital Bra-
sileira. Uma das funes oferecidas por essa interface a drawPolygon,
para desenhar um polgono em uma rea de desenho [ABNT 2007]. Um dos
problemas de funes para desenhar polgonos que a representao de um
14
Programao em Lua
polgono geralmente requer uma estrutura de dados especca, que pode no
ser compatvel com a estrutura usada pelo programa. A funo drawPolygon
de NCLua evita essa diculdade por meio de funes. Quando chamamos essa
funo passamos apenas dois parmetros: a tela (canvas) onde desenhar e o
modo de desenho: aberto, fechado ou preenchido. Essa funo ento retorna
uma nova funo, que adiciona pontos ao polgono. Chamando repetidas ve-
zes essa funo retornada, podemos adicionar todos os pontos do polgono
sem necessidade da criao de uma estrutura de dados particular. Quando
chamamos essa funo sem parmetros, indicamos que no h mais pontos a
inserir.
Para ilustrar o uso dessa funo, o cdigo a seguir desenha um polgono
armazenado em um array de pontos, onde cada ponto uma estrutura com
dois campos, x e y:
local f = drawPolygon(canvas, "close")
for i = 1, #points do
f(points[i].x, points[i].y)
end
f()
Na primeira linha, chamamos drawPolygon, que cria um novo polgono e re-
torna a funo para adicionar pontos a esse polgono. Em seguida, fazemos
uma iterao sobre o array points para adicionar os pontos ao polgono. Fi-
nalmente, chamamos a funo de adio sem argumentos, para indicar que
no h mais pontos a serem inseridos.
Uma facilidade adicional de drawPolygon que cada chamada funo
de adio de pontos retorna a prpria funo. Isso permite uma sintaxe simples
para criarmos polgonos pequenos, via um encadeamento de chamadas. O
cdigo a seguir ilustra esse uso:
drawPolygon(c, "fill")(1.5,2.3)(3.6,4.5)(0.4,9.7)()
No difcil adaptarmos uma funo para desenhar polgonos com uma
interface convencional para essa interface funcional. Por exemplo, suponha
que temos uma funo como a descrita a seguir:
DP(canvas, mode, n, x, y)
Nessa funo, n o nmero de pontos do polgono, x um array com as co-
ordenadas horizontais dos pontos e y um array com suas coordenadas verti-
cais. Com essa funo, podemos implementar a nova interface por meio do c-
digo mostrado na Figura 3.5. Quando chamada, nossa funo drawPolygon
cria dois arrays vazios (list_x e list_y) e um contador n, e retorna a fun-
o de adio de pontos, que executa o grosso do trabalho. Se chamada sem
argumentos, ela invoca a primitiva DP para desenhar o polgono acumulado.
Caso contrrio, ela verica se os argumentos possuem o tipo correto e insere
um novo ponto na sua estrutura de dados. Essa estrutura, que uma particu-
laridade da funo DP, ca completamente escondida do resto do programa.
15
R. Ierusalimschy
function drawPolygon (canvas, mode)
local list_x, list_y, n = {}, {}, 0
return function (x, y)
if x == nil and y == nil then
DP(canvas, mode, n, list_x, list_y)
elseif type(x) ~= "number" then
error("x deve ser um numero")
elseif type(y) ~= "number" then
error("y deve ser um numero")
else
n = n + 1
list_x[n] = x
list_y[n] = y
end
end
end
Figura 3.5. Uma implementao para drawPolygon so-
bre uma interface convencional.
3.4.4. Trechos de Cdigo
Como discutimos anteriormente, a unidade de execuo de cdigo em Lua
chamada de trecho. Vamos ver agora mais detalhes sobre como Lua trata
trechos de cdigo.
Antes de executar um trecho de cdigo, Lua pr-compila o trecho para um
formato interno. Esse formato uma sequncia de instrues para uma m-
quina virtual, algo semelhante ao cdigo de mquina para uma CPU conven-
cional. Para essa pr-compilao, Lua trata o trecho exatamente como se ele
fosse o corpo de uma funo annima. Alm disso, o resultado da compilao
uma funo Lua, com todos os direitos de qualquer funo.
Normalmente, Lua executa a funo annima correspondente a um tre-
cho imediatamente aps sua compilao, de modo que essa fase de pr-
compilao ca transparente para o programador. Mas podemos ter acesso
a esse passo quando necessrio. Em especial, a funo loadstring compila
um trecho de cdigo arbitrrio e retorna a funo resultante, sem execut-la.
Veja o exemplo a seguir:
i = 0
f = loadstring("print(i); i = i + 1")
f() --> 0
f() --> 1
O fato de trechos serem compilados como funes permite algumas tcni-
cas teis. Em particular, trechos podem ter variveis locais. Como Lua oferece
16
Programao em Lua
visibilidade lxica, essas variveis so acessveis para as funes declaradas
dentro do trecho, mas so invisveis fora dele. Em particular, uma funo ar-
mazenada em uma varivel local de um trecho s visvel dentro daquele
trecho. Lua inclusive oferece um acar sinttico para declararmos funes
locais. Veja o exemplo a seguir:
local function foo (x)
print(2
*
x)
end
Essa declarao equivalente ao cdigo a seguir:
local foo
foo = function (x)
print(2
*
x)
end
Uma outra opo de traduo seria assim:
local foo = function (x)
print(2
*
x)
end
Mas essa traduo no to conveniente quanto a primeira opo. (Voc sabe
explicar por que? Oque ocorre nos dois casos se quisermos denir uma funo
recursiva?)
Como qualquer funo, trechos de cdigo tambm podem retornar valores.
Isso til em algumas situaes particulares. Por exemplo, o interpretador in-
dependente de Lua imprime qualquer valor retornado por um trecho executado
em modo interativo. Alm disso, ele substitui um sinal de igual no incio de uma
linha por um return. Assim, podemos imprimir o resultado de uma expresso
em modo interativo iniciando a linha com um sinal de igual:
$ lua
> = 2^-3 --> 0.125
Lembre-se que cada linha em modo interativo tratado como um trecho com-
pleto.
Em Lua, a denio de funes uma operao feita em tempo de execu-
o, no em tempo de compilao. Anal, ela apenas acar sinttico para
uma atribuio. Assim, quando pr-compilamos um trecho de cdigo contendo
denies, essas denies no so vlidas at executarmos a funo resul-
tante da compilao. Veja o exemplo a seguir:
f = loadstring("function foo (x) print(10
*
x) end")
print(foo) --> nil
f() -- executa trecho
print(foo) --> function: 0x807ad58
foo(10) --> 100
17
R. Ierusalimschy
Mais especicamente, a funo loadstring nunca gera nenhum efeito cola-
teral. Seu nico efeito retornar uma nova funo correspondente ao trecho
de cdigo passado como parmetro. Qualquer efeito do trecho s executado
quando (e se) chamamos a funo retornada.
3.5. Programando com Tabelas
Lua oferece um nico mecanismo para estruturao de dados, chamado
tabela. Tabelas nada mais so do que arrays associativos, isto , uma estrutura
de dados que associa chaves com valores e permite um rpido acesso ao valor
associado a uma dada chave.
Tabelas so, provavelmente, a caracterstica mais marcante de Lua. Muitas
outras linguagens, em especial linguagens dinmicas, oferecem arrays asso-
ciativos, mas nenhuma os usa de modo to extensivo quanto Lua. Em Lua
usamos tabelas para implementar estruturas de dados como arrays, estrutu-
ras (registros), conjuntos e listas, e tambm para implementar conceitos mais
abstratos como objetos, classes e mdulos.
A semntica bsica de tabelas bastante simples. A expresso {} cria
uma tabela vazia e retorna uma referncia para ela. Uma atribuio t[x]=y
associa o valor y chave x na tabela referenciada por t. E a expresso t[x]
retorna o valor associado chave x na tabela referenciadapor t, ou o valor nil
caso a tabela no contenha a chave dada. Analogamente, se atribuimos nil a
uma chave, eliminamos aquela chave da tabela: as chaves de uma tabela so
aquelas com um valor associado diferente de nil.
J vimos alguns usos de tabelas nos exemplos das sees anteriores. Um
dos primeiros exemplos deste texto, de uma funo para somar os elementos
de um array, na verdade usa uma tabela. Lua no tem arrays. O que cha-
mamos de array em Lua meramente uma tabela cujas chaves so nmeros
naturais. Lua usa um algoritmo que garante que tabelas usadas como arrays
so efetivamente armazenadas internamente como arrays. Essa implementa-
o completamente transparente para o programador; no existe nada na
linguagem que dependa dessa implementao, com exceo do desempenho
de certas operaes.
Para manipularmos tabelas como estruturas, Lua oferece um acar sint-
tico bastante simples: a notao t.x equivalente a t["x"], isto , a tabela t
indexada pela string literal "x". O exemplo a seguir ilustra esse uso:
t = {}
t["x"] = 10; t.y = 20;
print(t.x, t["y"]) --> 10 20
Note que, nessa sintaxe, s podemos usar como nomes de campos identica-
dores vlidos na linguagem. Por exemplo, a expresso t.or invlida, pois
or uma palavra reservada em Lua. Obviamente, a sintaxe bsica t["or"]
sempre vlida.
18
Programao em Lua
3.5.1. Construtores
A expresso para criar uma tabela vazia, {}, na verdade um caso parti-
cular de um construtor. Construtores oferecem uma sintaxe bastante rica para
a criao e inicializao de tabelas em Lua. Existem basicamente trs tipos de
construtores: um para listas, outro para estruturas e um genrico.
O construtor de listas tem a forma a seguir:
{exp1, exp2, exp3, ...}
Essa expresso cria uma tabela com o valor de exp1 associado ao ndice 1,
exp2 associado ao ndice 2, e assim por diante. O trecho a seguir ilustra um
uso simples desse tipo de construtor:
dia = {"domingo", "segunda", "tera", "quarta",
"quinta", "sexta", "sbado"}
print(dia[5]) --> quinta
O construtor de estruturas tem a forma a seguir:
{nome1 = exp1, nome2 = exp2, nome3 = exp3, ...}
Essa expresso cria uma tabela com o valor da expresso exp1 associado
string "nome1", exp2 associado string "nome2", e assim sucessivamente.
O trecho a seguir ilustra um uso simples desse tipo de construtor:
point = {x = 10.5, y = -15.34}
print(point.x) --> 10.5
O construtor genrico tem a forma a seguir:
{[e1] = exp1, [e2] = exp2, [e3] = exp3, ...}
Essa expresso cria uma tabela com o valor de exp1 associado ao valor da ex-
presso e1, exp2 associado ao valor da expresso e2, e assim por diante. Em
particular, qualquer construtor de listas pode ser reescrito na forma a seguir:
{ [1] = exp1, [2] = exp2, [3] = exp3, ...}
De forma similar, construtores de estruturas tambm podem ser reescritos
usando-se o construtor genrico:
{ ["nome1"] = exp1, ["nome2"] = exp2, ...}
(Certique-se que voc entendeu por que essas equivalncias esto corretas.)
Apesar de sua genericidade, o construtor genrico menos usado que
os dois anteriores, pois a maior parte dos usos tpicos se encaixa naqueles
padres. Uma situao particular onde ele til quando chaves do tipo string
19
R. Ierusalimschy
no so identicadores vlidos, como no exemplo a seguir:
op = {["+"] = "add", ["-"] = "sub",
["
*
"] = "mul", ["/"] = "div"}
Um construtor tambm pode usar uma mistura desses trs formatos. Por
exemplo, considere o construtor a seguir:
{23, "ho", op = 10, ot = "a", ["or"] = 30}
Ele criar uma tabela com o valor 23 na chave 1, "ho" na chave 2, 10 na
chave "op", "a" na chave "ot" e 30 na chave "or". (Como or uma palavra
reservada em Lua, ela no pode ser usada como um identicador.)
Quando escrevemos uma chamada de funo onde o nico argumento
um construtor, o uso dos parnteses opcional. Isso d um aspecto mais de-
clarativo quando usamos Lua para descrio de dados, como neste fragmento:
character{"merlin",
image = "files/img/merlin.jpg",
strength = 100.5,
category = wizard
}
Essa descrio na verdade uma chamada funo character passando
como argumento uma tabela com a string "merlin" no ndice 1 mais os outros
campos com chaves explcitas.
3.5.2. Arrays
Como j comentamos, em Lua representamos arrays diretamente como
tabelas, usando nmeros inteiros positivos como ndices.
O uso de tabelas como arrays traz diversos benefcios. Por exemplo, Lua
manipula arrays esparsos
4
de forma bastante eciente, sem necessidade de
algoritmos especiais. Se temos uma tabela t vazia, uma atribuio como
t[1000000000]=1 insere apenas um elemento na tabela, o par com chave
1000000000 e valor 1. (O mesmo programa em Perl d erro de Out of me-
mory!) Alm disso, todas as operaes oferecidas pela linguagem para tabe-
las se estendem naturalmente para arrays.
Quando manipulamos arrays, frequentemente necessrio sabermos seu
tamanho. Em algumas linguagens estticas o tamanho de um array parte
do tipo do array (e.g., Pascal); em outras, responsabilidade do programador
saber o tamanho (e.g., C). Em linguagens mais dinmicas, comum existir
um operador para se consultar o tamanho de um dado array. Como j vimos,
Lua oferece o operador de comprimento (o operador prexado #), que quando
aplicado a um array retorna o seu comprimento. Mas o que o comprimento
de um array em Lua?
4
Arrays onde a grande maioria dos elementos tm valor nil ou zero.
20
Programao em Lua
Como arrays so na verdade tabelas, o conceito de comprimento (ou ta-
manho) no claro em todos os casos. Por exemplo, qual o tamanho de um
array esparso? Seu nmero de elementos? Seu ltimo ndice? Para simpli-
car essas questes, Lua dene o comportamento do operador de comprimento
apenas para arrays bemcomportados. Se os nicos ndices numricos presen-
tes em uma tabela t so inteiros consecutivos de 1 at algumn, ento esse n
o resultado da expresso #t; se a tabela no tem nenhumndice numrico, en-
to o resultado da expresso #t zero. Em todos os outros casos, o resultado
de #t no denido univocamente.
Em particular, arrays com buracos, isto , com elementos com valor nil,
no so bem comportados. Considere o construtor a seguir:
{10, 20, nil, 40}
Para ns, pode parecer bvio que queremos um array de quatro elementos,
onde o terceiro tem valor nil. Mas para Lua, entretanto, no existe um ter-
ceiro elemento. No claro se o array termina neste nil e por acaso tem um
outro elemento desrelacionado no ndice 4. O operador de comprimento, se
aplicado ao resultado desse construtor, pode retornar 2 ou 4. O ideal evitar
esses casos, usando o valor false em vez de nil na tabela. Se isso no for
possvel, deve-se usar algum artifcio para indicar o tamanho do array, como
por exemplo armazenar esse tamanho explicitamente:
{10, 20, nil, 40, n = 4}
Para arrays bem comportados, o operador # bastante til. A atribuio a
seguir ilustra uma construo idiomtica muito comum em Lua:
t[#t + 1] = v
Ela anexa o valor v ao nal da lista t. De forma anloga, a atribuio a seguir
apaga o ltimo elemento de uma lista:
t[#t] = nil
3.5.3. Palavras mais Frequentes
Vamos ver agora um exemplo de um pequeno programa completo, que
ilustra vrios conceitos que vimos at agora. O objetivo do programa listar as
n palavras mais frequentes em um dado texto [Bentley et al. 1986].
O algoritmo geral desse programa bastante simples. Primeiro percorre-
mos todas as palavras do texto, contando quantas vezes cada uma aparece.
Para isso, mantemos uma tabela que associa cada palavra j vista com o n-
mero de vezes que ela apareceu. Em seguida, ordenamos o resultado por or-
dem decrescente de frequncias e listamos os n primeiros elementos da lista.
A Figura 3.6 mostra o cdigo completo do programa. Na primeira linha, lemos o
arquivo de entrada todo de uma vez como uma nica string (com a opo
*
all
para a funo de leitura io.read), e armazenamos o resultado em t. Isso
21
R. Ierusalimschy
local t = io.read("
*
all")
local count = {}
for w in string.gmatch(t, "%w+") do
count[w] = (count[w] or 0) + 1
end
local words = {}
for w in pairs(count) do
words[#words + 1] = w
end
table.sort(words, function (a,b)
return count[a] > count[b]
end)
for i=1, (arg[1] or 10) do
print(words[i], count[words[i]])
end
Figura 3.6. Programa para listar as palavras mais fre-
quentes em um texto.
22
Programao em Lua
bastante razovel para arquivos comat algumas dezenas de megabytes. Mais
adiante vamos ver como tratar arquivos realmente grandes.
Em seguida, criamos a tabela count, para armazenar a frequncia de cada
palavra, e percorremos todas as palavras do texto, contando-as. Para esse per-
corrimento, usamos um gerador baseado em um padro: o lao ser repetido
para cada substring de t que case com o padro "%w+". Esse padro sig-
nica uma sequncia de um ou mais caracteres alfanumricos, que nossa
denio de palavra. (Esse padro similar ao padro "\w+" de Perl, por
exemplo; em Lua usamos o caractere % como escape nos padres para evitar
conito com o signicado de \ em strings.) Note o uso idiomtico do conectivo
or no corpo do lao: se a palavra w j tem um contador, o resultado da dis-
juno esse contador; caso contrrio, o resultado zero. Em qualquer caso,
o valor incrementado e atribudo como a nova contagem associada quela
palavra.
O prximo passo ordenar as palavras. Isso um pouco mais sutil do que
parece. No podemos ordenar diretamente a tabela count, pelo simples fato
de que tabelas no tm ordem; elas apenas mapeiamchaves para valores. Por
isso, para impormos uma ordenao sobre as palavras, precisamos coloc-las
em uma lista.
5
Assim, criamos uma nova tabela (chamada words) e inserimos
nela todas as palavras presentes como chave na tabela count.
Para ordenar a lista words, usamos a funo predenida table.sort. O
segundo parmetro dessa funo a funo usada por sort para comparar os
valores sendo ordenados. Essa funo recebe como parmetros dois valores
sendo ordenados, e retorna verdadeiro se e somente se o valor do primeiro
parmetro deve preceder o valor do segundo parmetro na ordem nal. No
nosso caso, os valores so as palavras sendo ordenadas, e a funo ento
consulta a tabela count para comparar qual tem maior frequncia.
Finalmente, no ltimo lao imprimimos as n palavras mais frequentes, que
so as primeiras da lista ordenada, e suas respectivas frequncias. O valor de
n pode ser dado como um argumento na chamada do programa; o interpre-
tador independente armazena esses argumentos em uma tabela global arg.
Quando no fornecido um valor, o programa usa um valor preestabelecido
(10, no exemplo).
Como comentamos anteriormente, a tcnica de ler o arquivo inteiro para
posterior tratamento bastante eciente para arquivos no muito grandes. Mas
para arquivos grandes (da ordemde centenas de megabytes ou mais) ela pode
se tornar invivel. Nesse caso, podemos modicar o incio do programa para
5
Algumas pessoas se confundem com essa ideia: anal, se listas tambm so tabelas,
como orden-las? Realmente listas tambm no tm nenhuma ordem interna, mas suas
chaves, inteiros positivos, tm uma ordenao natural independente da lista. Em ou-
tras palavras, do ponto de vista de Lua uma lista no tem ordem, mas denimos que o
elemento associado chave n o n-simo elemento.
23
R. Ierusalimschy
percorrer o arquivo linha a linha, e para cada linha percorrer suas palavras:
local count = {}
for line in io.lines() do
for w in string.gmatch(lines, "%w+") do
count[w] = (count[w] or 0) + 1
end
end
3.5.4. Mdulos
A combinao de tabelas com funes de primeira classe bastante po-
derosa. Lua no oferece nenhum mecanismo especco para a construo de
mdulos, pois mdulos em Lua podem ser diretamente implementados como
tabelas.
Quase todas as bibliotecas padro de Lua so implementadas como mdu-
los via tabelas. Nesse texto j usamos vrias funes dessas bibliotecas. Por
exemplo, a funo string.sub denida na biblioteca de manipulao de
strings. Essa biblioteca exporta todas as suas funes dentro de uma tabela,
armazenada na varivel global string. Quando escrevemos string.sub,
isso nada mais que uma indexao convencional:
print(type(string)) --> table
print(type(string.sub)) --> function
Normalmente, um mdulo denido por meio de um trecho de cdigo Lua
armazenado em um arquivo.
6
Lua indiferente em relao a como um mdulo
escrito, desde que sua execuo resulte na criao de uma tabela global
contendo os itens exportados pelo mdulo. Por razes que veremos mais adi-
ante, tambm educado o mdulo retornar sua tabela de exportaes. Como
ilustrao, o trecho de cdigo a seguir dene um mdulo vector exportando
duas funes, norm1 e norm2.
vector = {}
function vector.norm1 (x, y)
return (x^2 + y^2)^(1/2)
end
function vector.norm2 (x, y)
return math.abs(x) + math.abs(y)
end
return vector
Observe que Lua oferece um acar sinttico para denirmos funes direta-
mente como campos em tabelas.
6
Tambm podemos denir mdulos para Lua em C.
24
Programao em Lua
Para carregar um mdulo, podemos simplesmente execut-lo, por exemplo
com a funo predenida dofile. Entretanto, Lua oferece uma funo bem
mais conveniente para a carga de mdulos, chamada require. A funo
require difere de um simples dofile em dois aspectos importantes:
Ela mantm uma lista de mdulos j carregados, de modo que requerer
um mdulo j carregado no o carrega novamente.
Ela usa uma lista de lugares onde procurar o arquivo contendo o mdulo;
com a funo dofile temos que especicar o caminho completo at o
arquivo.
A funo require retorna como resultado o valor retornado pelo trecho
que criou o mdulo. Assim, se o trecho retorna a tabela de exportao, essa
tabela ser retornada quando chamarmos require para carregar o mdulo.
Isso permite o idioma a seguir:
local v = require("vector")
Esse idioma permite usarmos um outro nome para o mdulo no nosso cdigo
(v, no exemplo), alm de evitar o uso de globais.
3.5.5. Objetos
Programao orientada a objetos (OO) em Lua tambm se vale da combi-
nao de tabelas com funes de primeira classe. Em Lua, um objeto mera-
mente uma tabela, contendo campos com seus dados (variveis de instncia)
e operaes (mtodos).
O exemplo a seguir ilustra uma primeira abordagem com um objeto bem
simples:
Rectangle = {x = 0, y = 0, width = 10, height = 20}
function Rectangle.area ()
return Rectangle.width
*
Rectangle.height
end
Uma diculdade bvia com esse esquema que o mtodo area s opera
para esse retngulo particular. Para o mtodo ser til para qualquer retngulo,
podemos incluir o objeto como parmetro do mtodo:
function Rectangle.area (self)
return self.width
*
self.height
end
Agora, para chamarmos o mtodo, temos que passar o objeto como argu-
mento, o que no muito conveniente:
print(Rectangle.area(Rectangle))
25
R. Ierusalimschy
Para resolver esse inconveniente, Lua oferece o operador de dois pontos (colon
operator ). Com esse operador, podemos reescrever a chamada anterior da
seguinte forma:
print(Rectangle:area())
O operador de dois pontos automaticamente insere o receptor do mtodo como
um primeiro argumento adicional na chamada.
De forma anloga, tambm podemos usar o operador de dois pontos na
denio do mtodo, como ilustrado a seguir:
function Rectangle:area ()
return self.width
*
self.height
end
Nesse caso, o operador insere automaticamente um primeiro argumento adici-
onal na denio do mtodo, com o nome self.
O uso do parmetro self, mais o suporte sinttico do operador de dois pon-
tos, possibilita que ummesmo mtodo possa operar sobre vrios objetos seme-
lhantes. Mas como criamos esses objetos? Emparticular, partindo da denio
de Rectangle, como podemos criar outros retngulos?
Uma opo seria copiarmos todos os campos do objeto original nos novos
objetos; isto , clonarmos o objeto original. Essa opo no muito convi-
dativa quando o objeto oferece muitos mtodos. Uma outra opo, bem mais
interessante, exige um novo mecanismo de Lua, chamado delegao, que foi
inspirado na linguagem Self [Ungar et al. 1987].
Delegao permite que uma tabela herde campos de outra tabela. Mais
especicamente, suponha que uma tabela A delega sua indexao para ou-
tra tabela B. Se indexamos A com uma chave presente, o valor associado
retornado normalmente. Mas se indexamos A com uma chave ausente, Lua
automaticamente ir procurar essa chave na tabela B.
Para construirmos uma relao de delegao entre A e B precisamos de
uma tabela intermediria, chamada de metatabela de A.
7
Para a tabela A dele-
gar suas buscas para B, o campo __index de sua metatabela deve referenciar
B. O exemplo a seguir deve ajudar a claricar essas relaes:
A = {x = 10}
B = {x = 20, y = 30}
mt = {__index = B} -- metatabela
print(A.x, A.y) --> 10 nil
setmetatable(A, mt)
print(A.x, A.y) --> 10 30
7
Delegao em Lua uma instncia especca de um mecanismo mais geral chamado
metamtodos, que justica o uso de metatabelas. Neste texto vamos discutir apenas
delegao.
26
Programao em Lua
Rectangle = {x = 0, y = 0, width = 10, height = 20}
Rectangle.__index = Rectangle
function Rectangle:new (o)
setmetatable(o, self)
return o
end
function Rectangle:area ()
return self.width
*
self.height
end
r = Rectangle:new{width = 40, height = 60}
print(r:area()) --> 2400
Figura 3.7. Classe Rectangle usando delegao.
A chamada funo setmetatable estabelece mt como a metatabela de
A. Podemos trocar a metatabela de uma tabela a qualquer hora (chamando a
funo setmetatable), assim como podemos trocar o contedo do campo
__index (via atribuies convencionais). No momento do acesso a um campo
ausente de uma tabela, o valor corrente do campo __index de sua metatabela
corrente ir indicar a quem delegar o acesso. Se a metatabela ou seu campo
__index no existir, o acesso retorna o valor nil, que o comportamento
normal de acesso a um campo ausente.
Nada nos obriga a criarmos uma terceira tabela para ser a metatabela. No
exemplo anterior, tanto A quanto B poderiam ser a metatabela de A. Adotando
a segunda alternativa, o cdigo caria assim:
A = {x = 10}
B = {x = 20, y = 30}
B.__index = B
setmetatable(A, B)
print(A.x, A.y) --> 10 30
Na Figura 3.7 juntamos tudo que temos at agora para denir novamente
um objeto Rectangle. Desta vez, esse objeto vai oferecer um mtodo new
para criar novos retngulos, e usar delegao para que esses novos retngulos
possam usar os mtodos denidos no objeto original.
Vamos acompanhar, em detalhes, o que ocorre quando Lua executa as
duas ltimas linhas desse fragmento. Na primeira, o programa cria uma tabela
e a passa como argumento para a funo Rectangle.new. Devido notao
de dois pontos tanto na chamada quanto na sua denio, essa funo recebe
27
R. Ierusalimschy
um outro parmetro, self, com o valor de Rectangle. Essa funo apenas
atribui o valor de Rectangle como metatabela do novo objeto e o retorna.
Na prxima linha chamamos o mtodo area nesse novo objeto. Lembre-
se que r:area() equivalente a r.area(r). Ou seja, o primeiro passo
acessar o campo area na tabela r. Como essa tabela no tem esse campo, o
interpretador consulta sua metatabela para uma alternativa. A metatabela de r
Rectangle, que tambm o valor do seu campo __index. Assim, a funo
Rectangle.area se torna o resultado do acesso r.area. Lua ento chama
essa funo, passando r como argumento extra para o campo self.
3.6. Programando com Co-rotinas
Co-rotinas so um tipo de mecanismo de controle bastante poderoso, mas
pouco convencional. Como sempre, o termo em si no tem um signicado
preciso. Vrios mecanismos de controle, muitos no equivalentes entre si, re-
cebem o nome de co-rotinas.
Genericamente, o termo co-rotina se refere a um mecanismo que permite
que um procedimento suspenda temporariamente sua execuo e continue
mais tarde. Esse mecanismo bsico permite diversas variaes:
Co-rotinas podem ser valores de primeira classe, isto , co-rotinas po-
dem ser manipuladas livremente e reinvocadas em qualquer ponto do
programa, ou podem haver restries sobre seu uso.
O controle de uxo pode ser simtrico ou assimtrico. O controle sim-
trico tem uma nica primitiva para transferir o controle entre co-rotinas; o
assimtrico tem duas primitivas, uma para suspender a execuo e outra
para reiniciar.
Co-rotinas podem ser suspensas enquanto executando outras funes
(com pilha, ou stackful ) ou apenas enquanto executando sua funo prin-
cipal. [de Moura and Ierusalimschy 2009]
Em particular, Lua oferece um mecanismo de co-rotinas assimtrico, de
primeira classe e com pilha [de Moura et al. 2004]. Nesta seo, vamos apre-
sentar esse mecanismo e mostrar alguns de seus usos.
Co-rotinas, como implementadas por Lua, so bastante similares a linhas
de execuo (threads) cooperativas. Cada co-rotina em Lua representa uma
linha de execuo independente, com sua prpria pilha de chamadas
8
. Mas,
ao contrrio de umsistema multithreading convencional, no h preempo em
um sistema de co-rotinas. Uma co-rotina s interrompe sua execuo quando
termina ou quando invoca explicitamente uma primitiva de suspenso (yield).
A funo coroutine.wrap cria uma co-rotina e retorna uma funo que,
8
Por isso classicada como stackful.
28
Programao em Lua
ao ser chamada, executa (resume) a co-rotina.
9
O parmetro nico para wrap
uma funo Lua contendo o cdigo do corpo da co-rotina:
co = coroutine.wrap(function () print(20) end)
co() --> 20
Essa funo pode opcionalmente ter parmetros, cujos valores so dados na
chamada co-rotina:
co = coroutine.wrap(function (x) print(x) end)
co("alo") --> alo
Como j vimos, o poder de co-rotinas vem da possibilidade de uma co-
rotina suspender sua execuo para continuar posteriormente. Para isso, ela
deve chamar a funo yield, como no exemplo a seguir:
co = coroutine.wrap(function (x)
print(x)
coroutine.yield()
print(2
*
x)
end)
co(20) --> 20
Note que, ao ser chamada, a co-rotina nesse exemplo executou apenas at a
chamada a yield. Mas ela no terminou sua execuo, apenas a suspendeu.
Ao ser chamada novamente, ela ir continuar do ponto onde parou:
co() --> 40
A funo yield tambm pode passar um valor de retorno para o ponto
onde a co-rotina foi invocada:
co = coroutine.wrap(function ()
for i = 1, 10 do coroutine.yield(i) end
return "fim"
end)
print(co()) --> 1
print(co()) --> 2
...
print(co()) --> 10
print(co()) --> fim
O exemplo anterior, apesar de simples e de pouca utilidade, ilustra a es-
9
Tambm podemos criar uma co-rotina com a funo coroutine.create, que no
vamos tratar neste texto.
29
R. Ierusalimschy
sncia da construo de geradores, que est intimamente ligada construo
de iteradores.
3.6.1. Iteradores e Geradores
Uma questo recorrente em programao a construo de iteradores:
estruturas de controle para percorrer os elementos de uma estrutura de da-
dos em uma determinada ordem. Tradicionalmente, existem duas maneiras de
se estruturar iteradores, chamadas de exportao de dados e importao de
aes [Eckart 1987].
Exportao de dados se baseia em geradores: funes que, cada vez que
so chamadas, retornamumprximo elemento da estrutura de dados (segundo
alguma ordem). Essa a forma mais usada em Java, por exemplo, por meio
da interface Iterator.
Importao de aes usa funes de mais alta ordem: nesse esquema,
uma funo de iterao recebe uma funo como parmetro e a aplica a todos
os elementos da estrutura de dados. Essa a forma mais usada em Ruby,
por exemplo (apesar de Ruby no usar funes de mais alta ordem, mas um
mecanismo especialmente dedicado a esse m). A vantagem dessa forma de
iteradores que muito mais fcil manter o estado da iterao: considere, por
exemplo, o percorrimento de uma rvore em pr-ordem. A desvantagem que
o lao de iterao no pode ser modicado, o que diculta algumas tarefas:
considere, por exemplo, o percorrimento de duas estruturas em paralelo.
Existe um problema famoso que ilustra simultaneamente os problemas das
duas abordagens, chamado problema das bordas iguais (same-fringe pro-
blem). O problema consiste em, dadas duas rvores, determinar se o percorri-
mento das folhas das duas rvores produz a mesma sequncia de elementos.
Com exportao de dados, usando geradores, difcil percorrer cada rvore,
pois no temos recurso para manter o estado do percorrimento. Com impor-
tao de aes, usando funes de iterao, difcil percorrer as duas rvores
em paralelo.
O uso de co-rotinas elimina essa dicotomia entre as duas abordagens, pois
torna trivial a transformao de uma funo de iterao em um gerador. Para
ilustrar essa transformao, vamos resolver o problema das bordas iguais.
Percorrer as folhas de uma rvore uma tarefa bem simples, se usarmos
recurso. O cdigo na Figura 3.8 implementa um iterador via importao de
aes. Esse iterador recebe a ao como uma funo (f), e aplica essa funo
a cada folha da rvore a seguindo a ordem da esquerda para a direita.
Construir o iterador foi bem fcil; mas, como comentamos, um iterador no
muito til para o nosso problema. No temos como percorrer duas rvores
emparalelo usando esse iterador, j que o lao de percorrimento est embutido
no iterador, e percorre apenas uma nica rvore.
Portanto, vamos tranformar esse iterador em um gerador. Tudo que temos
a fazer especicar yield como a funo de visita e executar a funo de
percorrimento dentro de uma co-rotina:
30
Programao em Lua
function leaves (a, f)
if a ~= nil then
if a.left or a.right then
leaves(a.left, f)
leaves(a.right, f)
else -- folha
f(a.value)
end
end
end
Figura 3.8. Funo para percorrer as folhas de uma r-
vore binria.
function gen (a)
return coroutine.wrap(function ()
leaves(a, coroutine.yield)
end)
end
A funo gen, quando chamada, retorna uma funo que cede as folhas da
rvore dada uma a uma, na ordemcorreta; ou seja, um gerador. Com poucas
linhas de cdigo transformamos um iterador por importao de aes em um
iterador por exportao de dados.
Agora, ca bem fcil resolver o problema das bordas iguais:
function samefringe (a, b)
local gen_a, gen_b = gen(a), gen(b)
repeat
local a, b = gen_a(), gen_b()
if a ~= b then return false end
until a == nil
return true
end
A funo samefringe cria um gerador para cada rvore dada, e percorre as
duas em paralelo comparando os elementos gerados.
10
3.6.2. Co-rotinas Simtricas
A forma de co-rotinas usada por Lua, chamada de assimtrica, oferece
duas primitivas para passagem de controle entre co-rotinas: resume, que
10
Essa funo se vale de uma sutileza das regras de escopo de Lua. Note que a varivel
local a, declarada dentro do lao, ainda visvel na sua condio.
31
R. Ierusalimschy
local main = coroutine.wrap(function () end)
local next
current = main
transfer = function (co, val)
if current ~= main then
next = co
return coroutine.yield(val)
else
current = co
while current ~= main do
next = main
val = current(val)
current = next
end
return val
end
end
Figura 3.9. Implementao de transfer em Lua.
invocada implicitamente quando chamamos a co-rotina, e yield. Existe uma
outra forma de implementarmos co-rotinas, chamada de simtrica. Essa se-
gunda forma mais conhecida por alguns programadores, devido a seu uso na
linguagem Modula-2 [Wirth 1982].
Co-rotinas simtricas utilizam uma nica primitiva de controle de uxo, tra-
dicionalmente chamada de transfer. Essa primitiva simultaneamente sus-
pende a execuo da co-rotina em execuo e transfere o controle para uma
outra co-rotina qualquer, dada como parmetro na chamada a transfer.
A maioria das implementaes de co-rotinas assimtricas no so com pi-
lha (e.g., geradores em Python). Por essa razo, algumas pessoas tendem a
achar que co-rotinas assimtricas so menos expressivas que co-rotinas sim-
tricas. Mas isso no verdade. Em particular, podemos implementar a funo
transfer em Lua, como veremos agora.
Como um transfer simultaneamente suspende uma co-rotina e ativa ou-
tra, podemos implement-lo usando uma combinao de um yield, para sus-
pender uma co-rotina, seguido de umresume, para ativar a outra. O cdigo da
Figura 3.9, adaptado de [de Moura and Ierusalimschy 2009], implementa essa
ideia. Na primeira linha, o cdigo cria uma nova co-rotina para representar o
uxo de execuo principal, de modo a ser possvel voltarmos o controle para
esse uxo. Em seguida, declara uma varivel local next, que ser usada pela
transfer, e uma varivel global current, que ir sempre conter a co-rotina
32
Programao em Lua
ping = coroutine.wrap(function ()
while true do
print(ping)
transfer(pong)
end
end)
pong = coroutine.wrap(function ()
while true do
print(pong)
transfer(ping)
end
end)
transfer(ping)
Figura 3.10. Um pequeno programa com co-rotinas simtricas.
que estiver em execuo. Finalmente, temos a denio de transfer.
Quando a funo transfer chamada pela co-rotina principal, ela entra
em um lao e invoca a co-rotina destino (co). A partir da, qualquer co-rotina
que chame transfer ir colocar o valor da co-rotina destino na varivel next
e ceder o controle, que voltar ao lao. Se a co-rotina destino for a principal,
o lao termina e a co-rotina principal continua sua execuo. Caso contrrio, o
lao repete e invoca a co-rotina destino (current).
A Figura 3.10 apresenta um pequeno programa que ilustra o uso da fun-
o transfer. Apesar de sua simplicidade, o programa apresenta uma tpica
arquitetura produtorconsumidor.
3.7. A API LuaC
Como comentamos anteriormente, um dos pontos fortes de Lua sua faci-
lidade para se comunicar com C. Nessa seo, vamos ver como essa comuni-
cao feita.
Lua foi projetada para se comunicar com C. Isso to importante que Lua
organizada como uma biblioteca em C, no como um programa. O programa
lua, que temos usado ao longo do texto para executar os exemplos, na ver-
dade um pequeno programa com menos de 400 linhas de cdigo que um
cliente da biblioteca Lua. Essa biblioteca exporta pouco menos que 100 fun-
es, que permitem executarmos trechos de cdigo Lua, chamarmos funes,
registrarmos funes C para serem chamadas por Lua, manipularmos tabelas,
e outras operaes bsicas.
Sempre que manipulamos uma API como a de Lua, importante lembrar
que trabalhamos com dois nveis de abstrao simultneos. Por um lado, existe
33
R. Ierusalimschy
o nvel do programa em C, que estamos escrevendo. Por outro lado, existe o
nvel do programa em Lua que est sendo manipulado por esse programa C.
Por exemplo, existe uma funo na API para consultar o valor de uma varivel
global. Quando chamamos essa funo, estamos executando uma chamada
de funo, no nvel C, mas estamos consultando uma global no nvel Lua.
Uma considerao importante na implementao de Lua o tamanho e a
portabilidadedo cdigo. Lua comumente usada em plataformas bastante no
convencionais, como consoles de jogos, conversores (set-top box) para TVs,
cameras fotogrcas, etc. Para diminuir seu tamanho e aumentar sua porta-
bilidade, o cdigo de Lua dividido em trs partes: o ncleo, uma biblioteca
auxiliar e as bibliotecas padro.
O ncleo contm toda a parte bsica de Lua, como o pr-compilador, o in-
terpretador, os algoritmos de manipulao de tabela e de coleta de lixo. Porm,
ele no assume nada sobre o sistema operacional. Por exemplo, o ncleo faz
toda sua alocao de memria chamando uma funo externa, que deve ser
fornecida a ele na sua inicializao. Da mesma forma, o ncleo no l arquivos,
mas carrega trechos de cdigo chamando uma funo externa apropriada. A
API do ncleo totalmente denida no arquivo lua.h; todos os nomes deni-
dos por essa API tem o prexo lua_.
A estrutura do ncleo bastante apropriada para aplicaes embarcadas,
rodando em plataformas no convencionais que muitas vezes nem dispem
de um sistema operacional. Para plataformas mais convencionais, entretanto,
ela demasiadamente detalhista. Para isso existe a biblioteca auxiliar. Essa
biblioteca usa a API do ncleo e funes normais do sistema operacional para
oferecer uma interface de mais alto nvel para o programador. Essa API auxi-
liar denida no arquivo lauxlib.h; todos os nomes denidos por essa API
tem o prexo luaL_. Neste texto vamos usar a biblioteca auxiliar sempre que
necessrio, pois assumimos estar programando em uma plataforma convenci-
onal.
A comunicao LuaC bi-direcional. Por um lado, Lua pode chamar fun-
es que na verdade esto escritas em C. Por exemplo, todas as bibliotecas
padro de Lua, como para manipulao de strings e para manipulao de ar-
quivos, so escritas em C. Por outro lado, tambm bastante fcil C chamar
funes escritas em Lua. Isso permite que partes de um programa escrito em
C sejam congurveis por meio de cdigo Lua.
Uma distino til entre embutir (embed) e estender uma linguagem de
script. Embutir usar a linguagem como uma biblioteca C dentro de um pro-
grama hospedeiro, enquanto estender escrever o programa principal na lin-
guagem dinmica estendida com funes escritas em C. Uma linguagem din-
mica poder chamar cdigo escrito em C bastante comum; mesmo em Java
podemos fazer isso, por meio da Java Native Interface. Entretanto, poucas lin-
guagens oferecem suporte adequado para serem embutidas, e portanto indu-
zem o projetista a usar uma arquitetura de extenso, com o programa principal
escrito na linguagem dinmica [Muhammad and Ierusalimschy 2007]. Em Lua,
34
Programao em Lua
podemos escolher qual a melhor arquitetura para cada aplicao particular.
Por exemplo, tanto World of Warcraft quanto Ginga usam Lua de forma embu-
tida, enquanto o Photoshop Lightroom estende Lua.
3.7.1. Embutindo Lua
A comunicao LuaC envolve sempre duas estruturas centrais, que va-
mos apresentar agora. A primeira o estado Lua, representado por um pon-
teiro de tipo lua_State
*
. Como Lua implementada como uma biblioteca,
seu cdigo C no possui nenhuma varivel global (extern ou static). Todo o es-
tado do interpretador armazenado na estrutura dinmica lua_State. Para
qualquer programa usar Lua, ele deve criar pelo menos um estado, por meio
da funo luaL_newstate. O valor retornado por essa funo, um ponteiro
de tipo lua_State
*
, deve ser passado como parmetro para todas as ou-
tras funes da API. A funo lua_close naliza um estado e libera todos os
recursos alocados por ele, incluindo memria, arquivos abertos e bibliotecas
dinmicas carregadas.
A segunda estrutura central a pilha. A pilha uma estrutura abstrata, que
o cdigo C s acessa por meio de chamadas API. Como o nome implica,
essa estrutura uma pilha de valores Lua. Esses so os nicos valores que
o cdigo C consegue acessar. Para se acessar qualquer outro valor, ele deve
primeiro ser copiado para a pilha.
Para tornar as coisas um pouco mais concretas, a Figura 3.11 mostra um
programa completo usando a biblioteca Lua. Esse programa executa um ar-
quivo Lua e imprime o valor numrico nal da varivel global result. Vamos
analis-lo passo a passo, ignorando alguns detalhes por enquanto.
A primeira coisa que o programa faz criar um estado Lua. Em seguida,
chama a funo luaL_loadfile para compilar o arquivo argv[1] nesse
estado. Se no houver erros na compilao, essa funo deixa um valor do tipo
function no topo da pilha e retorna o valor LUA_OK. Caso contrrio, retorna
um cdigo de erro e deixa uma mensagem de erro no topo da pilha. A funo
lua_tostring converte esse valor, uma string em Lua, para uma string em
C, que ento impressa na sada de erros (stderr).
A funo criada pelo compilador representa o trecho de cdigo lido do
arquivo. At aqui esse cdigo foi apenas compilado. Para execut-lo, pre-
cisamos chamar a funo criada pelo compilador. Isso feito pela funo
lua_pcall, que chama a funo presente no topo da pilha. Enventuais par-
metros para a funo devem tambm ser empilhados. Nesse caso, chamamos
a funo sem parmetros, e isso indicado pelo zero como segundo argu-
mento de lua_pcall. Assim como luaL_loadfile, em caso de erros a
funo lua_pcall tambm deixa uma mensagem no topo da pilha e retorna
um cdigo diferente de LUA_OK. Em caso de sucesso, deixa na pilha o nmero
de resultados pedidos. No nosso exemplo, queremos zero resultados, como
indicado pelo terceiro argumento da chamada lua_pcall.
No ltimo trecho do cdigo imprimimos o valor da varivel result. A fun-
35
R. Ierusalimschy
#include "lua.h"
#include "lauxlib.h"
int main (int argc, char
**
argv) {
lua_State
*
L = luaL_newstate();
if (luaL_loadfile(L, argv[1]) != LUA_OK)
fprintf(stderr, "error: %s\n",
lua_tostring(L, -1));
else if (lua_pcall(L, 0, 0, 0) != LUA_OK)
fprintf(stderr, "error: %s\n",
lua_tostring(L, -1));
else {
lua_getglobal(L, "result");
printf("resultado: %f\n", lua_tonumber(L, -1));
}
lua_close(L);
return 0;
}
Figura 3.11. Um programa para executar um arquivo Lua
e imprimir o valor da varivel global result.
o lua_getglobal copia o valor da global nomeada para o topo da pilha e a
funo lua_tonumber converte esse valor para um nmero do tipo double.
Vrias funes da API usam a pilha de modo normal, adicionando e re-
tirando elementos pelo topo. Por exemplo, a funo luaL_loadfile empi-
lha a funo criada ou a mensagem de erro; lua_pcall desempilha a fun-
o a ser chamada e eventuais argumentos e empilha eventuais resultados; e
lua_getglobal empilha o valor da global nomeada. Outras funes podem
acessar diretamente qualquer elemento dentro da pilha. Fazem parte dessa
categoria todas as funes de projeo, que transformam valores Lua em va-
lores C; no nosso exemplo vimos lua_tostring e lua_tonumber.
Para indicar um elemento na pilha, podemos usar um ndice positivo ou
negativo. ndices positivos contam a partir da base da pilha: o ndice 1 indica o
primeiro elemento que foi empilhado. ndices negativos contama partir do topo:
o ndice 1 indica o elemento no topo, o ltimo que foi empilhado. No nosso
exemplo, usamos o ndice 1 em todas as chamadas a funes de projeo,
pois queramos sempre referenciar o elemento no topo.
3.7.2. Estendendo Lua
O arquivo Lua executado pelo programa da Figura 3.11 pode ser algo to
simples quanto result=12 ou algo bastante complexo, envolvendo muitas
computaes. Entretanto, ele no pode chamar nenhuma funo: quando cri-
36
Programao em Lua
amos um novo estado Lua ele est completamente vazio, sem nenhuma glo-
bal denida. Nem mesmo as bibliotecas padro esto abertas. Para abri-las,
precisamos chamar a funo luaL_openlibs, denida em lualib.h. Essa
chamada vai povoar o estado Lua com todas as funes das bibliotecas padro
da linguagem.
A necessidade de chamar luaL_openlibs em separado novamente ex-
plicada pela nfase em exibilidade da API de Lua. Muitos sistemas usam Lua
sem oferecer todas as bibliotecas. Por exemplo, sistemas embarcados rara-
mente podem implementar a biblioteca de entradasada, por no terem um
sistema de arquivos. Outros sistemas podem no oferecer a biblioteca mate-
mtica para economizar espao. Tais sistemas podem usar Lua sem nenhuma
modicao, bastando no usar a funo luaL_openlibs.
A funo luaL_openlibs, assim como todas as funes da biblioteca
padro, no faz nada mgico para estender Lua. Ela apenas usa a API Lua
C. Vamos ver agora, por meio de um exemplo, como estender Lua com uma
nova funo.
Uma funo C, para poder ser chamada por Lua, deve respeitar um proto-
colo especco. No nvel de C, a funo deve seguir o prottipo abaixo:
typedef int (
*
lua_CFunction) (lua_State
*
L);
Isso , ela deve receber um nico parmetro, que o estado Lua onde ela ir
operar, e retornar uminteiro, que o nmero de valores que ela est retornando
no nvel Lua. No nvel de Lua, ela recebe seus parmetros na pilha e retorna
seus resultados tambm na pilha.
Cada funo tem sua prpria pilha local. Quando a funo chamada, a
sua pilha contmapenas os parmetros para a funo, com o primeiro parme-
tro na base da pilha. Ao retornar, os valores no topo da pilha so os resultados
da funo, com o ltimo resultado no topo. O inteiro retornado no nvel C in-
dica quantos desses valores na pilha so resultados. Por exemplo, se a funo
retorna zero no nvel C, signica que ela no est retornando nenhum valor no
nvel Lua, independente de quantos valores esto empilhados. Se ela retorna
um no nvel C, apenas o valor no topo da pilha considerado valor de retorno.
Esse esquema facilita a codicao das funes, pois no h necessidade de
se limpar a pilha de outros valores ao retornar.
Como exemplo, na Figura 3.12 temos a implementao da funo de seno,
copiada diretamente da biblioteca matemtica de Lua. A funo math_sin
basicamente um encadeamento de trs funes: luaL_checknumber, sin,
da biblioteca matemtica de C, e lua_pushnumber.
A funo auxiliar luaL_checknumber verica se o primeiro elemento da
pilha realmente um nmero e retorna seu valor; observe o uso do ndice posi-
tivo 1 na sua chamada, para indexar o elemento na base da pilha. Essa funo
auxiliar construda sobre a lua_tonumber, que j vimos anteriormente, mas
usa antes a funo lua_isnumber para se certicar que o parmetro real-
mente um nmero. Em caso negativo ela chama uma funo de erro que, por
37
R. Ierusalimschy
static int math_sin (lua_State
*
L) {
lua_pushnumber(L, sin(luaL_checknumber(L, 1)));
return 1;
}
Figura 3.12. Funo seno, da biblioteca matemtica.
meio de um long jump, salta diretamente para um tratador de erros, sem retor-
nar. Assim, quando luaL_checknumber retorna sabemos que o parmetro
tem o tipo correto.
A funo lua_pushnumber, como seu nome indica, empilha um nmero;
no nosso caso, esse nmero o resultado da chamada funo sin. Esse
nmero ser empilhado por cima do parmetro original, que permanece na
pilha. A funo math_sin termina retornando 1, o que indica para Lua que
apenas o valor do topo (o seno) deve ser considerado como resultado.
Aps denirmos a funo math_sin, precisamos registr-la em Lua. A
parte principal do trabalho feita pela funo lua_pushcfunction: ela re-
cebe umponteiro para uma funo C e empilha um valor Lua do tipo function
que, quando chamado, invoca a funo C correspondente. Como qualquer fun-
o em Lua, a funo empilhada por lua_pushcfunction um valor de pri-
meira classe. Podemos por exemplo armazen-la em uma varivel global, para
uso futuro. O trecho de cdigo a seguir ilustra como podemos modicar nosso
programa da Figura 3.11 para registrar nossa funo em uma global sin:
int main (int argc, char
**
argv) {
lua_State
*
L = luaL_newstate();
lua_pushcfunction(L, math_sin);
lua_setglobal(L, "sin");
/
*
... como antes ...
*
/
Aps a criao do estado Lua, lua_pushcfunction cria a funo no topo da
pilha e lua_setglobal armazena o valor do topo da pilha na global nomeada
(sin). Aps essas chamadas, qualquer programa executado no estado L ter
acesso funo sin.
3.8. Comentrios Finais
Neste texto procurei ilustrar algumas das caractersticas mais marcantes da
linguagem Lua. Abordamos funes de primeira classe, tabelas, co-rotinas e a
API LuaC. Procurei tambmenfatizar o poder expressivo desses mecanismos,
mostrando alguns exemplos mais complexos.
Devido a restries de tamanho, no pude abordar vrios outros aspectos
importantes de Lua. Em particular, o tratamento da API LuaC foi bastante in-
trodutrio; por ser ummecanismo no muito convencional, temos que introduzir
38
Programao em Lua
vrios conceitos bsicos novos para podermos chegar a tpicos mais avana-
dos. Tambm no abordei as bibliotecas padro de Lua; algumas so conven-
cionais, como a biblioteca de manipulao de arquivos. Mas outras, como a
biblioteca de strings com suas funes de casamento de padres, merecem
um tratamento mais longo. Espero que o leitor, aps este texto introdutrio,
sinta-se motivado a aprofundar seus conhecimentos sobre Lua.
Referncias bibliogrcas
[ABNT 2007] ABNT (2007). Televiso digital terrestre Codicao de dados
e especicaes de transmisso para radiodifuso digital. Associao Bra-
sileira de Normas Tcnicas. ABNT NBR 15606-2.
[Bentley et al. 1986] Bentley, J., Knuth, D., and McIlroy, D. (1986). Program-
ming pearls: a literate program. Communications of the ACM, 29(6):471
483.
[de Moura and Ierusalimschy 2009] de Moura, A. L. and Ierusalimschy, R.
(2009). Revisiting coroutines. ACM Transactions on Programming Langua-
ges and Systems, 31(2):6.16.31.
[de Moura et al. 2004] de Moura, A. L., Rodriguez, N., and Ierusalimschy,
R. (2004). Coroutines in Lua. Journal of Universal Computer Science,
10(7):910925.
[Eckart 1987] Eckart, J. D. (1987). Iteration and abstract data types. SIGPLAN
Notices, 22(4):103110.
[Hudak and Jones 1994] Hudak, P. and Jones, M. P. (1994). Haskell vs. Ada
vs. C++ vs. Awk vs. . . . an experiment in software prototyping productivity.
Technical report, Yale University.
[Ierusalimschy 2006] Ierusalimschy, R. (2006). Programming in Lua. Lua.org,
Rio de Janeiro, Brasil, segunda edio.
[Ierusalimschy et al. 2006] Ierusalimschy, R., de Figueiredo, L. H., and Celes,
W. (2006). Lua 5.1 Reference Manual. Lua.org, Rio de Janeiro, Brasil.
[Ierusalimschy et al. 2007] Ierusalimschy, R., de Figueiredo, L. H., and Celes,
W. (2007). The evolution of Lua. Em Third ACM SIGPLAN Conference on
History of Programming Languages, pginas 2.12.26, San Diego, CA.
[Muhammad and Ierusalimschy 2007] Muhammad, H. and Ierusalimschy, R.
(2007). C APIs in extension and extensible languages. Em XI Brazilian Sym-
posium on Programming Languages, pginas 137150, Natal, RN.
[Ousterhout 1990] Ousterhout, J. (1990). Tcl: an embeddable command lan-
guage. Em Proc. of the Winter 1990 USENIX Conference. USENIX Associa-
tion.
[Ungar et al. 1987] Ungar, D. et al. (1987). Self: The power of simplicity. Sig-
plan Notices, 22(12):227242. (OOPSLA87).
[Wirth 1982] Wirth, N. (1982). Programming in Modula-2. Springer-Verlag.
39

Você também pode gostar