Help With Understanding Custom Iterator with Coroutines

local function BasePartIterator(Table)
	local Length = #Table;
	local Thread = coroutine.create(function(_, Index)
		if (not Index) then --// If we're not passed an Index, make it 1;
			Index = 1;
		else
			Index = Index + 1; --// Otherwise increase it
		end

		for i = Index, Length do --// From the current Index to the Length
			if (Table[i]:IsA("BasePart")) then
				coroutine.yield(Table[i], i); --// These will be passed back again next iteration
			end
		end

		--// If none is found then it'll return nil, nil stops the for loop iterating
	end)

	return function() --// Iterator
		local Success, BasePart, Index = coroutine.resume(Thread)
		return BasePart, Index;
	end
end

local WorkspaceDescendants = workspace:GetDescendants();
for BasePart, IndexFound in BasePartIterator(WorkspaceDescendants) do
	print(BasePart, IndexFound)
end

In this example from this tutorial, how come only baseparts are printed? Lets say its going through the coroutine and it happens to be something thats not a basepart. So the coroutine isn’t yielded as it didn’t pass the if check, then coroutine.resume didn’t return the correct basepart and index, index being nil as the coroutine wasn’t yielded. So if this is then returned, how come it doesn’t print it out in the output?

I changed the custom iterator loop at the bottom of the script to this:


And it did the exact thing I discussed in the paragraph above, it printed:

So it must be to do with the for in thing magically making the BasePartIterator function work. Can someone please explain this?

Well, the for i = Index, Length do loop would just keep running in the same coroutine.resume until it either finishes without finding any BaseParts (at which point the iteration ends), or it finds a BasePart (at which point it will yield).

1 Like

Ok well how come this doesn’t print a basepart and index:

local function BasePartIterator(Table)
	local Length = #Table;
	local Thread = coroutine.create(function(_, Index)
		if (not Index) then --// If we're not passed an Index, make it 1;
			Index = 1;
		else
			Index = Index + 1; --// Otherwise increase it
		end

		for i = Index, Length do --// From the current Index to the Length
			if (Table[i]:IsA("BasePart")) then
				coroutine.yield(Table[i], i); --// These will be passed back again next iteration
			end
		end

		--// If none is found then it'll return nil, nil stops the for loop iterating
	end)

	return function() --// Iterator
		local Success, BasePart, Index = coroutine.resume(Thread)
		return BasePart, Index;
	end
end

local WorkspaceDescendants = workspace:GetDescendants();

local BasePart, IndexFound = BasePartIterator(WorkspaceDescendants)

print(BasePart, IndexFound)

--for BasePart, IndexFound in BasePartIterator(WorkspaceDescendants) do
	--print(BasePart, IndexFound)
--end

It just prints some weird function thing and nil:

Because BasePartIterator() returns a function and doesn’t return anything else, so it’s like doing

local BasePart, IndexFound = function() end
print(BasePart, IndexFound)

How come it didn’t do that in the first script? Just stop there and return the function?

Because for loops are fancy and special and do things behind the scenes for you, like repeatedly calling the function that you give it until said function returns nil.

1 Like

That makes more sense now thanks, is there a link where I can learn about this in more detail?

https://www.lua.org/pil/7.1.html
https://www.lua.org/pil/7.2.html
https://www.lua.org/pil/7.3.html

2 Likes

Yup! Read through chapter 7 of Programming in Lua.

The whole book is great, also.

1 Like