Em busca de uma programação poliglota, e entender as vantagens da cada linguagem oferece, iniciei meus estudos na linguagem Lua.
Histórico
Lua é uma linguagem de extensão, criada nos laboratórios da PUC-RJ, com o objetivo de ser simples de ser embarcada. Isso é, ele é extremamente pequena, sucinta, de sintaxe simples, esta presente praticamente em qualquer sistema. É uma verdadeira “write once, run everywhere”(bem mais que java). O pacote de Lua, com documentação, código fonte e exemplos tem cerca de 800KBs, apenas o runtime compilado(no linux) tem 150KBs. São 1700 linhas de código C portável, o mesmo código compila em várias plataformas.
Roda em Unix, Linux, Windows, e em várias plataformas embarcadas, como celulares e setop boxes, robôs entre outros. Pode ser incluída em qualquer aplicação feita em outra linguagem , com quase nenhum overhead. Para ter melhor noção, um interpretador Lua completo em puro Java (tanto J2ME quanto J2SE), tem cerca de 150KBs. É usada como linguagem de extensão em produtos da Adobe, roda no Android, esta no Ginga (da TVDigital), e é muito usada em Jogos, como o sempre citado World of Warcraft.
E por que não fazer tudo em Lua então? Lua foi pensada em extensão de aplicativos, então a biblioteca básica é bem limitada, não possui as milhares de funções para tudo como teria em Java ou PHP ou Ruby ou Python ou Outra. Seus 150KBs incluem apenas o necessário, muito tem disponível em módulos externos, como os do LuaRocks. Embora seja possível para certas aplicações, mas não é foco.
Enfim, vendido o peixe de Lua. Agora é demonstrar, afinal “Show me the code”.
Exemplos
Primeiro o essencial, lua é Imperativo, tem funções como cidadão de primeira classe(ah, deu para entender) e tudo é armazenado em tabelas. Tabelas são como arrays, mas são tabelas, depois eu explico. É tipagem dinamica, essencialmente não é orientada a objetos(pode ser simulado), possui escopo e é modular. Vamos ao exemplo.
-- rss.test.lua
local xml = dofile("xml.lua") local sample = "<?xml ?><channel><rss><title>Lua Rock!</title>" sample = sample .. "<item><title>Item 1</title></item>" sample = sample .. "<item><title>Item 2</title></item>" sample = sample .. "</rss></channel>"; local rss = xml.parse_rss(sample) assert(rss[1].title == "Item 1","Invalid Item 1 title") assert(rss[2].title == "Item 2","Invalid Item 2 title") print("rss tests ok")
Esse simples exemplo é o arquivo de testes de uma biblioteca de parser de rss que uso. O uso do modificador “local” indica o escopo mais fechado possível dessa variável. O não uso de “local” implica numa variável “global”, visível em toda a aplicação.
A função “dofile” carrega um arquivo (como se fosse um include), esse arquivo carregado pode retornar uma ou mais variáveis, no caso o xml.lua me retorna uma tabela.
A chamada de função é normal, no caso o xml é uma tabela, e um dos seus índices (parse_rss) é uma função que recebe um argumento e retorna uma tabela.
Fato interessante sobre tabelas em Lua, é que os índices numéricos começam em 1, e não em 0 como estamos acostumados.
Em “rss[1].title”, eu pego o item de índice 1(o primeiro), da tabela “rss” e o item de índice title dentro desta. No “assert” eu garanto que a expressão retorne True, do contrário levanto o erro no segundo argumento.
O arquivo de rss é assim:
-- rss.lua local xml = dofile("utils/xml.lua") xml.parse_rss = function (str) local rss = {}; local t = xml.parse(str) local channel = t[2][1] for k,v in pairs(channel) do local label = v['label'] if label == "item" then local item = {} for k,v in pairs(v) do local label = v['label'] if label and v[1] then item[label] = v[1] end -- if label and value end --for pairs in v table.insert(rss,item) end -- if is item end -- for item in channel return rss end return xml
Novamente eu carrego um parser xml externo, e na tabela xml eu adcione um índice, o parse_rss. Nesse índice eu ligo uma função, que recebe um argumento. Veja que não tem tipo fixo, eu poderia usar assert para garantir o tipo.
Eu defino então a variável “rss” como uma tabela vazia. Então faço um loop for nos itens retornados pelo parser xml. A função “pairs” retorna as chaves (k) e os valores(v) de uma tabela.
Temos então um IF padrão, nova tabela e mais um for. Então eu uso o “insert”, “table”, que é uma tabela embutida no core do lua. E retorno o RSS, o arquivo então retorna a tabela com as funções.
O parser XML é deveras grande, não vale a pena passa-lo aqui, e isso tudo foi apenas para dar um visão geral na estrutura da sintaxe da linguagem. Vamos agora no passo-a-passo de verdade.
Tutorial
Em Lua, funções são objetos de primeira classe, então podemos por exemplo ter funções “curried”, como no exemplo abaixo:
-- curry.lua function soma(n) return function(n2) return n + n2 end end local f2 = soma(2) local soma2e3 = f2(3) assert(soma2e3 == 5)
Podemos levar um pouco alem:
-- curry.lua local somador = function(n) local soma = n function f(n2) if type(n2) == "number" then soma = soma + n2 return f else return soma end end return f end
local total = somador (1) (2) (3) () print(total)
local somador = function (itens) assert(type(itens) == "table") local soma = 0; for k,v in pairs(itens) do soma = soma + v end return soma, #itens end
local total,n = somador { 1,2,3 } print(total,n)
local somador = function(```) local soma = 0; for k,v in pairs(arg)do if type(k) == "number" then soma = soma + v end end return soma end
local total = somador(1,2,3) print(total)
São as várias formas de fazer a mesma coisa, veja no penúltimo somador uma característica legal, uma função pode retornar mais de uma variável.
Agora que você conhece como fazer funções, loops e condicionais, vamos tratar das tais tabelas.
--tabelas.lua local tab = { "um","dois", x="3", y=function() return "hello" end } table.insert(tab,"cinco") table.remove(tab,2) local final = table.concat(tab," , ") -- apenas numéricos print(tab[1], tab[2], tab["x"],tab.y,tab["y"](),tab[5],final)
Entendeu?
A ultima parte que quero tratar nesse texto é sobre coroutines, que são as “threads” do lua, incluído no core da linguagem, exemplo geral:
--co.lua local hello = coroutine.create(function(name) print ("hello "..name) end)
coroutine.resume(hello,"diogo")
local hello2 = coroutine.create(function(name) repeat print("hello you, "..name) name = coroutine.yield(name) until not type(name) == "string" end)
local h2 = coroutine.resume(hello2,"diogo") local h3 = coroutine.resume(hello2,"diego") local h3 = coroutine.resume(hello2) local h4 = coroutine.resume(hello2)
Acho que com esses exemplo já dá para decidir se quer ou não aprender Lua. Caso queira, o melhor lugar é a wiki da documentação de lua.
Termino com um exemplo de uso real, uma função que realiza uma busca na wikipedia e retorna o resultado, o JSON eu achei na internet, e o http usa o Lua Socket, o wikipedia.lua.
Legal! Me interessei muito por essa linguagem, pois ela é simples e leve, não é como C ou C++ que são muito complexas, por isso quero aprender LUA, mas o site http://lua-users.org/wiki/LuaOrgGuide é todo em inglês, e isso dificulta um pouco. Não tem nenhum site em portugês, não? Se tiver por favor me deêm um link. (me mandem por E-Mail) :)
Sim, lua é realmente impressionante.
Você pode achar muitos links em português aqui:
http://www.lua.org/portugues.html
Com o manual completo:
http://www.lua.org/manual/5.1/pt/
Para quem ainda não sabe encontra-se publicado um livro de linguagem Lua em português. Este material pode ser adquirido em http://clubedeautores.com.br/book/122264–Lua__Programacao_de_Computadores