Reiterador
Uma coisa fácil de fazer em Python é um reiterador (para quem gosta de estrangeirismos: «iterador», de iterator). Basta um simples comando yield e está tudo resolvido.
Estava pensando comigo mesmo: e em Lua?
Então resolvi fazer um objeto que retorne indefinidamente e de forma aleatória os elementos de um vetor, mas sem repetir até que todos tenham sido retornados. Em Python a gente resolve isso com uma função:
from random import choice
def rand_new(*args):
assert len(args) > 0
t1 = list(args)
t2 = []
while True:
e = choice(t1)
t1.remove(e)
t2.append(e)
if len(t1) == 0:
t1, t2 = t2, []
yield ePara resolver este problema (pretensão =P) em Lua, decidi usar uma metatabela.
Vamos então criar a metatabela:
local iter = { sec = {} }
iter.__index = iter
iter.__newindex = function (t, k, v)
error("Chave " .. k .. " não encontrada")
endPronto. A chave
sec vai funcionar como a lista t2 do código Python. A o próprio objeto (a parte indexada) vai funcionar como a lista t1.Vamos agora criar o construtor:
function iter:new(o)
o = o or {}
setmetatable(o, self)
return o
endComo vamos colocar isso dentro de um módulo, vamos criar uma função para retornar o objeto:
function new(t)
if type(t) ~= "table" then
error "Esta função recebe um vetor como parâmetro"
end
if table.getn(t) == 0 then
error "Vetor vazio"
end
return iter:new(t)
endVamos criar uma função para reiniciar o contador interno, ou seja, reabilitar todos os elementos do vetor:
function iter:reinit()
while table.getn(self.sec) > 0 do
table.insert(self, table.remove(self.sec, 1))
end
endAgora vamos criar o
next() (como no reiterador de Python):function iter:next()
local m, resp
m = table.getn(self)
-- Esvaziou? Recomeça do princípio...
if m == 0 then
self:reinit()
m = table.getn(self)
end
resp = table.remove(self, math.random(m))
table.insert(self.sec, resp)
return resp
endPrimeiro determinamos quantos elementos ainda estão no vetor principal (se não tiver nenhum, reinicia). Escolhemos um aleatoriamente, removemos, inserimos no vetor secundário e retornamos o elemento.
E é só isso!
Agora vamos transformar esta festa toda em um módulo!
Se você está usando Lua 5.1 ou possui o Compat-5.1, basta no início do arquivo transformar em locais todas as funções globais que estamos usando e em seguida declarar o módulo:
local error = error
local math = math
local table = table
local setmetatable = setmetatable
local type = type
module "rand"Se você está usando Lua 5.0 e não possui o módulo Compat-5.1, talvez isto não funcione, então, em vez disso, transforme a função
new() em local e no final do arquivo declare a seguinte tabela:rand = { new = new }Vamos testar agora:
lua> require "rand"
lua> a = rand.new { 1, 2, 3 }
lua> =a:next()
3
lua> =a:next()
1
lua> =a:next()
2
lua> =a:next()
2
lua> =a:next()
1
lua> =a:next()
3
lua> =a:next()
1[]'s
CC-BY: Os textos deste blog podem ser reporduzidos contanto que sejam informados autor e origem
Aviso aos navegantes!!!
ResponderExcluirOntem escrevi os códigos, testei e escrevi o artigo, tudo em menos de meia hora.
Então, na hora de copiar os códigos pro artigo, dei algumas moscadas (copy no lugar de list, then no lugar de do...).
Estou corrigindo conforme vou encontrando (ou melhor, conforme o Walter, que está revisando, vai encontro). =P
[]'s
PS: Valeu Walter!
O Walter sugeriu o mesmo argumento usando corrotina (um dos recursos de Lua, assim como metatabela), então segue o código:
ResponderExcluirlocal coroutine = coroutine
local error = error
local math = math
local table = table
local type = type
module "rand"
function new(t)
if type(t) ~= "table" then
error "Esta função recebe um vetor"
end
if table.getn(t) == 0 then
error "Vetor vazio"
end
co = coroutine.create(function (t1)
local m, e, t2
t2 = {}
coroutine.yield(nil)
while true do
m = table.getn(t1)
if m == 0 then
while table.getn(t2) > 0 do
table.insert(t1, table.remove(t2, 1))
end
m = table.getn(t1)
end
e = table.remove(t1, math.random(m))
table.insert(t2, e)
coroutine.yield(e)
end
end)
coroutine.resume(co, t)
return co
end
Agora, em vez de a:next(), deverá ser usado coroutine.resume(a).
[]'s
Gente,
ResponderExcluirHouve um comentário anônimo de um programador que está tendo alguma dificuldade com a linguagem. Ele pediu para quem puder ajudar, adicioná-lo no MSN: kin88_@hotmail.com
Apesar de ter deixado um endereço de email e MSN, ele não se identificou de qualquer outra forma (nome ou página), e avisei que não permitirei comentários anônimos que não sejam assinados (ou seja, anônimos de verdade, no sentido literal da palavra).
É uma regra que criei e não posso abrir excessões assim.
Desculpe amigo. Da próxima, por favor assine o comentário.
[]'s