Lua is a lightweight, high-level, multi-paradigm programming language designed primarily for embedded use in applications.
Variables in Lua are declared simply by assigning a value, e.g., x = 10. The 'local' keyword can be used for local scope.
Global variables are accessible anywhere in the program, while local variables are limited to the block where they are declared using the 'local' keyword.
Single-line comments start with two hyphens (--), and multi-line comments are enclosed within --[[ and ]].
Functions are defined using the 'function' keyword, e.g., function add(a, b) return a + b end.
A table is the main data structure in Lua, used to represent arrays, dictionaries, and objects. It is created using curly braces, e.g., t = {}.
Elements are accessed using the syntax table[key], e.g., t[1] or t['name'].
Lua supports 'while', 'repeat...until', and 'for' loops to iterate over code blocks.
Strings are concatenated using the '..' operator, e.g., 'Hello' .. ' World'.
'nil' is a special value in Lua that represents the absence of a useful value or a non-existent field in a table.
Errors can be handled using the 'pcall' (protected call) function or 'xpcall' for custom error handling.
Metatables allow you to change the behavior of tables, such as operator overloading and custom behaviors for certain operations.
The 'require' function is used to load and run modules, allowing code reuse and modularity.
Arrays are implemented as tables with integer keys, e.g., arr = {1, 2, 3}.
By convention, Lua arrays start at index 1, not 0.
Object-oriented programming in Lua is achieved using tables and metatables. You can define methods as functions within tables and use metatables to simulate inheritance and other OOP features.
'self' refers to the table instance on which a method is called. It allows access to the table's fields and other methods within the same table.
'pairs' iterates over all key-value pairs in a table, including non-integer keys, while 'ipairs' iterates over integer keys in order, stopping at the first nil value.
You can use metatables to set the '__newindex' metamethod, which can prevent or control modifications to a table's fields.
A closure is a function that captures and retains access to variables from its enclosing scope, even after that scope has exited. This allows for powerful constructs like function factories and encapsulation.
Use '...' in the function definition to accept a variable number of arguments. Inside the function, 'select' or '{...}' can be used to access them.
Coroutines provide cooperative multitasking. They allow functions to yield and resume execution, enabling asynchronous programming and complex control flows.
The 'module' function was used to create modules in older Lua versions, but it is deprecated. Modern Lua recommends returning a table from a file to define a module.
Use the 'pairs' function to iterate over all key-value pairs in a table, regardless of key type.
To remove an element, set its key to 'nil'. For arrays, you can use 'table.remove' to shift elements and maintain order.
Tail call optimization allows Lua to reuse the current function's stack frame for a function call that is the last action in a function, preventing stack overflow in recursive functions.
Lua does not have built-in serialization, but you can use libraries like 'serpent' or write custom functions to convert tables to strings and back.
'__index' controls table field access when a key is missing, often used for inheritance. '__newindex' controls behavior when assigning to missing keys, allowing custom logic or protection.
Use 'table.sort' for arrays. You can provide a custom comparison function to define the sorting order.
'require' loads a module only once and caches it, while 'dofile' executes a file every time it is called, without caching.
Weak tables allow Lua to perform automatic garbage collection on their keys and/or values. By setting the metatable's '__mode' field to 'k', 'v', or 'kv', you can create tables with weak keys, values, or both, which is useful for caches and memoization.
Lua uses metatables and the '__index' metamethod to implement inheritance. A common pattern is to set the metatable of a child table to reference a parent table, allowing the child to inherit methods and fields from the parent.
Environments in Lua (prior to 5.2) allowed you to control the global variable scope for chunks and functions using setfenv/getfenv. In Lua 5.2+, environments are replaced by explicit _ENV tables, making variable scope more explicit and flexible.
Sandboxing can be achieved by running code with a restricted environment table (_ENV), removing or replacing dangerous functions, and controlling access to system resources. This limits what the executed code can do, enhancing security.
'load' compiles a chunk from a string or reader function and returns it as a function. 'loadfile' loads and compiles a chunk from a file. 'dofile' loads and immediately executes a file. 'load' and 'loadfile' allow for more control over execution.
Custom iterators are implemented by defining a function that returns an iterator function, an invariant state, and a control variable. The iterator function is called repeatedly by the 'for' loop, producing values until it returns nil.
The debug library provides functions for inspecting and manipulating the runtime state, such as stack inspection, setting hooks, and modifying local variables. It is powerful but can break encapsulation and should be used with caution, especially in production.
Lua does not support true multithreading but provides coroutines for cooperative multitasking. For parallelism, Lua is often embedded in host applications that manage threads or use libraries like Lua Lanes or LuaJIT's FFI.
Memoization caches the results of expensive function calls. In Lua, this is typically implemented using a table to store results keyed by input arguments, often with weak tables to allow garbage collection.
Lua provides a C API for embedding and extending. You can write C functions, register them with Lua, and call them from scripts. Libraries like LuaJIT's FFI or tools like SWIG can simplify binding C libraries to Lua.
Upvalues are external local variables referenced by a function. When a function is defined inside another, it can access variables from the enclosing scope, and these variables become upvalues, enabling closures.
Performance can be improved by minimizing table lookups, using local variables, avoiding unnecessary memory allocations, preallocating tables, and leveraging LuaJIT for just-in-time compilation. Profiling tools can help identify bottlenecks.
Lua uses automatic memory management with incremental garbage collection. The collector reclaims unused memory by tracking object references. You can tune the collector using 'collectgarbage' to balance performance and memory usage.
Lua uses lexical scoping, meaning variable scope is determined by the program's structure at compile time, not by the call stack at runtime. This allows closures to capture variables from their defining environment.
Event-driven programming can be implemented using tables of callback functions, dispatchers, or observer patterns. Libraries and frameworks may provide event loops, but you can also build custom event systems using tables and function references.
Use 'pcall' and 'xpcall' for protected calls, provide meaningful error messages, and structure code to handle errors gracefully. Consider using custom error types and centralized error logging for maintainability.
Functions and closures cannot be serialized directly in standard Lua due to their internal state. You can serialize data and code separately, or use third-party libraries that support limited function serialization, but this is generally discouraged.
Use the built-in debug library or external profilers like LuaProfiler or LuaJIT's profiler to measure function call times and memory usage. Analyze the results to optimize hot spots in your code.
Metatables can define metamethods like '__add', '__sub', '__eq', etc., to customize the behavior of standard operators for tables, enabling operator overloading for user-defined types.
Organize code into modules, use 'require' to load them, and manage dependencies explicitly. Tools like LuaRocks can help manage external libraries and versioning for larger projects.