Lua - Reverse Iterating over Lists



In order to reverse iterate a list, it is good idea to build an iterator which can return the prev element of the list. Then we can start traversing from the last element in order to iterate list in reverse way. See the following code of the reverse iterator function on the list.

-- iterate through the list
local function reverseIterate(self, current)
   -- if current is nil
   -- set the current as last node
   if not current then
      current = self.last
   -- if current is present
   -- set current as current prev   
   elseif current then
      current = current._prev
   end
   -- return current  
   return current
end

-- return the iterator
function list:reverseIterator()
   return reverseIterate, self, nil
end

We can use list:reverseIterator() function to reverse iterate the list which will return the prev element when invoked in a for loop.

We'll build the list and then add method to insert an element. Finally, using iterate function in a for loop, we'll iterate the list in reverse order.

Step 1: Create List

Create a List with a push method to add an element to the end of the list.

-- List Implementation
list = {}
list.__index = list

-- push an element to the end of the list
function list:push(t)
   -- move till last node    
   if self.last then
      self.last._next = t
      t._prev = self.last
      self.last = t
   else
      -- set the node as first node
      self.first = t
      self.last = t
   end
   -- increment the length of the list
   self.length = self.length + 1
end

Step 2: Using setmetatable

modify list behavior when list is called to push elements.

setmetatable(list, { __call = function(_, ...)
   local t = setmetatable({ length = 0 }, list)
      for _, v in ipairs{...} 
         do t:push(v) 
      end
      return t
   end })

Step 3: Create iterator over list

Create an iterator to navigate through elements of the list.

-- iterate through the list
local function reverseIterate(self, current)
   -- if current is nil
   -- set the current as last node
   if not current then
      current = self.last
   -- if current is present
   -- set current as current prev   
   elseif current then
      current = current._prev
   end
   -- return current  
   return current
end

-- return the iterator
function list:reverseIterator()
   return reverseIterate, self, nil
end

Step 4: Test Iterations on List

In list, we can insert objects, and test the iteration

l = list({A}, {"B"}, {"C"},"D"},{"E"}, {"F"} )
-- iterate throgh entries
for v in l:reverseIterator() do 
   print(v[1]) 
end

Complete Example - Reverse Iterating elements of a List

Following is the complete example of inserting and reverse traversing elements of a list.

main.lua

-- List Implementation
list = {}
list.__index = list

setmetatable(list, { __call = function(_, ...)
   local t = setmetatable({ length = 0 }, list)
      for _, v in ipairs{...} 
         do t:push(v) 
      end
      return t
   end })

-- push an element to the end of the list
function list:push(t)
   -- move till last node    
   if self.last then
      self.last._next = t
      t._prev = self.last
      self.last = t
   else
      -- set the node as first node
      self.first = t
      self.last = t
   end
   -- increment the length of the list
   self.length = self.length + 1
end

-- iterate through the list
local function reverseIterate(self, current)
   -- if current is nil
   -- set the current as last node
   if not current then
      current = self.last
   -- if current is present
   -- set current as current prev   
   elseif current then
      current = current._prev
   end
   -- return current  
   return current
end

-- return the iterator
function list:reverseIterator()
   return reverseIterate, self, nil
end

-- create a new list with values
local l = list({ "Mon" }, { "Tue" }, { "Wed" }, { "Thu" }, { "Fri" })

-- iterate throgh entries
for v in l:reverseIterator() do 
   print(v[1]) 
end

Output

When we run the above code, we will get the following output−

Fri
Thu
Wed
Tue
Mon
Advertisements