What is I, V in pairs?

What does it do? What is it’s practical usage in game making?

2 Likes

It allows you to iterate through a dictionary.

Tables have keys associated with values, for i , v in pairs() makes it so one set of key and value is registered as one time the things within the loop will run

example:

local table = {"Car" , "Bus"}
for i , v in pairs(table) do
print(v)
end

this would print only the value of each key and not the actual key.

1 Like

As stated above, it runs the loop once for each index in a table. The first variable, i, can be used to get number of the index that the loop is on. For example:

    local Example = {true, false, true, true}
    for i, v in pairs(Example) do
        print(i)
    end 

This will print 1, 2, 3, and 4 (In that order).
Now, the variable v contains the actual value for that index of the table. For example:

    local Example = {true, false, true, true}
    for i, v in pairs(Example) do
        print(v)
    end 

This will print true,false,true, and true.

One way you could use this is if you want to apply an effect to a whole table of player (Or, say, the entire server). Luckily, game.Players has a built in :GetPlayers() function which makes this super easy, since it returns a table containing all the players in the game. This script would kill all the players in the server when run.

for i, v in pairs(game.Players:GetPlayers()) do
    v.Character.Humanoid.Health = 0
end

A less evil use of this could be to delete all children or type of child of a parent. :GetChildren() returns a table containing all of the children of an object.
This script will look through all of the children of the folder Folder and delete any parts it finds.

for i, v in pairs(game.Workspace.Folder:GetChildren()) do
    if v:IsA("BasePart") then
        v:Destroy()
    end
end

There are countless other applications, and I barely scratched the surface here. Let me know if you need further clarification!

8 Likes

i and v are the two values returned by the function that the pairs iterator returns. An iterator function is just a function that is used to iterate. That is to say for ... in iter, b where b is some limit and ... represents a varadic amount of variables returne per iter call.

In the case of the question: i being associated with the current index and value being associated with the value tied to that index. That is to say, given a table t, v = t[i].

pairs is generally defined as being functionally equivalent to:

function pairs(t)
    return next, t, nil
end

I think the point of pairs was to create a “iterator factory” style in Lua–this is seen in gmatch for example which is a function that creates an iterator function. (hence “factory”–it creates iterators).

In luau (ROBLOX’s optimized version of Lua 5.1) this is not really the case, but for sake of argument this will produce identical results as the pairs function implemented in luau.

The arguments returned from the next call are going to be i and v. Here is an example of a custom iterator function implementation which can be used to generate the even numbers below n as well as their square.

local function squares(limit, cur)
    cur = cur or 0

    if (cur + 2 < limit) then
        return cur + 2, (cur + 2)^2
    end
end

local n = 14

for i, square in squares, n do
    print(i, square)
end

We can extend our knowledge further to create an iterator with any amount of variables.

local function iter(limit, cur)
    cur = cur or 0

    if (cur < limit) then
        return cur + 1, ("hello"):rep(cur), ("world"):rep(cur)
    end
end

for i, h, w in iter, 4 do
    print(i, h, w)
end

1
2 hello world
3 hellohello worldworld
4 hellohellohello worldworldworld

1 Like

The pairs function or for ... in func(...) do loops in general are kind of misleading.

Before I get into that, let’s take a look at the following loop works: for var_1, ..., var_n in explist do block end. By default, Lua only cares about a few things in that loop. According to this official Lua article, your loop becomes the equivalent of this:

do
    local _f, _s, _var = explist
    while true do
        local var_1, ... , var_n = _f(_s, _var)
        _var = var_1
        if _var == nil then break end
        block
    end
end

All that happens is a block is repeatedly ran with the for loop’s variables until the iterator returns nil.
Contrary to popular belief, pairs(t) is not an iterator, but instead more or less a generator function which returns an iterator function (which is not exactly equivalent to next @DrKittyWaffles), t, and the initial control state (nil). Therefore, the pairs(t) in for i, v in pairs(t) do gets compiled into an OP_CALL instruction with 3 returns (which are the aforementioned three magic parameters) in order to match with the internally accepted semantics for generic for loops.

As far as how the iterator in for i, v in pairs(t) do works, it goes through a table’s hash and array map with a pivot (that being the i (state) in the loop). That means that you could also theoretically create new keys on the go as you loop through it and they will be accounted for. E.g.:

local t = {"a"}
for i, v in pairs(t) do
    if i == 1 then
        t[2] = "b"
    end
    print(i, v)
end

will print [ 1, “a” ] and [ 2, “b” ], even tho we initially started with just “a” in the array.

Most idiomatic for loops (including pairs, ipairs and next ones) have since been heavily optimized by Luau and converted into a sequence of native engine opcodes tho, so my explanation above only applies for unique/custom generated iterator for loop cases.

2 Likes