Moduler i Lua

I denna lektion lär du dig hur du kan organisera din Lua-kod i moduler för att skapa återanvändbara kodbibliotek och strukturera större program.

Vad är moduler?

Moduler är ett sätt att organisera och paketera kod i separata filer så att den blir mer:

  • Återanvändbar - Du kan använda samma modul i flera projekt
  • Hanterbar - Koden blir uppdelad i logiska enheter
  • Enklare att underhålla - Du behöver bara fixa buggar på ett ställe
  • Samarbetsvänlig - Olika personer kan arbeta på olika moduler

I Lua skapar du en modul genom att definiera funktioner och variabler i en fil, och sedan returnera dem som en tabell som kan importeras av andra Lua-program.

Skapa en modul

Så här skapar du en enkel modul i Lua:

-- Filen: matte.lua
-- Detta är en enkel matematikmodul

local matte = {}  -- Skapa en tabell för modulen

-- Funktion för att beräkna summan av en array
function matte.summa(arr)
    local sum = 0
    for _, v in ipairs(arr) do
        sum = sum + v
    end
    return sum
end

-- Funktion för att beräkna medelvärdet av en array
function matte.medelvärde(arr)
    return matte.summa(arr) / #arr
end

-- Funktion för att hitta minsta värdet i en array
function matte.min(arr)
    local minst = arr[1]
    for i = 2, #arr do
        if arr[i] < minst then
            minst = arr[i]
        end
    end
    return minst
end

-- Funktion för att hitta största värdet i en array
function matte.max(arr)
    local störst = arr[1]
    for i = 2, #arr do
        if arr[i] > störst then
            störst = arr[i]
        end
    end
    return störst
end

-- Konstanter
matte.PI = 3.14159265359
matte.E = 2.71828182846

-- Returnera modultabellen
return matte
Obs! Notera att modulen skapar en lokal tabell, lägger till funktioner och konstanter i den, och sedan returnerar hela tabellen.

Använda en modul

För att använda en modul i ditt Lua-program kan du använda funktionen require:

-- Importera vår matematikmodul
local matte = require("matte")

-- Skapa en array med testdata
local data = {12, 45, 3, 22, 87, 9, 31}

-- Använd funktionerna från modulen
print("Summa: " .. matte.summa(data))
print("Medelvärde: " .. matte.medelvärde(data))
print("Minsta värdet: " .. matte.min(data))
print("Största värdet: " .. matte.max(data))

-- Använd konstanter från modulen
print("Cirkelns omkrets med radie 5: " .. 2 * matte.PI * 5)

När du kör require("matte") kommer Lua att:

  1. Kolla om modulen redan är laddad
  2. Om inte, söka efter filen matte.lua i flera möjliga platser
  3. Köra koden i modulen om den hittas
  4. Cacha resultatet för framtida anrop
  5. Returnera modultabellen

Modulsökvägar

När du använder require för att ladda en modul, söker Lua efter filen i flera platser. Dessa platser definieras av variabeln package.path:

-- Visa alla platser Lua söker efter moduler
print(package.path)

Typiskt innehåller package.path sökvägar som:

  • ./?.lua - Sök i aktuell mapp
  • /usr/local/share/lua/5.4/?.lua - Systemmappar (på Linux/Mac)
  • C:\Program Files\Lua\5.4\?.lua - Installationsplatser (på Windows)

Du kan lägga till egna sökvägar:

-- Lägg till en sökväg för dina egna moduler
package.path = package.path .. ";/mina/moduler/?.lua"

Moduler med privata funktioner

Ibland vill du ha privata funktioner i din modul som bara används internt och inte är tillgängliga för användare av modulen:

-- Filen: geometri.lua
local geometri = {}

-- Privat hjälpfunktion (inte tillgänglig utanför modulen)
local function kvadrat(x)
    return x * x
end

-- Offentlig funktion (tillgänglig för användare av modulen)
function geometri.cirkelArea(radie)
    return math.pi * kvadrat(radie)  -- Använder den privata funktionen
end

function geometri.sfärVolym(radie)
    return (4/3) * math.pi * kvadrat(radie) * radie
end

-- Returnera endast den offentliga delen
return geometri

Användning:

local geometri = require("geometri")

print(geometri.cirkelArea(5))  -- Fungerar!
print(geometri.kvadrat(5))     -- Fel: 'kvadrat' är privat

Moduler i underkatalog

Du kan organisera större projekt i underkataloger:

-- Filen: utils/string.lua
local stringUtils = {}

function stringUtils.capitalize(str)
    return str:sub(1,1):upper() .. str:sub(2)
end

function stringUtils.split(str, sep)
    sep = sep or "%s"  -- Standardseparator är whitespace
    local t = {}
    for s in string.gmatch(str, "([^"..sep.."]+)") do
        table.insert(t, s)
    end
    return t
end

return stringUtils

För att använda denna modul:

-- Notera punktnotationen för underkatalog
local strUtils = require("utils.string")

local text = "hej på dig"
print(strUtils.capitalize(text))  -- Skriver ut: "Hej på dig"

local words = strUtils.split(text, " ")
for i, word in ipairs(words) do
    print(i .. ": " .. word)
end
Observera! På vissa system behöver du använda slash (/) istället för punkt (.) i require-anropet.

Övningar

Övning 1: Text-utilities modul

Skapa en textbearbetningsmodul med följande funktioner:

  1. En funktion wordCount som räknar antal ord i en sträng
  2. En funktion reverse som vänder på en sträng
  3. En funktion isPalindrome som kontrollerar om en sträng är ett palindrom (samma framåt som bakåt)
  4. En funktion countVowels som räknar antal vokaler i en sträng

Skapa sedan ett testprogram som använder din modul för att testa alla funktioner.

Lösningsförslag:

-- textUtils.lua
local textUtils = {}

-- Räkna antal ord i en text
function textUtils.wordCount(text)
    local count = 0
    for _ in string.gmatch(text, "%S+") do
        count = count + 1
    end
    return count
end

-- Vänd på en sträng
function textUtils.reverse(text)
    local result = ""
    for i = #text, 1, -1 do
        result = result .. string.sub(text, i, i)
    end
    return result
end

-- Kontrollera om en sträng är ett palindrom
function textUtils.isPalindrome(text)
    -- Ta bort mellanslag och konvertera till gemener för bättre jämförelse
    text = string.lower(string.gsub(text, "%s+", ""))
    local reversed = textUtils.reverse(text)
    return text == reversed
end

-- Räkna vokaler i en text
function textUtils.countVowels(text)
    local count = 0
    for _ in string.gmatch(string.lower(text), "[aeiouyåäö]") do
        count = count + 1
    end
    return count
end

return textUtils

-- test_textUtils.lua (separat fil för test)
local textUtils = require("textUtils")

-- Testa wordCount
local text1 = "Detta är en exempeltext med åtta ord."
print("Antal ord: " .. textUtils.wordCount(text1))

-- Testa reverse
local text2 = "Hej Världen!"
print("Original: " .. text2)
print("Omvänd: " .. textUtils.reverse(text2))

-- Testa isPalindrome
local palindrom1 = "ni talar bra latin"
local palindrom2 = "En dum gud med nalle kallas för nalle med mude ne"
local notPalindrom = "Detta är inte ett palindrom"

print(palindrom1 .. " är palindrom: " .. tostring(textUtils.isPalindrome(palindrom1)))
print(palindrom2 .. " är palindrom: " .. tostring(textUtils.isPalindrome(palindrom2)))
print(notPalindrom .. " är palindrom: " .. tostring(textUtils.isPalindrome(notPalindrom)))

-- Testa countVowels
local text3 = "Denna text innehåller sexton vokaler!"
print("Antal vokaler: " .. textUtils.countVowels(text3))

Övning 2: Calculator Modul

Skapa en matematikmodul som tillhandahåller funktioner för:

  1. De fyra grundläggande räknesätten (addition, subtraktion, multiplikation, division)
  2. Beräkning av fakultet (n!)
  3. Beräkning av potens (x^y)
  4. Konvertering mellan radianer och grader
  5. Modulen ska ha minst två konstanter (t.ex. Pi och E)

Skriv ett testprogram som demonstrerar alla funktioner i modulen.

Lösningsförslag:

-- calculator.lua
local calculator = {}

-- Grundläggande matematiska operationer
function calculator.add(a, b)
    return a + b
end

function calculator.subtract(a, b)
    return a - b
end

function calculator.multiply(a, b)
    return a * b
end

function calculator.divide(a, b)
    if b == 0 then
        error("Division med noll är inte tillåtet")
    end
    return a / b
end

-- Beräkna fakultet av n
function calculator.factorial(n)
    if n < 0 then
        error("Fakultet är inte definierad för negativa tal")
    end
    if n == 0 or n == 1 then
        return 1
    end
    local result = 1
    for i = 2, n do
        result = result * i
    end
    return result
end

-- Beräkna x upphöjt till y
function calculator.power(x, y)
    return x ^ y
end

-- Konvertera från grader till radianer
function calculator.degToRad(degrees)
    return degrees * (calculator.PI / 180)
end

-- Konvertera från radianer till grader
function calculator.radToDeg(radians)
    return radians * (180 / calculator.PI)
end

-- Matematiska konstanter
calculator.PI = 3.141592653589793
calculator.E = 2.718281828459045

return calculator

-- test_calculator.lua (separat fil för test)
local calc = require("calculator")

-- Testa grundläggande operationer
print("Addition: 5 + 3 = " .. calc.add(5, 3))
print("Subtraktion: 10 - 4 = " .. calc.subtract(10, 4))
print("Multiplikation: 6 * 7 = " .. calc.multiply(6, 7))
print("Division: 20 / 5 = " .. calc.divide(20, 5))

-- Testa fakultet
print("Fakultet av 5 (5!) = " .. calc.factorial(5))

-- Testa potens
print("2^8 = " .. calc.power(2, 8))

-- Testa konvertering mellan grader och radianer
local angle_deg = 180
local angle_rad = calc.degToRad(angle_deg)
print(angle_deg .. " grader = " .. angle_rad .. " radianer")
print(angle_rad .. " radianer = " .. calc.radToDeg(angle_rad) .. " grader")

-- Visa konstanter
print("PI = " .. calc.PI)
print("E = " .. calc.E)