Skip to content

Instantly share code, notes, and snippets.

@appgurueu
Last active January 26, 2026 00:22
Show Gist options
  • Select an option

  • Save appgurueu/0a7c87ea6a8b18cbea477fba1949697d to your computer and use it in GitHub Desktop.

Select an option

Save appgurueu/0a7c87ea6a8b18cbea477fba1949697d to your computer and use it in GitHub Desktop.
`pairs` order randomization
--[[
Slightly randomize `pairs` traversal in order to deliberately break most code that relies on it.
Works by doing a coin toss to decide whether to "postpone" visiting the first entry
(visiting it at the end of the traversal).
Shuffling all keys, then iterating in that shuffled order would provide more thorough randomization; however:
* It is less efficient if the loop is exited early; it may arbitrarily wreck asymptotics
(consider a large table where `pairs` is only used to inspect very few entries.)
* More sophisticated "on-demand" shuffling using appropriate data structures is possible,
but increases code complexity and overhead significantly.
* I believe this simple solution already suffices for most code
that incorrectly relies on a specific order of traversal.
Notably, this has a 50% chance of breaking code that
expects `pairs` to exhibit `ipairs`-like behavior.
This does not seed the random for you,
so if you want to achieve different outcomes across different runs,
seed the random yourself.
]]
function pairs(tab)
local fk = next(tab)
local sk = nil
if math.random(2) == 1 then
sk = fk
end
local f = sk ~= nil
local done = false
return function(t, k)
if done then
return nil
end
local rk, rv = next(t, k)
if rk == nil then
done = true
if f then
f = false
return fk, t[fk] -- note: reindex, t[fk] may have been modified during traversal
end
return nil
end
if f and rawequal(rk, fk) then
f = false
end
return rk, rv
end, tab, sk
end
-- Example:
for i = 1, 10 do
print(i)
for k, v in pairs{a = 1, b = 2, c = 3} do
print(k, v)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment