A Library in Lua

Artin MohammadiArtin Mohammadi
10 min read

My third post, like the previous two, is related to "adventure" in different programming languages! I don't know how useful this kind of posts can be, but most likely it has its own fans (even a few) and it's interesting for me too...

Lua Programming Language

Since this post is not long in terms of content other than code, I will tell you a little about Lua!

Wikipedia says:

Lua (/ˈluːə/ LOO-ə; from Portuguese: lua [ˈlu(w)ɐ] meaning moon) is a lightweight, high-level, multi-paradigm programming language designed primarily for embedded use in applications. Lua is cross-platform, since the interpreter of compiled bytecode is written in ANSI C, and Lua has a relatively simple C API to embed it into applications.

And:

Lua originated in 1993 as a language for extending software applications to meet the increasing demand for customization at the time. It provided the basic facilities of most procedural programming languages, but more complicated or domain-specific features were not included; rather, it included mechanisms for extending the language, allowing programmers to implement such features. As Lua was intended to be a general embeddable extension language, the designers of Lua focused on improving its speed, portability, extensibility and ease-of-use in development.

Except for the last program that you will see in the next part of this article, almost the rest of the programs are also written in Teal (Lua, but its statically typed version)... I really liked its syntax, the facilities it offers and its good execution speed; but due to the inability to understand the logic of the code, and because it only relies on specified types, it gives ridiculous errors sometimes!

What Did I Make With It?

Lua Itself

Definitely the first one was Fizzbuzz!

io.write("Enter the starting point of the range: ")
startPoint = io.read("*n")

io.write("And the end point: ")
endPoint = io.read("*n")

for i = startPoint, endPoint do
    if i % 15 == 0 then
        print("FizzBuzz")
    elseif i % 5 == 0 then
        print("Buzz")
    elseif i % 3 == 0 then
        print("Fizz")
    else
        print(i)
    end
end

To learn how to take user input, mathematical operators, and function declarations, writing a simple calculator program isn't a bad place to start...

local Calculator = {}

function Calculator:add(a, b)
    return a + b
end

function Calculator:sub(a, b)
    return a - b
end

function Calculator:mul(a, b)
    return a * b
end

function Calculator:div(a, b)
    return a / b
end

function Calculator:mod(a, b)
    return a % b
end

while true do
    io.write("A (a.k.a. first number): ")
    local a = io.read("*n")
    io.write("B (a.k.a. second number): ")
    local b = io.read("*n")

    print(a .. " + " .. b .. " = " .. Calculator:add(a, b))
    print(a .. " - " .. b .. " = " .. Calculator:sub(a, b))
    print(a .. " * " .. b .. " = " .. Calculator:mul(a, b))
    print(a .. " / " .. b .. " = " .. Calculator:div(a, b))
    print(a .. " % " .. b .. " = " .. Calculator:mod(a, b))
    print()
end

A very simple linear search algorithm on the languages ​​I knew (even just their names)...

local langs = {"Python", "Lua", "JavaScript", "Go[lang]", "Zig",
               "C++", "C", "Rust", "TypeScript", "Ruby", "Julia",
               "Clojure", "Scala", "Java", "Ada", "D", "Elixir",
               "Elm", "OCaml", "C#", "F#", "Hack", "Groovy", "Kotlin",
               "Swift", "MATLAB", "R", "Nim", "MoonScript", "Pascal",
               "COBOL", "PHP", "PureScript", "Red", "Raku", "SQL",
               "Solidity", "V", "Vala", "Fortran", "Gleam", "HTML(!)",
               "CSS", "Farr", "Lisp", "Crystal", "Bash", "Chapel",
               "Brainfuck", "JSFuck", "Assembly", "Dart", "LiveScript",
               "Objective-C", "Perl", "Prolog", "SASS", "Smalltalk"}
local target = "MoonScript"

for i = 1, #langs do
    if langs[i] == target then
        print("Hooray! We found it at index " .. i .. ".")
        os.exit(0)
    end
end

print("Sorry, dude!")

Number guessing game with the ability to manipulate the difficulty level:

io.write("Enter the difficulty level: ")
local difficulty = io.read("*n")

if difficulty < 1 then
    print("The difficulty level must be a positive number...")
    os.exit(1)
end

math.randomseed(os.time())

local secretNumber = math.floor(math.random() * 10 ^ difficulty)

for i = 1, 3 do
    io.write("Enter your " .. i .. " guess?! ")
    local guess = io.read("*n")

    if guess == secretNumber then
        print("Hooray!")
        os.exit(0)
    elseif guess < secretNumber then
        print("Greater...")
    else
        print("Smaller...")
    end
end

print("Sorry, loser! The secret number was " .. secretNumber .. "!")

And something that can be very useful, especially for those who have experience working with the functional programming paradigm:

local DEBUG_MODE = arg[1] == "--debug-mode"
local Boost = {}

-- Checks if all elements in a table satisfy a condition.
-- @param {function} func - Function to check on each element.
-- @param {table} tbl - Table to check.
-- @returns {boolean} - True if all elements pass the check, false otherwise.
function Boost.all(func, tbl)
   for _, e in ipairs(tbl) do
      if not func(e) then
         return false
      end
   end
   return true
end

-- Tests the `Boost.all` function.
function Boost.testAll()
   print("🤞 Testing `Boost.all` function...")
   local inputTbl = { 2, 4, 6 }
   local expectedResult = true
   local result = Boost.all(
      function(x)
         return x % 2 == 0
      end,
      inputTbl
   )

   assert(
      result == expectedResult,
      (
         "Result mismatch: " .. "Expected " .. tostring(expectedResult)
         .. " but got " .. tostring(result) .. "!"
      )
   )
   print("✅ Test passed successfully!")
end

-- Checks if any element in a table satisfies a condition.
-- @param {function} func - Function to check on each element.
-- @param {table} tbl - Table to check.
-- @returns {boolean} - True if any element passes the check, false otherwise.
function Boost.any(func, tbl)
   for _, e in ipairs(tbl) do
      if func(e) then
         return true
      end
   end
   return false
end

-- Tests the `Boost.any` function.
function Boost.testAny()
   print("🤞 Testing `Boost.any` function...")
   local inputTbl = { 1, 2, 3, 4, 5 }
   local expectedResult = true
   local result = Boost.any(
      function(x)
         return x % 2 == 0
      end,
      inputTbl
   )

   assert(
      result == expectedResult,
      (
         "Result mismatch: " .. "Expected " .. tostring(expectedResult)
         .. " but got " .. tostring(result) .. "!"
      )
   )
   print("✅ Test passed successfully!")
end

-- Applies a function to each element of a table.
-- @param {function} func - Function to apply to each element.
-- @param {table} tbl - Table to map.
-- @returns {table} - New table with the results of applying func to each element.
function Boost.map(func, tbl)
   local result = {}
   for _, e in ipairs(tbl) do
      table.insert(result, func(e))
   end
   return result
end

-- Tests the `Boost.map` function.
function Boost.testMap()
   print("🤞 Testing `Boost.map` function...")
   local inputTbl = { 0, 1, 2, 3, 4 }
   local expectedTbl = { 0, 1, 4, 9, 16 }
   local resultTbl = Boost.map(
      function(x)
         return x ^ 2
      end,
      inputTbl
   )

   assert(
      #resultTbl == #expectedTbl,
      (
         "Length mismatch: " .. "Expected " .. #expectedTbl
         .. " elements, but got " .. #resultTbl .. "!"
      )
   )
   for i, v in ipairs(resultTbl) do
      assert(
         v == expectedTbl[i],
         "Element mismatch at index " .. i .. "!"
      )
   end
   print("✅ Test passed successfully!")
end

-- Filters elements of a table based on a condition.
-- @param {function} func - Function to filter elements.
-- @param {table} tbl - Table to filter.
-- @returns {table} - New table with elements that pass the filter.
function Boost.filter(func, tbl)
   local result = {}
   for _, e in ipairs(tbl) do
      if func(e) then
         table.insert(result, e)
      end
   end
   return result
end

-- Tests the `Boost.filter` function.
function Boost.testFilter()
   print("🤞 Testing `Boost.filter` function...")
   local inputTbl = { 1, 2, 3, 4, 5, 6 }
   local expectedTbl = { 2, 4, 6 }
   local resultTbl = Boost.filter(
      function(x)
         return x % 2 == 0
      end,
      inputTbl
   )

   assert(
      #resultTbl == #expectedTbl,
      (
         "Length mismatch: " .. "Expected " .. #expectedTbl
         .. " elements, but got " .. #resultTbl .. "!"
      )
   )
   for i, v in ipairs(resultTbl) do
      assert(
         v == expectedTbl[i],
         "Element mismatch at index " .. i .. "!"
      )
   end
   print("✅ Test passed successfully!")
end

-- Finds the first element in a table that satisfies a condition.
-- @param {function} func - Function to find an element.
-- @param {table} tbl - Table to find in.
-- @returns {*} - First element that passes the function, or nil if none.
function Boost.findFirst(func, tbl)
   for _, e in ipairs(tbl) do
      if func(e) then
         return e
      end
   end
   return nil
end

-- Tests the `Boost.findFirst` function.
function Boost.testFindFirst()
   print("🤞 Testing `Boost.findFirst` function...")
   local inputTbl = { 1, 2, 3, 4, 5 }
   local expectedResult = 2
   local result = Boost.findFirst(
      function(x)
         return x % 2 == 0
      end,
      inputTbl
   )

   assert(
      result == expectedResult,
      (
         "Result mismatch: " .. "Expected " .. expectedResult
         .. " but got " .. result .. "!"
      )
   )
   print("✅ Test passed successfully!")
end

-- Reduces a table to a single output by applying a function cumulatively.
-- @param {function} func - Function to apply cumulatively to elements.
-- @param {table} tbl - Table to reduce.
-- @param {*} initial - Initial value for reduction.
-- @returns {*} - Result of reducing the table.
function Boost.reduce(func, tbl, initial)
   local result = initial
   for i = 1, #tbl do
      result = func(result, tbl[i])
   end
   return result
end

-- Tests the `Boost.reduce` function.
function Boost.testReduce()
   print("🤞 Testing `Boost.reduce` function...")
   local inputTbl = { 1, 2, 3, 4, 5 }
   local expectedResult = 15
   local result = Boost.reduce(
      function(a, b)
         return a + b
      end,
      inputTbl,
      0
   )

   assert(
      result == expectedResult,
      (
         "Result mismatch: " .. "Expected " .. expectedResult
         .. " but got " .. result .. "!"
      )
   )
   print("✅ Test passed successfully!")
end

if DEBUG_MODE then
   Boost.testAll()
   print()
   Boost.testAny()
   print()
   Boost.testMap()
   print()
   Boost.testFilter()
   print()
   Boost.testFindFirst()
   print()
   Boost.testReduce()
end

return Boost

In Teal

I have decided to put the codes here without explanation so that you can understand the syntax; but from the names of the functions, you can easily guess what they are about...

local function linearSearch(list: {number}, target: number): integer | nil
    for i = 1, #list do
        if list[i] == target then
            return i
        end
    end
    return nil
end

local list = {5, 1, 2, 3, 8, 9, 4}
local target = 8
print(linearSearch(list, target))
local function binarySearch(list: {number}, target: number): integer
    local low, mid, high: integer, integer, integer = 0, 0, #list

    while low <= high do
        mid = math.ceil((low + high) / 2)

        if list[mid] < target then
            low = mid + 1
        elseif list[mid] > target then
            high = mid - 1
        else
            return mid
        end
    end
    return -1
end

local list: {number} = {1, 2, 3, 4, 5, 6, 7, 8, 9}
local target: number = 4
print("List: " .. table.concat(list, ", ") .. " | Target: " .. target)
print("Index: " .. binarySearch(list, target))

list = {0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50}
target = 40
print("\nList: " .. table.concat(list, ", ") .. " | Target: " .. target)
print("Index: " .. binarySearch(list, target))

local function bubbleSort(list: {number}): {number}
    for i = 1, #list do
        for j = 1, #list - i do
            if list[j] > list[j+1] then
                local temp = list[j]
                list[j] = list[j+1]
                list[j+1] = temp
            end
        end
    end
    return list
end

print(table.concat(bubbleSort({6, 2, 8, 9, 3, 1}), ", "))
local function fakeNaturalLanguageProcessing(text: string): {string}
    local categories: {string: {string}} = {
        angry = {"angry", "mad", "upset", "frustrated", "irritated", "infuriated", "enraged", "outraged"},
        happy = {"happy", "joyful", "cheerful", "delighted", "thrilled", "ecstatic", "elated", "jubilant"},
        sad = {"sad", "depressed", "unhappy", "miserable", "melancholy", "despondent", "gloomy", "somber"},
        surprised = {"surprised", "amazed", "astonished", "stunned", "shocked", "thunderstruck", "flabbergasted", "dumbfounded"},
        fearful = {"fearful", "afraid", "scared", "terrified", "petrified", "apprehensive", "anxious", "nervous"}
    }
    local labels: {string} = {}

    for category, words in pairs(categories) do
        local count: integer = 0

        for _, word in ipairs(words) do
            if string.find(string.lower(text), word) then
                count = count + 1
            end
        end
        if count >= 2 then
            table.insert(labels, category)
        end
    end

    return labels
end

local text: string = [[
I was thrilled to hear the news, but my friend was terrified and scared.
She was so upset and angry that she started crying, feeling sad and miserable.
]]
local labels: {string} = fakeNaturalLanguageProcessing(text)
print(table.concat(labels, ", "))

Last Words

I don't know yet what I want to do with Lua in the future, but maybe later I want to make a game with it for smartphones that...

Useful links:

0
Subscribe to my newsletter

Read articles from Artin Mohammadi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Artin Mohammadi
Artin Mohammadi

A software developer who sometimes creates content — I'm very eager to get a deeper experience by implementing things from scratch.