Я люблю Lua. I love Lua.

Итераторы

Posted in Uncategorized by ilovelua on 29 августа, 2013

После того, как написал несколько раз такой код:

function(command, ...)
	...

	local combos = command.combos

	if combos then
		local deviceCombos = combos[deviceName]

		if deviceCombos then
			for i, combo in ipairs(deviceCombos) do
				-- что-то сделать с combo
				...
			end
		end
	end

	...
end

мне стало понятно, что нужно придумать что-нибудь поизящнее, чем эти двойные проверки. И тут я вспомнил про итераторы! Programming in Lua говорит нам, что итератором может быть функция, которая при каждом вызове возвращает следующий элемент коллекции. Поскольку deviceCombos это массив, а мне при переборе индекс элемента не нужен, то задача упрощается:

function commandCombos(command, deviceName)
	local pos = 0
	local combos
	local deviceCombos

	if command then
		combos = command.combos

		if combos then
			deviceCombos = combos[deviceName]
		end
	end

	return function()
		if deviceCombos then
			pos = pos + 1
			return deviceCombos[pos]
		end
	end
end

Обратите внимание, что переменная pos — это upvalue.
Теперь проверим получившуюся функцию:

local commandNil = nil
local commandNoCombos = {}
local commandEmptyCombos = {
	combos = {},
}
local command = {
	combos = {
		mouse = {
			{key = 'MOUSE1', action = 1},
			{key = 'MOUSE2', action = 2},
		},
		keyboard  = {
			{key = 'A', action = 11},
			{key = 'B', action = 22},
		},
	}
}

for combo in commandCombos(commandNil) do
	assert(false)
end

for combo in commandCombos(commandNoCombos) do
	assert(false)
end

for combo in commandCombos(commandNoCombos) do
	assert(false)
end

for combo in commandCombos(commandEmptyCombos) do
	assert(false)
end

for combo in commandCombos(command, 'mouse') do
	print('~~~mouse', combo.key, combo.action)
end

for combo in commandCombos(command, 'keyboard') do
	print('~~~keyboard', combo.key, combo.action)
end

На выходе ожидаемо получаем:

~~~mouse MOUSE1 1
~~~mouse MOUSE2 2
~~~keyboard A 11
~~~keyboard B 22