diff --git a/README.md b/README.md index bcad025..44af317 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # cheat-sheet-lua -Here is the IO-Project cheat sheet to quickly learn the "Lua" programming language +Here is the IO-Project cheat sheet to quickly learn the "Lua" programming language. +I suggest you to consult the documentation of lua for more information on the language, it is available by following [this link](https://www.lua.org/manual/5.3/). ## Table of contents @@ -8,7 +9,7 @@ Use this table of contents to travel more easily through this cheat sheet. - [cheat-sheet-lua](#cheat-sheet-lua) - [Table of contents](#table-of-contents) - - [The basics](#the-basics) + - [Basics](#basics) - [Code comments](#code-comments) - [Variables and loop](#variables-and-loop) - [Functions](#functions) @@ -16,8 +17,13 @@ Use this table of contents to travel more easily through this cheat sheet. - [Tables, Array, dict..](#tables-array-dict) - [Metatables and metamethods](#metatables-and-metamethods) - [Class-like tables and inheritance.](#class-like-tables-and-inheritance) + - [Coroutine](#coroutine) + - [Error handling](#error-handling) + - [Modules](#modules) -## The basics +## Basics + +> We'll now introduce the basics of lua, starting with comments, variables, loops and functions. ### Code comments @@ -163,14 +169,13 @@ print 'hello' -- Works fine. ## Advanced +> Let's move on to more advanced notions. With the notion of table, class, module, coroutine, meta-programming and module. + ### Tables, Array, dict.. -````lua --- Tables = Lua's only compound data structure; --- they are associative arrays. --- Similar to php arrays or js objects, they are --- hash-lookup dicts that can also be used as lists. +Tables are the only compound data structure in Lua, they are associative arrays. Similar to php arrays or js objects, they are hash-lookup dicts that can also be used as lists. +````lua -- Using tables as dictionaries / maps: -- Dict literals have string keys by default: @@ -218,11 +223,9 @@ end #### Metatables and metamethods -````lua --- A table can have a metatable that gives the table --- operator-overloadish behavior. Later we'll see --- how metatables support js-prototypey behavior. +A table can have a metatable that gives the table operator-overloadish behavior. Later we'll see how metatables support js-prototypey behavior. +````lua f1 = {a = 1, b = 2} -- Represents the fraction a/b. f2 = {a = 2, b = 3} @@ -282,4 +285,194 @@ eatenBy = myFavs.animal -- works! thanks, metatable -- __call(a, ...) for a(...) ```` -#### Class-like tables and inheritance. \ No newline at end of file +#### Class-like tables and inheritance. + +Classes aren't built in, there are different ways to emulate them with tables and metatables. +The different ways to define a class in Lua are not easy to understand, so I suggest you to look at the [following document](support/class.lua) implementing 3 types of class definition. The last one being the one I chose (my preferred method). + +````lua +-- Explanation for this example is below it. +Dog = {} -- 1. + +function Dog:new() -- 2. + newObj = {sound = 'woof'} -- 3. + self.__index = self -- 4. + return setmetatable(newObj, self) -- 5. +end + +function Dog:makeSound() -- 6. + print('I say ' .. self.sound) +end + +mrDog = Dog:new() -- 7. +mrDog:makeSound() -- 'I say woof' -- 8. + +-- 1. Dog acts like a class; it's really a table. +-- 2. function tablename:fn(...) is the same as +-- function tablename.fn(self, ...) +-- The : just adds a first arg called self. +-- Read 7 & 8 below for how self gets its value. +-- 3. newObj will be an instance of class Dog. +-- 4. self = the class being instantiated. Often +-- self = Dog, but inheritance can change it. +-- newObj gets self's functions when we set both +-- newObj's metatable and self's __index to self. +-- 5. Reminder: setmetatable returns its first arg. +-- 6. The : works as in 2, but this time we expect +-- self to be an instance instead of a class. +-- 7. Same as Dog.new(Dog), so self = Dog in new(). +-- 8. Same as mrDog.makeSound(mrDog); self = mrDog. + +---------------------------------------------------- + +-- Inheritance example: +LoudDog = Dog:new() -- 1. + +function LoudDog:makeSound() + s = self.sound .. ' ' -- 2. + print(s .. s .. s) +end + +seymour = LoudDog:new() -- 3. +seymour:makeSound() -- 'woof woof woof' -- 4. + +-- 1. LoudDog gets Dog's methods and variables. +-- 2. self has a 'sound' key from new(), see 3. +-- 3. Same as LoudDog.new(LoudDog), and converted to +-- Dog.new(LoudDog) as LoudDog has no 'new' key, +-- but does have __index = Dog on its metatable. +-- Result: seymour's metatable is LoudDog, and +-- LoudDog.__index = LoudDog. So seymour.key will +-- = seymour.key, LoudDog.key, Dog.key, whichever +-- table is the first with the given key. +-- 4. The 'makeSound' key is found in LoudDog; this +-- is the same as LoudDog.makeSound(seymour). + +-- If needed, a subclass's new() is like the base's: +function LoudDog:new() + newObj = {} + -- set up newObj + self.__index = self + return setmetatable(newObj, self) +end +```` + +### Coroutine + +Let's turn now to coroutines. Coroutines are functions that can be suspended and resumed at a later time. They are used to implement iterators, generators and event loops and represent a line of execution with its own stack. In other words, they can be compared to threads. + +````lua +-- Create a coroutine that prints 'Hello' and then stops. +coHi = coroutine.create(function () print('Hello') end) +print(coHi) -- thread: 0x7f9c0c00a0c0 + +-- Coroutine status can be 'suspended', 'running' or 'dead'. +-- The coroutine is created in the 'suspended' state. + +-- Resume the coroutine. It will print 'Hello' and stop. +coroutine.resume(coHi) -- return 'true' +print(coroutine.status(coHi)) -- 'dead' + +-- We can also pass arguments to the coroutine. +-- The arguments of the first resume are passed to the +-- function of the coroutine. The following arguments +-- are passed to the yield function. +routine = coroutine.create(function (a, b, c) + print('first print: ', a, b, c) + print('yield1: ', coroutine.yield()) + print('yield2: ', coroutine.yield('a variable')) + return(a+b+c) +end) + +-- will run the coroutine until the first yield. +coroutine.resume(routine, 1, 2, 3) +-- run the coroutine until the second yield passing +-- the arguments 4, 5 and 6 to the 1er yield and +-- retrieve the return value of the second yield. +print('out routine: ' , coroutine.resume(routine, 4, 5, 6)) +-- this will run out the second yield and made the +-- adition of 'a+b+c' and kill the coroutine. +print(coroutine.resume(routine, 7, 8, 9)) -- 1+2+3 = '6' + +-- All these steps will print: +--[[ + first print: 1 2 3 + yield1: 4 5 6 + out routine: true a variable + yield2: 7 8 9 + true 6 +--]] +```` + +### Error handling + +Lua allows low-level error handling with the `error` function and high-level error handling with the `assert` function. The `error` function raises an error and handles it with the `pcall` or `xpcall` function. The `assert` function checks a condition and raises an error if the condition is not met. + +````lua + + + +-- Throw an error if the first argument is false +-- the second argument is the error message. +assert(type(firstvariable) = 'string', 'not a string') + +-- TODO to continue + +```` + +### Modules + +Modules are a way to organize your code. They are a way to group functions and variables together in a single file. You can then use the module in other files by using the `require` function. + +````lua +-- Suppose the file mod.lua looks like this: +local M = {} + +local function sayMyName() + print('Hrunkner') +end + +function M.sayHello() + print('Why hello there') + sayMyName() +end + +return M -- Return the table M. + +-- Another file can use mod.lua's functionality: +local mod = require('mod') -- Run the file mod.lua. + +-- require is the standard way to include modules. +-- require acts like: (if not cached; see below) +local mod = (function () + +end)() +-- It's like mod.lua is a function body, so that +-- locals inside mod.lua are invisible outside it. + +-- This works because mod here = M in mod.lua: +mod.sayHello() -- Says hello to Hrunkner. + +-- This is wrong, sayMyName only exists in mod.lua: +mod.sayMyName() -- error + +-- require's return values are cached so a file is +-- run at most once, even when require'd many times. + +-- Suppose mod2.lua contains "print('Hi!')". +local a = require('mod2') -- Prints Hi! +local b = require('mod2') -- Doesn't print; a=b. + +-- dofile is like require without caching: +dofile('mod2.lua') --> Hi! +dofile('mod2.lua') --> Hi! (runs it again) + +-- loadfile loads a lua file but doesn't run it yet. +f = loadfile('mod2.lua') -- Call f() to run it. + +-- loadstring is loadfile for strings. +g = loadstring('print(343)') -- Returns a function. +g() -- Prints out '343', nothing printed before now. + +```` + diff --git a/support/class.lua b/support/class.lua new file mode 100644 index 0000000..39c3fd1 --- /dev/null +++ b/support/class.lua @@ -0,0 +1,102 @@ + +-- ============================================================================ +-- This is a simple Person class (Raw example without synthetic sugar). +-- ============================================================================ +Person1 = {} + +-- Constructor +function Person1.new(self, name, age) + local o = {} + self.__index = self + setmetatable(o, self) + o.name = name + o.age = age + return o +end + +-- Methods +function Person1.getName(self) + return self.name +end + +function Person1.getAge(self) + return self.age +end + +-- Usage +local instance = Person1.new(Person1, "John", 30) +print('name: ' .. instance.getName(instance)) -- John +print('age: ' .. instance.getAge(instance)) -- 30 + +-- Garbage collection +Person1 = nil +instance = nil + + +-- ============================================================================ +-- This is a simple Person class (Synthetic sugar example). +-- ============================================================================ +Person2 = {} + +-- Constructor +function Person2:new(name, age) + local o = {} + self.__index = self + setmetatable(o, self) + o.name = name + o.age = age + return o +end + +-- Methods +function Person2:getName() + return self.name +end + +function Person2:getAge() + return self.age +end + +-- Usage +local instance = Person2:new("John Doe", 50) +print('name: ' .. instance:getName()) -- John Doe +print('age: ' .. instance:getAge()) -- 50 + +-- Garbage collection +Person2 = nil +instance = nil + + +-- ============================================================================ +-- This is my class definition method (with Synthetic sugar example). +-- ============================================================================ +Person = {} + +-- Constructor +function Person:new(name, age) + local this = setmetatable({}, {__index = self}) + this.name = name or 'EndMove' + this.age = age or 20 + return this +end + +-- Methods +function Person:getFName() + return 'name: ' .. self.name +end + +function Person:getFAge() + return 'age: ' .. self.age +end + +-- Usage +local instance = Person:new("Gashy", 21) +print(instance:getFName()) -- name: Gashy +print(instance:getFAge()) -- age: 21 + +-- Garbage collection +Person = nil +instance = nil + +-- ============================================================================ +-- Thanks for reading! \ No newline at end of file diff --git a/support/while.lua b/support/while.lua index a8e2d17..0b551dd 100644 --- a/support/while.lua +++ b/support/while.lua @@ -1,3 +1,8 @@ + +-- ============================================================================ +-- Let's study the different loops offered by the Lua language. +-- ============================================================================ + -- Jeb while loop local jebSum = 0 print('jeb : '..jebSum) @@ -38,4 +43,7 @@ while true do print('Breaking out in') if true then break end end -print('Breaking out -> Done') \ No newline at end of file +print('Breaking out -> Done') + +-- ============================================================================ +-- Thanks for reading! \ No newline at end of file