18/06/2011 Programação Lua

Muita gente sabe que Lua é minha linguagem de programação favorita. Vivo no mundo da Lua, ou melhor, estou sempre falando de Lua. Mas há uma coisa que nunca havia reparado em Lua: não existe o comando continue como parte das estruturas de controle.

Vou explicar melhor. Eu tinha um programa com uma tabela contendo nomes de arquivos. Para cada nome na tabela eu abria o arquivo, verificava alguma coisa no conteúdo dele e fechava novamente. Por fim, passava para o próximo nome na tabela. Seria algo assim:

for i,v in ipairs(nomes) do
  local arq = io.open(v, "r")
  local url = string.match(arq:read("*l"), "url=(.*)")

  -- outros comandos aqui

  arq:close()
end

Tudo bem até aqui, tem um laço for com uma função iteradora que vai abir e ler dados de todos os arquivos na tabela nomes. Tendo visto que meu programa estava funcionando corretamente, fui aperfeiçoando ele adicionando tratamento de erro:

for i,v in ipairs(nomes) do
  local arq, e = io.open(v, "r")
  if (arq == nil) then
    print(e)
  else
    local url = string.match(arq:read("*l"), "url=(.*)")
    if (url == nil) then
      print(v .. ": read error")
    else
        -- outros comandos aqui
    end
    arq:close()
  end
end

Certo, agora com os condicionais if o programa iria continuar apenas se não acontecessem erros. No caso de erro ele voltava para o próximo laço do for. Mas... se eu tivesse ali 10, 20 ou mais leituras ou outros comandos que pudessem gerar erro? Iria ficar um aninhamento de if descomunal - feio e fonte de erros e bugs. Usar if não era a solução.

Temos o comando break, mas ele interrompe o laço e vai para o próximo comando depois do laço. Se usasse ele, dando erro no primeiro arquivo ele iria sair do laço ignorando todos os outros arquivos da tabela. Em outras linguagens de programação, temos o comando continue, que ignora o restante do bloco e vai para a próxima etapa do laço (seja for, while, ou o que for). Fui usar e... para minha surpresa, não tinha! E agora? Ficar no aninhamento insano de if? Claro que não, só tem espaço para um insano aqui e código insano não está incluído nisso.

A solução encontrada foi adicionar um bloco repeat until true apenas para colocar o break dentro dele. De forma que o break vai encerrar o bloco que está dentro do laço for , de forma que a execução vai seguir para a próxima etapa do laço - o que é exatamente o que queremos. Veja como fica:

for i,v in ipairs(nomes) do
  repeat
    local arq, e = io.open(v, "r")
    if (arq == nil) then
      print(e)
      break
    end
    local url = string.match(arq:read("*l"), "url=(.*)")
    if (url == nil) then
      print(v .. ": read error")
      arq:close()
      break
    end

    -- outros comandos aqui

    arq:close()
  until true
end

Resolvido o problema. Note que until true vai fazer com que esse bloco extra seja executado somente uma vez caso não tenha ocorrido nenhum erro e a execução tenha chegado até o final do bloco. Sem continue, mas sem aninhamentos desnecessários no código.

br_lemes, o Centauro insano (Fobia)