Jump to content

A diferença entre pairs e ipairs


Recommended Posts

  • Other Languages Moderators

Neste tutorial irei explicar qual a diferença entre um loop usando pairs e um loop usando ipairs. Mas antes de prosseguir, é necessário entender o que é uma tabela indexada e uma tabela não indexada.

Tabela indexada é aquela cujos itens tem seu índice declarado. Os índices podem ser números, textos ou elementos.

Tabela não indexada é aquela cujos itens não tem seu índice declarado. Porém a linguagem Lua automaticamente indexará essas tabelas com inteiros sequenciais.

local tabela = {"1", 2, "três", 0.4, false, 0} -- Tabela não indexada.
-- O MTA vai considerar a tabela acima dessa forma:
local tabela = { -- Tabela indexada com inteiros sequenciais.
    [1] = "1",
    [2] = 2,
    [3] = "três",
    [4] = 0.4,
    [5] = false,
    [6] = 0
} -- Escrevi a tabela na vertical para facilitar a leitura, não faz diferença escrever tudo na mesma linha.

É importante saber disso pois ipairs leva em consideração os índices dos itens de uma tabela, como veremos a seguir.

CONCEITO

Tanto o pairs quanto o ipairs são utilizados para fazer loops que percorrem uma tabela usando o laço de repetição for. Mas existem situações em que um ipairs não funciona e também há situações em que o pairs não atende ao objetivo que o scripter precisa.

Basicamente, o pairs percorre os itens de uma tabela mas não garante a mesma ordem em que eles foram declarados e nem a sequência de seus índices. Sem qualquer ordem específica, ele não exige que a tabela seja indexada por inteiros sequenciais, já que ele não leva em consideração a ordem dos índices da tabela.

Enquanto o ipairs (significado: index-value pairs) percorre os itens de uma tabela sempre seguindo a sequência de seus índices. Verificando primeiro o item de índice 1, depois o item de índice 2 e assim por diante. Se um índice da sequência estiver faltando, ele não percorrerá o resto e vai parar de verificar. Também não importa a ordem em que os índices são declarados. Se você declara o item de índice 2 e depois o item de índice 1, mesmo assim ele vai ler o item de índice 1 primeiro e depois o item de índice 2. O ipairs também não é capaz de verificar itens cujo índice não seja um inteiro, pois ele não sabe a sequência que ele pertence, portanto itens de índice string não são verificados pelo ipairs.

EXEMPLOS

Usando o pairs: (note que o exemplo é server-side, mas tanto o pairs quanto o ipairs podem ser usados client-side também)

Server-side

addCommandHandler("eae", function(thePlayer, cmd)
    -- Vamos setar algumas elementDatas em si mesmo só para testes.
    setElementData(thePlayer, "vida", 100)
    setElementData(thePlayer, "colete", 90) -- (Datas fictícias, não alteram de verdade a vida nem colete do jogador).
    setElementData(thePlayer, "vivo", true)
    setElementData(thePlayer, "procurado", false)
    setElementData(thePlayer, "emprego", "Mecânico")
    
    local datas = getAllElementData(thePlayer) -- Obtém uma tabela com todas as elementDatas do jogador que executou o comando /eae
    
    -- A tabela retornada seria assim: (supondo que o jogador não tenha outras elementDatas setadas nele) 
    --[[
    local datas = {
        ["vida"] = 100,
        ["colete"] = 90,
        ["vivo"] = true,
        ["procurado"] = false, -- Este é o único jeito de saber se uma elementData false existe mesmo.
        ["emprego"] = "Mecânico"
    }
    --]]
    
    -- Para verificar cada item dessa tabela, o pairs precisa ser usado pois os índices são strings enquanto o ipairs só funciona com índices inteiros sequenciais.
    for name, value in pairs(datas) do
        print(name.." = "..tostring(value))
    end
    -- Não há qualquer garantia de que os itens sejam verificados na mesma ordem em que foram declarados. Consideramos isso aleatório.
end)

Obs: O pairs também funciona em tabelas com índices inteiros sequenciais como o ipairs, porém percorre de maneira aleatória enquanto o ipairs segue a sequência dos índices.

1) Se trocar o pairs por um ipairs, nenhum item será verificado. Pois não foi encontrado o índice 1.

Obs2: Mesmo se você definir uma elementData com nome "1", o índice continuará sendo uma string, não sendo lido pelo ipairs.

Vimos acima um exemplo que precisa usar pairs pois o ipairs não funcionaria.

Agora veremos um exemplo onde o ipairs seria mais adequado.

Client-side

local palavras = {
    [1] = "Neste ",
    [2] = "caso ",
    [3] = "a ",
    [4] = "ordem ",
    [6] = "importa " -- Esqueci do índice 5 de propósito.
}

local mensagem = ""
for i, v in ipairs(palavras) do
    mensagem = mensagem..v
end

print(mensagem)

Faça os seguintes testes:

1) Execute o código acima do jeito que está, veremos que a mensagem final aparece incompleta. Pois ele não encontrou o índice 5 e parou de verificar o resto dos itens. Retornando a mensagem "Neste caso a ordem "

2) Se trocarmos o ipairs por um pairs, veremos que a mensagem ficará bagunçada, pois os itens foram verificados de maneira aleatória. Porém desta vez todas as palavras serão verificadas, pois o índice não é levado em consideração pelo pairs, somente pelo ipairs.

3) Troque o pairs pelo ipairs de volta e substitua o índice 6 por 5. A mensagem aparecerá completa e com as palavras em ordem. Já que não haverá nenhum índice faltando na sequência.

4) Troque a ordem dos itens da tabela dessa forma: (preste atenção na vírgula, ela é obrigatória em todos os itens, exceto no último)

local palavras = {
    [4] = "ordem ",
    [2] = "caso ",
    [5] = "importa ",
    [1] = "Neste ",
    [3] = "a " 
}

Veremos que o ipairs continuará verificando cada item na ordem correta. Já que o que importa para ele são os índices e não a ordem em que os itens foram declarados na tabela.

Edited by Lord Henry
Link to comment
  • Other Languages Moderators

CURIOSIDADES

Em questão de performance, a diferença entre eles é irrelevante. Mas se considerarmos casos extremos de tabelas gigantes com milhares de itens, a execução do pairs é um pouco mais leve do que ipairs, visto que ele não precisa obedecer ordem nenhuma em sua execução, enquanto que o ipairs precisa sempre verificar a cada execução se o próximo índice existe na tabela inteira. Por esse motivo, se você for obcecado por otimização, prefira usar o pairs.

Para saber quantos itens uma tabela possui, geralmente usamos #NomeDaTabela. Mas vale ressaltar que o caractere # na verdade retorna o maior índice conhecido (veremos casos abaixo em que pode existir um índice maior, mas ele não é conhecido). Sendo assim, ele não funciona em tabelas cujos índices não sejam inteiros sequenciais e também retornará a quantidade errada de itens em tabelas que tenham algum índice da sequência faltando. Para contar itens numa tabela de índices aleatórios, usamos uma variável de contador, que vai aumentando em +1 a cada execução do loop e no final nos mostra quantas verificações foram feitas, indicando quantos itens tem na tabela. Vejamos alguns exemplos:

local tabela1 = {
    [4] = "a",
    [2] = "b",
    [5] = "c",
    [1] = "d",
    [3] = "e" 
}
print("Tabela1: "..#tabela1) -- Retornará 5. Pois é o maior índice conhecido.

local tabela2 = {
    [1] = "a",
    [2] = "b",
    [3] = "c",
    [4] = "d",
    [6] = "e",
}
print("Tabela2: "..#tabela2) -- Retornará 6. Mesmo tendo apenas 5 itens, o índice 6 é o maior.

local tabela3 = {
    ["um"] = "a",
    [22] = "b",
    ["3"] = "c",
    [1] = "d",
    [0] = "e"
}
print("Tabela3: "..#tabela3) -- Retornará 1. Ele começa a verificar a partir do índice 1, então o índice 0 seria ignorado de qualquer forma.
-- Em tabelas que possuem somente índices inteiros, ele iria encontrar o 22. Mas se tiver outros tipos de índices, ele não sabe qual a sequência correta e não verifica os demais.

local contador = 0
for _,v in pairs(tabela3) do -- Maneira correta de contar itens numa tabela onde o ipairs não funciona.
    contador = contador+1
end
print("Tabela3 de novo: "..contador) -- Retornará 5. Independente dos índices.

 

Edited by Lord Henry
Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...