{"id":15647407,"url":"https://github.com/stevedonovan/lua-gentle-intro","last_synced_at":"2025-06-25T07:34:10.182Z","repository":{"id":137108621,"uuid":"89711672","full_name":"stevedonovan/lua-gentle-intro","owner":"stevedonovan","description":null,"archived":false,"fork":false,"pushed_at":"2017-04-28T14:08:22.000Z","size":6,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-30T13:13:22.162Z","etag":null,"topics":["lua","tutorial"],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stevedonovan.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-04-28T14:03:30.000Z","updated_at":"2024-05-02T06:56:53.000Z","dependencies_parsed_at":null,"dependency_job_id":"fa400e02-c3f7-43c4-b485-fe514f896d1d","html_url":"https://github.com/stevedonovan/lua-gentle-intro","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/stevedonovan/lua-gentle-intro","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevedonovan%2Flua-gentle-intro","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevedonovan%2Flua-gentle-intro/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevedonovan%2Flua-gentle-intro/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevedonovan%2Flua-gentle-intro/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stevedonovan","download_url":"https://codeload.github.com/stevedonovan/lua-gentle-intro/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevedonovan%2Flua-gentle-intro/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261827411,"owners_count":23215741,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["lua","tutorial"],"created_at":"2024-10-03T12:19:21.115Z","updated_at":"2025-06-25T07:34:10.154Z","avatar_url":"https://github.com/stevedonovan.png","language":null,"readme":"## Values and Expressions\n\nLua can be started from the command-line and used as a calculator:\n\n```lua\n$\u003e lua53\nLua 5.3.0  Copyright (C) 1994-2014 Lua.org, PUC-Rio\n\u003e 2*5 + 3\n13\n\u003e 2*(5 + 2)\n14\n\u003e 5^2 + 1\n26.0\n```\n\nRegular numbers are the simplest kind of _value_, and can be combined into\n_expressions_ using _operators_.  `+`,`*` and `^` (meaning 'to the power of') are common\narithmetic operators.\n\nIt would not be a very good scientific calculator if the standard mathematical functions\nwere not available:\n\n```lua\n\u003e math.sin(0.4) + 1\n1.3894183423087\n```\n\nThis function takes a number value, and calculates the sine of that number in radians.\nThe conversion from degrees is straightforward, so we will calculate it and give that\nvalue a name:\n\n```lua\n\u003e d2r = math.pi/180\n\u003e math.sin(90*d2r)\n1.0\n```\n\n`d2r` is called a _variable_, and giving it a value is called _assignment_. You\nshould not pronounce `=` \"equals\" but rather call it \"becomes\" or something like that; it\ndoes not mean checking for equality!  This is where standard programming notation\nis different from mathematics. Otherwise this would make no sense:\n\n```lua\n\u003e x = 1\n\u003e x = x + 1\n\u003e x\n2\n```\n\nFunctions are a kind of value as well, so you can define shortcuts:\n\n```lua\n\u003e s = math.sin\n\u003e c = math.cos\n\u003e s(x)^2 + c(x)^2\n1.0\n```\n\nComparisons between values involve another kind of expression involving\n_conditional_ operators. Note `==` here does mean 'test for equality':\n\n```lua\n\u003e 10 \u003e 12\nfalse\n\u003e 42 == 42\ntrue\n\u003e 5 \u003c 7\ntrue\n\n```\nThese are often called 'boolean' operators after the English logician George Boole,\nand the resulting value is called 'boolean' in Lua.\n\n## Tables\n\n_Arrays_ are an important kind of value in programming languages. They store\na sequence of values. They have a size which is returned by the `#` operator,\nand can be _indexed_ using a integer value:\n\n```lua\n\u003e arr = {10,20,30}\n\u003e #arr\n3\n\u003e arr[1]\n10\n\u003e arr[3]\n30\n\u003e arr[20]\nnil\n```\n\nNote that Lua arrays go from 1 to the length of the array. It is _not_ an error\nto index outside that range - you will simply get `nil`. This is a special value\nmeaning 'no value'. This is also the value of an _undefined_ variable:\n\n```lua\n\u003e frodo\nnil\n```\n\n_Maps_ (often called _associative arrays_ or _lookup tables_) are also indexed but by arbitrary\nvalues. They consist of _key/value_ pairs.\n\n```lua\n\u003e map = {one=1,two=2,three=3}\n\u003e map['one']\n1\n\u003e map.one\n1\n\u003e map.four\nnil\n\u003e map.four = 4\n\u003e map.four\n4\n```\n\nNote an important feature of Lua maps - you can use `[]` as before (except with\nstrings in this case) but `map.key` is exactly the same as `map['key']`. And if\nthe key is not present in the map, then the result is (again) `nil`.\n\nIn Lua, both maps and arrays are called _tables_.  We used `math.sin` to calculate\nthe sine of a number, and `math` is **just a table** of functions. Generally\nyou would use a table either as an array or a map, but they can be mixed.\n\nTables are a distinct _type_, which is the proper term for 'kind of value'.\nIt is an error to pass the wrong type to a\nfunction expecting a number:\n\n```\n\u003e math.sin(map)\nstdin:1: bad argument #1 to 'sin' (number expected, got table)\nstack traceback:\n\t[C]: in function 'math.sin'\n\tstdin:1: in main chunk\n\t[C]: in ?\n```\n\n## Strings\n\nStrings are quoted text like \"hello\".  You can quote with either double or single\nquotes, Lua does not care. The length operator `#` will give the size in bytes:\n\n```lua\n\u003e s = 'hello'\n\u003e #s\n5\n```\n\nBy _byte_ I mean a 8-bit value (the smallest addressable unit of memory); for\nASCII text, a byte _is_ a character, but in general a character may be several bytes.\nUnlike tables, you cannot index a string. You cannot modify a string either, it\nis said that they are _immutable_.\n\nStrings are a distinct type of value, and so you get a type error when using\nthem in the wrong way. But if they represent a number, then `tonumber` will convert\nthat string into a number.\n\n```lua\n\u003e math.sin(s)\nstdin:1: bad argument #1 to 'sin' (number expected, got string)\n...\n\u003e s = \"42\"\n\u003e tonumber(s)\n42\n```\nLike real-world strings, they can be combined or _concatenated_ using `..`. Or\nyou can join a table of strings and numbers together using `table.concat`:\n\n```lua\n\u003e hello = \"hello\"\n\u003e world = \"world\"\n\u003e hello..\" \"..world\nhello world\n\u003e  table.concat({\"hello\",\"world\",42},' ')\nhello world 42\n```\nYou can extract a part of a string using `string.sub`:\n\n```rust\n\u003e string.sub(hello,1,3)\nhel\n```\nThe original string is not changed in any way, but a 'slice' is copied to a new string.\n\n\n## What is Programming?\n\nUp to now, Lua is  a convenient calculator. The concepts of value, expression,\nvariable and assignment are important programming concepts, but we haven't started\nprogramming yet!\n\n  - defining our own functions\n  - doing different things based on some condition\n  - repeating things until some condition is true\n\nNow it's time to introduce _statements_. An expression always has a value; an\nassignment like `x = 2` is a statement and has no value.\n\nTo conditionally perform some action, use the `if` statement:\n\n```lua\n\u003e x = 2\n\u003e if x \u003e 1 then print(\"ok\") end\nok\n```\n\nThese statements are generally `if EXPRESSION then STATEMENTS end`. The words\n`if`,`then` and `end` are _keywords_, and these are reserved words in Lua. You\ncannot use keywords for variables.\n\nThe `if` statement can also have an `else` clause:\n\n```lua\n\u003e if x \u003e 1 then print(\"ok\") else print(\"nope\") end\n```\n\nTo repeat things with a range of values, use the `for` statement:\n\n```lua\n\u003e for i = 1,5 do print(i) end\n1\n2\n3\n4\n5\n```\nAt this point, it's time to start writing programs. Type this into a text editor:\n\n```lua\n-- a comment, ignored by Lua. This is 'hello.lua'\nfor i = 1,5 do\n   print('hello',i)\nend\n```\n\nand then\n\n```lua\n$\u003e lua53 hello.lua\nhello\t1\nhello\t2\nhello\t3\nhello\t4\nhello\t5\n```\n\nIt's important to format code nicely, because it makes it easier to read. Anything\nbetween the `do` and the `end` should be indented. Tabs, spaces, it doesn't matter: just\nbe consistent. Use a [text editor](http://lua-users.org/wiki/LuaEditorSupport)\nthat understands this (I'm personally a fan of [Geany](http://www.geany.org/)).\n\nCan now add an `if` statement:\n\n```lua\n-- goodbye.lua\nfor i = 1,5 do\n   if i \u003e 2 then\n      print('goodbye',i)\n   else\n      print('hello',i)\n   end\nend\n```\n\nwhich gives us:\n\n```\nhello\t1\nhello\t2\ngoodbye\t3\ngoodbye\t4\ngoodbye\t5\n```\n\nAgain, indent each statement properly. You _could_ put this all on one line but\npeople will hate you.\n\nEvery Lua program has a predefined table called 'arg' containing the\n_command-line arguments_ passed to the program:\n\n```lua\n$\u003e cat args.lua\n-- args.lua\nfor i = 1,#arg do\n   print(i,arg[i])\nend\n$\u003e lua53 args.lua one 42 'hello dolly'\n1\tone\n2\t42\n3\thello dolly\n```\n\nYou can use `tonumber` to convert the first argument to a number. We will use\n`io.write` which works like `print` except it doesn't end the line:\n\n```lua\n$\u003e cat for.lua\n-- for.lua\nn = tonumber(arg[1])\nfor i = 1,n do\n     io.write('hi ')\nend\nprint()\n$\u003e lua53 for.lua 5\nhi hi hi hi hi\n```\n\nThere's another function in the `io` table which allows us to easily loop over\nall the lines in a file. We assign 1 to `i`, and then _increment_ `i` by one each time.\n\n\n```lua\n-- lines.lua\nfile = arg[1]\ni = 1\nfor line in io.lines(file) do\n   print(i,line)\n   i = i + 1\nend\n```\n\nAnd the output:\n\n```\n$\u003e lua53 lines.lua lines.lua\n1\t-- lines.lua\n2\tfile = arg[1]\n3\ti = 1\n4\tfor line in io.lines(file) do\n5\t   print(i,line)\n6\t   i = i + 1\n7\tend\n```\n\nThis is other form of the `for` statement, which works with _iterators_.\nHere's another iterator function `pairs`. It gives all the key/value pairs in\na table - in this case the predefined table `math`:\n\n```lua\n\u003e for k,v in pairs(math) do print(k,v) end\nmin\tfunction: 0x41e1d0\ntan\tfunction: 0x41dfb0\nmodf\tfunction: 0x41e5d0\nmaxinteger\t9223372036854775807\nasin\tfunction: 0x41e4a0\nceil\tfunction: 0x41e580\nrad\tfunction: 0x41df50\nrandom\tfunction: 0x41e080\nmininteger\t-9223372036854775808\nfloor\tfunction: 0x41e690\nhuge\tinf\nmax\tfunction: 0x41e260\nsqrt\tfunction: 0x41dfe0\npi\t3.1415926535898\ntointeger\tfunction: 0x41e6e0\natan\tfunction: 0x41e440\nabs\tfunction: 0x41e760\nsin\tfunction: 0x41e020\nacos\tfunction: 0x41e4d0\nrandomseed\tfunction: 0x41e050\nlog\tfunction: 0x41e2f0\nult\tfunction: 0x41e3a0\ncos\tfunction: 0x41e410\nfmod\tfunction: 0x41e7d0\ntype\tfunction: 0x41e500\ndeg\tfunction: 0x41df80\nexp\tfunction: 0x41e3e0\n```\n\n(For more information, go to [Mathematical Functions](https://www.lua.org/manual/5.3/manual.html#6.7)\nin the Lua manual.)\n\n## Defining Functions\n\nIn mathematics, simple functions take a value from a set of all possible inputs,\nthe _domain_ and output a value from a set called its _range_. Or, we _apply_ a\nfunction to any value from the _domain_ and get a value from the _range_.\nSo the simplest form of 'sine' goes from all real numbers to the range (-1,+1).\n\nIn programming we _call_ a function and _pass_ it an _argument_. It will then\n_return_ a value. These words may seem strange (they are not how we talk about\nmathematical functions) and come from the early days of programming, when\n'calling' a 'subprogram' involved saving your position in memory and jumping to\na new position. The subprogram would do its work, and then would 'return' to\nthe saved original position.\n\nThe keyord `function` defines a function in Lua; it is followed by the name,\nand then the names of arguments. Like `for` and `if`, any number\nof statements may appear afterwards ending with `end`. The `return` statement\npasses the value back explicitly:\n\n```lua\n-- sqr.lua\nfunction sqr(x)\n    print('x is',x)\n    return x * x\nend\n\ny = sqr(10)\nprint('y is',y)\nprint('x is',x)\n-- output\n-- x is 10\n-- y is 100\n-- x is nil\n```\n\nThe value 10 is assigned to the variable `x` in the function `sqr`, and we get\nback 100.\n\nNote that `x` is `nil` outside the function! That's to say, it's undefined.\n\nConsider these lines in the Lua Prompt:\n\n```lua\n\u003e sum = 0\n\u003e for i = 1,50 do sum = sum + i end\n\u003e sum\n1275\n\u003e = i\nnil\n```\nAgain, `i` is only defined inside the loop, between the `do` and `end`.\n\nThese are examples of _local_ variables, which are only visible in a particular\npart of a program; _global_ variables are visible everywhere.  Life without local\nvariables would be a mess, honestly. Consider this more readable version:\n\n```lua\nfunction sum_numbers (n)\n    sum = 0\n    for i = 1,n do\n        sum = sum + i\n    end\n    return sum\nend\n```\n\nIt does the job, sure, but every time we call it, it updates a global variable\ncalled `sum`.  Now there are only so many meaningful names you can give to\nvariables (without `stupid_long_names_creating_confusion`) and you do not want\nthe insides of functions 'escaping' like this!\nSetting the global variable `sum` is a _side effect_ and is\nsomething to be avoided.\n\nSo this is better:\n\n```lua\nfunction sum_numbers (n)\n    local sum = 0\n    for i = 1,n do\n        sum = sum + i\n    end\n    return sum\nend\n```\nThe keyword `local` _declares_ a variable, and usually also gives it an initial\nvalue.  `sum` is now local to `sum_numbers` (like the argument `n`) and we have\na better behaved function. I encourage you to use `local` declarations as much\nas possible, even in little programs.\n\nLua functions may have more than one argument, like `print` - it can take any\nnumber of arguments.  Lua functions may also return _more than one value_. Say\nwe want the position of some text within a larger body of text - or (properly)\nthe position of a _substring_ in a _string_:\n\n```lua\n\u003e s = \"hello dolly\"\n\u003e string.find(s,\"doll\")\n7\t10\n\u003e string.sub(s,7,10)\ndoll\n```\n\n`string.find` returns two values: the start _and_ the end position. You can\ntake these positions and get the substring back using `string.sub`. The\nend position is just after the end of the match; all positions are from one.\nA common task is to find if a string starts with another string, which we can now\ndefine as\n\n```lua\nfunction starts_with(str,sub)\n    return string.find(str,sub) == 0\nend\n\nlocal text = 'hello dolly you're so fine'\nlocal hello = 'hello'\n\nif starts_with(text,hello) then\n    print('goodbye')\nend\n```\n\nIn this comparison, the second return value is ignored.\n\nHere is a function that returns both the minimum and the maximum value\nof a table of values:\n\n```lua\n-- minmax.lua\nfunction minmax(values)\n   local vmin = math.huge\n   local vmax = -math.huge\n   for i = 1,#values do\n      vmin = math.min(vmin,values[i])\n      vmax = math.max(vmax,values[i])\n   end\n   return vmin, vmax\nend\n\nlocal values = {10,2,5,20,3,12}\nlocal min,max = minmax(values)\nprint(min, max)\n-- 2    20\n```\nI've used the constant `math.huge`, which is guaranteed to be larger than any\nother number, and also the `min` and `max` functions.  Please note how we\ncan assign multiple values to several variables at once. This also means\nthat you can say `local x, y = 1.0, 2.0`.\n\nThere are actually two variables called `values` in this program, one local\nto the function and the other to the so-called _main chunk_. They are completely\nunrelated.\n\n## Operations on Tables\n\nThere's no built-in way to print out tables in Lua, but it's easy to write a\nfunction that will turn an array of strings and numbers into a human-readable string:\n\n```lua\n-- show.lua\nfunction showa (arr)\n    return '{'..table.concat(arr,',')..'}'\nend\n```\nAnd then you can load it into the Lua prompt using the 'l' (for 'load') flag.\nNote that here we don't use the extension '.lua'!\n\n```lua\n~/lua/luabuild$ lua53 -l show\nLua 5.3.0  Copyright (C) 1994-2014 Lua.org, PUC-Rio\n\u003e arr = {10,20,30,40}\n\u003e showa(arr)\n{10,20,30,40}\n\u003e\n```\nWe can now demonstrate some common operations on tables. We can insert a new value\ninto a table using a position (which starts from one); values can be removed.\nThere is a second form of `table.insert` which takes two arguments, not three - it\nwill _append_ the value to the end of the table.\n\n```lua\n\u003e table.insert(arr,1,11)\n\u003e showa(arr)\n{11,10,20,30,40}\n\u003e table.remove(arr,2)\n10\n\u003e showa(arr)\n{11,20,30,40}\n\u003e table.insert(arr,50)\n\u003e showa(arr)\n{11,20,30,40,50}\n```\nYou can also create an array-like table by setting the values using an index:\n\n```lua\n\u003e seq = {}\n\u003e for i = 1,10 do\n\u003e\u003e  seq[i] = i*i\n\u003e\u003e end\n\u003e showa(seq)\n{1,4,9,16,25,36,49,64,81,100}\n\u003e #seq\n10\n\u003e seq[#seq + 1] = 200\n\u003e showa(seq)\n{1,4,9,16,25,36,49,64,81,100,200}\n```\n\nThere is no builtin way to find things in arrays either, but it's easy to write.\nJust add the following function to `show.lua`:\n\n```lua\nfunction finda (arr, value)\n    for i = 1,#arr do\n        if arr[i] == value then\n            return i\n        end\n    end\n    return nil\nend\n```\n\nIt's perfectly fine to _return early_ from a function - we've found our position, let's\nreturn what we found. If we do **not** find the value, then at the end of the `for`\nloop we return `nil` explicitly.\n\n We have to restart with `lua53 -l show` after this change:\n\n```lua\n\u003e arr = {10,20,30,40,50}\n\u003e finda(arr,30)\n3\n\u003e finda(arr,60)\nnil\n\u003e if finda(arr,70) then\n\u003e\u003e  print('found')\n\u003e\u003e else\n\u003e\u003e  print('not found')\n\u003e\u003e end\nnot found\n```\nNote that we can directly test whether `finda` returned `nil` or not. The **only** two\nvalues that are considered 'false' are the boolean `false` and the value `nil`.\nAnd that's why I returned `nil`, rather than some out-of-range number like 0.\n\n\n\n\n\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevedonovan%2Flua-gentle-intro","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstevedonovan%2Flua-gentle-intro","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevedonovan%2Flua-gentle-intro/lists"}