Advertisement
1ng0

Lua Tricks & more you can

Nov 23rd, 2015
118
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 15.04 KB | None | 0 0
  1. ----------
  2. Lua Tricks
  3. ----------
  4. Note that this is not a lua tutorial and is intended for intermediate to advanced lua users.
  5.  
  6. I've been looking at some peoples' programs around here and I notice that a lot of them aren't using some neat tricks that may or may not make coding easier/neater.
  7.  
  8. Strings
  9. The first thing is that a lot of people use double quotes instead of single quotes, but I think that's just a preference in most cases. In case you still aren't aware:
  10. string1 = ''
  11. string2 = ""
  12.  
  13. Both of these string variables are valid. I should probably mention that you should be careful with using single quotes, because there are common words with apostrophes such as "don't" and "you're". To prevent errors, you should put backslashes before single quotes inside single quotes:
  14. string1 = 'don\'t'
  15.  
  16. An alternate solution is to use double quotes in this case.
  17.  
  18. There's also this neat trick that I didn't learn until a little further down the line in my use of lua - multiline strings. Defined by [[ ]], much like ' ' and " ".
  19.  
  20. The following example is valid:
  21. print [[This is
  22. a multiline
  23. string.]]
  24.  
  25. Be wary that using multiline strings with tabs before text will actually output the tabs as well.
  26. print [[This string
  27.           will take the tabbing
  28.           with it.]]
  29.  
  30. Multiline strings are useful in printing menus for your console. Instead of doing this:
  31. print '------------------------------'
  32. print 'Screen Title'
  33. print '------------------------------'
  34. print 'Option'
  35. print 'Option'
  36. print 'Option'
  37.  
  38. you can do:
  39. print [[
  40. ------------------------------
  41. Screen Title
  42. ------------------------------
  43. Option
  44. Option
  45. Option
  46. ]]
  47.  
  48. A similar helpful trick to multiline strings can be done with comments, starting with --[[ and ending with ]]:
  49. --[[
  50. This is a multiline comment.
  51. It does nothing :P/>/>/>
  52. ]]
  53.  
  54. I learned this very recently from an amazing youtuber named NitroFingers. You can use the # operator on strings to get the length of a string, much like string.len().
  55. myString = 'waffles'
  56.  
  57. print(string.len(myString))
  58. --> 7
  59. print(#myString)
  60. --> 7
  61.  
  62. I haven't tested this with all string methods, but in most cases, you'll usually be able to perform the method on the actual string instead of calling the method with the string as an argument.
  63. local s = 'waffles'
  64. s:sub(1,3)
  65. --> waf
  66. s:match 'f.-s'
  67. --> fles
  68.  
  69. for i,v in s:gmatch '.' do
  70.   print(v)
  71. end
  72. --> w
  73. --> a
  74. --> f
  75. --> f
  76. --> l
  77. --> e
  78. --> s
  79.  
  80. Functions
  81. Function calls are also flexible in other ways. In the case that a function only takes one argument and it is a string, you don't have to supply parentheses.
  82. print('a string')
  83. print 'a string'
  84. Both of these examples will print a string.
  85.  
  86. Unless you're getting deep into some OOP stuff, this won't be very helpful to you in a lot of cases. If a function takes a table argument, you can drop the parentheses, like strings.
  87. myFunction {
  88. a = 1;
  89. b = 2;
  90. }
  91.  
  92. There's also an alternate way of defining functions.
  93. myFunct = function()
  94. -- stuff
  95. end
  96.  
  97. For me, this is useful for defining functions without having to go outside your table:
  98. myTable = {
  99. funct1 = function()
  100.   print 'stuff'
  101. end;
  102.  
  103. funct2 = function()
  104.   print 'more stuff'
  105. end;
  106. }
  107.  
  108. myTable.funct1()
  109. myTable.funct2()
  110.  
  111. Functions are able to be defined with a variable number of arguments:
  112. function stuff(...)
  113.  
  114. end
  115.  
  116. The three dots represent the table of arguments given and can me accessed in the function as "arg".
  117. function stuff(...)
  118. for i,v in pairs(arg) do
  119.   write(v)
  120. end
  121. end
  122.  
  123. stuff(1,2,3,'a','b','c')
  124. --> 123abc6
  125.  
  126. Woah wait, where'd that 6 come from? That's just the number of arguments given. It's indexed in the "arg" table as n.
  127. print(arg.n)
  128. --> 6
  129.  
  130. However you'll still get the number of arguments given if you use the pound sign operator, as it only counts values given as indexes.
  131. print(#arg)
  132. --> 6
  133.  
  134. At the end of the day, it's all a matter of preference. To make things less complicated, you can simply store your arguments in a local table, as shown by some computercraft base programs.
  135. tArgs = { ... }
  136.  
  137. If you don't want to check for individual arguments in a table, you can catch variable arguments using variables instead.
  138. -- the old way:
  139. local args = {...}
  140. local length, height = args[1], args[2]
  141.  
  142. -- the new way
  143. local length, height = ...
  144. Thinking about it is kind of screwy, but makes sense at the same time.
  145.  
  146. In lua, you have the ability to directly call anonymous functions without having actually defined them yet. It will sound less confusing when I use an example. Say I wanted to have a table full of all of the available colors in numerical order.
  147. local colorTable = (function()
  148.   local t = {}
  149.   for _, color in pairs(colors) do
  150.     if type(color) == 'number' then
  151.       table.insert(t, color)
  152.     end
  153.   end
  154.   table.sort(t)
  155.   return t
  156. end)()
  157.  
  158. print(colorTable[1])
  159. --> 8192
  160.  
  161. Functions can be used as "iterators", which is a fancy term for a function that keeps returning, and at some point, returns nil, such as when a table is empty, when there are no more lines in a file, or when a number has reached a certain limit. Iterators are used in "for" statements, like with pairs or ipairs.
  162. -- this function returns random numbers from 1 to 10, but doesn't return anything if the number is 10.
  163. local function randomNumbers()
  164.   local n = math.random(1,10)
  165.   if n == 10 then
  166.     return nil
  167.   end
  168.   return n
  169. end
  170.  
  171. -- this is how we'd do it the old way.
  172. while true do
  173.   local n = randomNumbers()
  174.   if n then
  175.     print(n)
  176.   else
  177.     break
  178.   end
  179. end
  180.  
  181. -- and this is using it as an iterator
  182. for n in randomNumbers do
  183.   print(n)
  184. end
  185.  
  186. A more applicable example:
  187. local file = fs.open('my-data', 'r')
  188. local lines = {}
  189.  
  190. -- the old way:
  191. while true do
  192.   local line = file.readLine()
  193.   if line then
  194.     table.insert(lines, line)
  195.   else
  196.     file.close()
  197.     break
  198.   end
  199. end
  200.  
  201. -- the new way:
  202. for line in file.readLine do
  203.   table.insert(lines, line)
  204. end
  205.  
  206. I feel like you may be wondering something, though. In the examples above, our iterators don't have parentheses. However pairs() and ipairs() do. This is because the functions pairs and ipairs aren't iterators, but they return iterators. They return the next() function, which returns the next key/value pair in a table after the one given, like how file.readLine returns the next line in a file. The next() function, however, does not automatically know which index you're currently on. It must be stored and given to it he next time you want a key/value pair out of it.
  207. local data = {
  208.  name = 'Kingdaro';
  209.  title = 'The Doctor';
  210.  posts = 'A lot';
  211. }
  212. local key, value
  213.  
  214. -- we do not have a current "position" in the table, so we just get the first index that lua can see.
  215. key, value = next(data)
  216. print(key, value) --> posts, A lot
  217. -- note that lua will not always find the indices in order.
  218.  
  219. -- so we take the current key we have, and use it to find the next index and key.
  220. key, value = next(data, key)
  221. print(key, value) --> name, Kingdaro
  222.  
  223. -- and again
  224. key, value = next(data, key)
  225. print(key, value) --> title, The Doctor
  226.  
  227. -- at this point, we run out of key/value pairs, so we just get nil after we try to get another.
  228. key, value = next(data, key)
  229. print(key, value) --> nil
  230.  
  231. And what we did above is what the function that pairs() returns does. Remember that it's not pairs() itself, because if it were, we couldn't pass a table to iterate through using next().
  232.  
  233. Tables
  234. Like strings, the # operator can also be used to find the length of tables.
  235. myTable = {'hello', 'world!', 3, 4, 5}
  236. print(#myTable) --> 5
  237.  
  238. This is tricky though; the length of a table is counted up until the last non-nil index. Setting an index in the middle of a table to nil will still have its elements counted up until the last value that isn't nil. If the last value becomes nil, the counter decreases until it's found a non-nil value. For example, the length of {1, nil, 3} is three, and the length of {1, 2, nil} is two. Indices that are strings do not count, so the length of {5, 7, 3, hello = 'world'} would be three.
  239.  
  240. Moving on, when I see people do this:
  241. something = {}
  242. something['key'] = 'value'
  243. something['sub'] = {}
  244. something['sub']['key'] = 'value'
  245. I die a little on the inside.
  246.  
  247. It's not very clean, and it's much more typing than doing this:
  248. something = {
  249.  key = 'value',
  250.  sub = {
  251.    key = 'value'
  252.  }
  253. }
  254. Defining tables and subtables this way allows you to more accurately see the hierarchy of the table, and as stated before, is much cleaner.
  255.  
  256. Note that there's still an alternative even when you need to define new indices after making the table. You can use a dot syntax instead of the string around brackets.
  257. local stuff = {}
  258.  
  259. function defineStuff()
  260.   stuff.x = 5
  261.   stuff.y = 5
  262.   stuff.fun = {
  263.     foo = 'bar';
  264.   }
  265. end
  266.  
  267. defineStuff()
  268. print(stuff.fun.foo) --> bar
  269.  
  270. However, brackets are required when using string variables, and the dot syntax is only used to get and set raw indices.
  271. local stuff = {}
  272. local key = 'hello'
  273.  
  274. stuff[key] = 'world'
  275.  
  276. -- this is wrong; we actually get the key named "key" instead of the value of the variable key
  277. print(stuff.key) --> nil
  278.  
  279. -- this is right; the value of the variable key is "hello", and up there, we set the key named "hello", the value of the key variable.
  280. print(stuff.hello) --> world
  281.  
  282. Also, when defining indices in tables, you can actually use semicolons instead of commas. For me, this is easier because I can rearrange elements of the table without having to redo my commas.
  283. myTable = {
  284.   index1 = "foobar";
  285.   index2 = 1337;
  286. }
  287.  
  288. In case you didn't know this either, you can also use strings to access indexes not defined by strings. This would be helpful for storing functions for possible user input in tables.
  289. local answers = {
  290.  yes = 'You\'re cool!';
  291.   no = 'Fine, be that way.';
  292. }
  293.  
  294. print 'Do you like pineapples?'
  295. input = read() -- this is accepted as a string.
  296. if answers[input] ~= nil then
  297. print(answers[input])
  298. else
  299. print 'I don't understand you, sorry.'
  300. end
  301.  
  302. Tables and multiple arguments/variables get along really well. Remember you can do this:
  303. args = {...}
  304.  
  305. In this situation, ... could represent "a, b", or "foo, bar, baz", or just "hello". Therefore, you can do this:
  306. local function add(n1, n2)
  307.  print(n1 + n2)
  308. end
  309.  
  310. local myTable = {5, 15}
  311. add(unpack(myTable))
  312.  
  313. And you can do this:
  314. local function numbers()
  315.  return math.random(1,10), math.random(11, 20)
  316. end
  317.  
  318. local myTable = {numbers())
  319. print(myTable[1]) --> a random number from 1 to 10
  320. print(myTable[2]) --> a random number from 11 to 20
  321.  
  322. In a situation applicable to ComputerCraft, you can even do this:
  323. while true do
  324.  local events = {os.pullEvent()}
  325.  if events[1] ~= 'mouse_click' then
  326.    doEvents(unpack(events))
  327.  end
  328. end
  329.  
  330. This catches the events, and only sends them to the doEvents function if the event isn't mouse_click.
  331.  
  332. Numbers
  333. There isn't much to learn in this category, however I think you'll eventually find it useful that you're actually able to concatenate numbers like strings.
  334. print(3 .. 3)
  335. --> 33
  336. There need to be spaces in between the dots and the numbers, otherwise lua will confuse your dots as decimals and throw an error.
  337.  
  338. Some of you coming from a different programming background may have already known this, but the function math.fmod() can be replaced with the operator %. If you don't know what this does, it returns the remainder of the division of two numbers. e.g. 7 % 3 would equal 1.
  339.  
  340. You could use this to tell if a number is even - if the remainder of the number and 2 is 0.
  341. while true do
  342.   textutils.slowPrint 'Type in a number.'
  343.   write '> '
  344.   local input = read()
  345.   local num = tonumber(input)
  346.   if num then --checking if the input was successfully converted into a number
  347.         if num % 2 == 0 then
  348.           textutils.slowPrint 'Your number is even.'
  349.         else
  350.           textutils.slowPrint 'Your number is odd.'
  351.         end
  352.   elseif input == 'exit' then --give the ability to leave the program.
  353.         textutils.slowPrint 'Thank you for using my useless program! :P/>/>/>/>/>/>/>/>/>'
  354.         sleep(1)
  355.         break
  356.   else
  357.         textutils.slowPrint "That's not a valid number!"
  358.   end
  359.  
  360.   sleep(1)
  361. end
  362.  
  363. Booleans
  364. In the bools field, there's actually a cute little statement that is way easier than if ... elseif ... end:
  365. var = condition and set_if_true or set_if_false
  366. Five unnecessary lines condensed into one. Here's an example:
  367.  
  368. var = 5 > 3 and 'yes!' or 'aw'
  369. In this case, var would be 'yes!' because 5 > 3. If on some distant alien planet in a parallel universe 5 were less than three, var would be 'aw'.
  370.  
  371. If you wanted to have a bit of fun, you can make yourself a clamp function by nesting these.
  372. local function clamp(low,n,high)
  373. return n < low and low or (n > high and high or n)
  374. end
  375.  
  376. There's also a simple way of reversing a boolean:
  377. bool = not bool
  378.  
  379. In a more applicable example, you could make a sort of toggling light switch program with one line (assuming your output is in the back and hooked up to your lights)
  380. rs.setOutput('back', not rs.getOutput('back'))
  381.  
  382. Oh, and remember that % example? This:
  383.        if num % 2 == 0 then
  384.          textutils.slowPrint 'Your number is even.'
  385.        else
  386.          textutils.slowPrint 'Your number is odd.'
  387.        end
  388.  
  389. Could be this:
  390.        textutils.slowPrint('Your number is ' .. (num % 2 == 0 and 'even.' or 'odd.'))
  391. Brackets needed because it isn't just one string anymore XD
  392.  
  393. Metatables
  394. Christ, where do I begin.
  395.  
  396. local myTable = {
  397.   value = 5;
  398.   anotherValue = 10;
  399. }
  400.  
  401. local mt = {
  402.   __call = function(tab, index, value)
  403.     if not index then return end
  404.     if value then
  405.       tab[index] = value
  406.     end
  407.     return tab[index]
  408.   end;
  409. }
  410.  
  411. setmetatable(myTable, mt)
  412.  
  413. What I just did here, is that I just made a callable table. Basically, I've made it so that this won't error:
  414. myTable()
  415.  
  416. Furthermore, in my specific example, I can get and set the keys and values of myTable through function calls.
  417. print(myTable('value')) --> 5
  418. myTable('value', 10)
  419. print(myTable('value')) --> 10
  420.  
  421. This is but one of the very many powerful functions of metatables. A metatable is like a child or sibling to a normal table. When you do something with a metatable, such as call it like a function, lua looks for a certain value in the table's metatable so it knows what to do in that situation. These values are called "metamethods". A very simple and commonly used metamethod is __index.
  422.  
  423. The __index metamethod is triggered when you try to access a value in a table that doesn't exist. If you define the __index metamethod as a table, Lua will try to look for a key in that table that doesn't exist in the original table. With this system, it's easy to make a lookup table:
  424. local lookupTable = {
  425.   someValue = 5;
  426. }
  427.  
  428. local myTable = {
  429.   -- no values here!
  430. }
  431.  
  432. print(myTable.someValue) --> nil
  433. setmetatable(myTable, { __index = lookupTable })
  434. print(myTable.someValue) --> 5
  435.  
  436. There's also __add, __sub, __mul, __div, and __tostring among others. You can find more about metatables on lua-users. http://lua-users.org...methodsTutorial
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement