⬆️ ⬇️

Lua and Corona SDK (3/3 part)

image



In this final third part of the large Lua review article in the Corona SDK, very important issues will be considered, after studying which you can go directly to the study of the Corona SDK.





Perhaps someone did not read the first two parts of this article, I advise them to begin with reading them: the first part and the second part



Cyclic operations



In any language and for almost any task, a situation will necessarily arise in which a certain section of code must be executed several times. Cyclic operations are used for this task. In the Lua language, there are 2 operators allowing to organize loops ( for and while ), then I will describe them.

')

while (exp) do end



The code between do and end is executed as long as the result of the expression exp is true ( true ). I will give an example of the implementation of the cycle:



local count = 4--  local b = true--    local i = 0--    while b do--   b  true i = i + 1--  print(i)--    if i >= count then--   b = false--     end end -->> 1 -->> 2 -->> 3 -->> 4 


Before the cycle, we set the variable b to true, and then after reaching the desired state of the counter, set b to false and exit the cycle. There is a way to create a loop in the while true do end format while creating an eternal loop - this is a rather dangerous situation and you always need to have stable conditions for exiting such cycles, in Lua there is a break statement for this purpose - this operator unconditionally terminates the loop. Consider an example:



 local count = 4--  local i = 0--    while true do--  i = i + 1--  print(i)--    if i >= count then--   break--   end end -->> 1 -->> 2 -->> 3 -->> 4 


Avoid negligence when organizing perpetual cycles and in general when using while. It is not always possible to accurately predict that in any situation the cycle will be completed, and this leads to a deep freeze of the application.



for (exp) do end



The code located between do and end will execute according to the rules set in exp. The for loop is widespread among the programming language, in Lua this operator can be used in two ways, below we consider them. The first and easiest way will be easily understood by any user familiar with any programming language:



 for i = 1,3 do--  3     print(i) end -->> 1 -->> 2 -->> 3 


In some cases, it is necessary to increase the iterator not by one, but for example by 2 or 5, it is also possible that you want to count in the opposite direction. This is implemented using 3 for parameters, which determines the increment value in one step.



 --  2  6   2 for i = 2,6,2 do print(i) end -->> 2 -->> 4 -->> 6 --  9  3   3 for i = 9,3,-3 do print(i) end -->> 9 -->> 6 -->> 3 


for [values] in (func) do end



This variant of the for loop was already considered in the last part when we considered the string.gmath, pairs, and ipairs functions. In Lua, the use of this cycle can be considered a great convenience and should not be avoided; it is easier to understand once and continue to do many things easier than usual in other languages. Once again I will give examples of the application of this type of cyclic operation:



 local reg = " (%w+):(%d+)"--    2  local s = " masha:30, lena:25, olia:26, kira:29, "--     for key, value in s:gmatch(reg) do--     print(key, value) end -->> masha 30 -->> lena 25 -->> olia 26 -->> kira 29 for key, value in ipairs{'vasia','petia','kolia'} do print(key, value) end -->> 1 vasia -->> 2 petia -->> 3 kolia for key, value in pairs{v1 = 'OK',v2 = 123, v3 = 12.3} do print(key, value) end -->> v1 OK -->> v2 123 -->> v3 12.3 


Literally in the next section, when considering working with files, we will consider another variant of the cyclic function as a loop argument - file: lines .



Work with files



Corona SDK uses the standard Lua I / O library for working with files. Not all library functions have a cross-platform implementation, just as not all functions in the Corona SDK are implemented in the same way as is customary in Lua. Along with working with files, the io library in Lua is used to work with the console ( if you do not specify a file identifier ), but in the Corona SDK this action will not be entirely right, I will give an example:



 io.write('A:') local A = io.read() io.write('B:') local B = io.read() io.write('A + B = ',A + B,'\n') 


This code fragment in Lua works like this: the first line is executed in the console, the string “A:” is displayed, then it is expected until the user enters the number and presses Enter, then the same operation takes place with the variable B, after which the summation result is output. In the Corona SDK, execution on the second and fourth lines does not stop, and as a result, an error occurs on line 5 when trying to add 2 variables equal to nil. Those. Creating applications that accept parameters from the console in the Corona SDK is not possible, i.e. to work with the console, it remains to use only io.write for text output, but this function has a direct analogue - print. From the above, it is worth concluding that there is no point in using the io library to work with the console . The library is suitable for working with files, but again, because of the dubious implementation in the Corona SDK, it is better to use the library in the most obvious and simple way: “Opened / Created - Read - Recorded - Closed”. Also, when developing in the Corona SDK, we have to take into account that a special directory system is inherent in mobile applications, this is where we will begin.



Catalogs available to the application



All application data is in a space isolated from other applications, there are several directories:





When working with files, the file opening functions are used; they require the file path to be specified, the path consists of a directory and the file name, the Corona SDK uses the system.pathForFile function to generate the correct file path value, the function is used as follows:



 local fileName = 'saves.dat' local filePath = system.pathForFile(fileName, system.DocumentsDirectory ) 


As the second parameter, you must use one of the available values: system.ResourceDirectory, system.DocumentsDirectory, system.TemporaryDirectory, system.CachesDirectory . As already mentioned above, the default value for system.ResourceDirectory, i.e. if you do not specify the second parameter, the root of the path will take the directory with the main.lua file.



Many functions in Corona SDK use files for my work, for example, display.newImage loads an image from a file on the device screen. In these functions, the file name is entered separately from the directory:



 local mouse = display.newImage( "assets/images/mouse.png", system. ResourceDirectory, 0, 0 ) 


Since the system. ResourceDirectory is the default value, this parameter can be omitted, thereby reducing the record:



 local mouse = display.newImage( "assets/images/mouse.png", 0, 0 ) 


Pay attention to 3 things:





IMPORTANT : One of the most popular reasons for the error in which the application runs on a computer simulator, but does not work on the device, is to use a different register in the names of directories and files. The fact is that in the simulator there is no register dependence, i.e. Assets / Images / Mouse.PNG and assets / images / mouse.png are considered to be the same way, in Android and iOS they are different paths, so if the function contains a lower case path, but the actual names of directories or files have uppercase characters ( or vice versa ) - this leads to an error. Try to avoid directory names and uppercase files everywhere and always.



Next, we consider the most simple and acceptable methods of working with files. In this tutorial, I will not write my own version of the translation of official reference materials. You can read them yourself both for Lua and for the crown , I will try to give you the most necessary information, namely how to make a convenient storage of project settings and implement work with logs.



Creating / Writing / Reading the entire file



We implement a simple example in which a file is created and a line is written to it:



 local write_data = "Hello file!"--     local fileName = 'saves.dat'--  local filePath = system.pathForFile(fileName, system.DocumentsDirectory )--   local file = io.open(filePath, "w")--       file:write( write_data )-- io.close( file )--  


Consider the features. In the io.open function, a special flag is used as the second parameter, which defines the type of file access, consider the main access flags:





Let's implement an example of opening a file and reading all the content from it (the line recorded last time ):



 local fileName = 'saves.dat'--  local filePath = system.pathForFile(fileName, system.DocumentsDirectory )--   local file = io.open(filePath, "r")--    local read_data = file:read("*a")--   io.close( file )--  print(read_data) 


IMPORTANT : If you want to view files created in project directories in the emulator, you need to perform the action: File -> Show Project Sandbox, this opens the project root directory (system.ResourceDirectory), in this folder you will find three subfolders:





The way of working with files described above overwrites the file every time it is written and the entire file is read, which can be useful if you want to store your game settings in the file, but this method has one major drawback - to save the settings, you will have to somehow format it and that in the future, when opening for reading, you could read them back, if you do not have many settings and there are no nested tables, then most likely there will not be any special problems ( but this is still not convenient ) if the structure of your settings is sufficient It would be more convenient to use the json format as a serialization tool (a link for those who don’t know what they are talking about but wants to know ). The sequence of our actions will be as follows:





Below is an example of a simple project that implements the idea described above, you can easily transfer this mechanism to your projects, since for the purposes of this lesson a universal library was made in which you only need to fill in the default settings template to adapt to your project, and optionally change the name of the settings file.



Library code (libLoadSave.lua)



 ----------------------------------------- --      -- ----------------------------------------- local M = { template = {--  --   num = 1,--  time = 0,--   }, config_file = system.pathForFile('saves.dat', system.DocumentsDirectory )--   } local json = require "json"--    json --   , --   -    M.save = function() local file = io.open(M.config_file, "w")--    if file then--    local write_data = json.encode(config)--   json file:write( write_data )--   io.close( file )--  end end --    M.load = function() local file = io.open( M.config_file, "r" )--    --   if file then local read_data = file:read("*a")--   config = json.decode(read_data)--  json  lua,  config io.close( file ) --   else config = M.template--   M.save()--     end end return M 


The procedure for applying the library is as follows:





Consider the source of the main file that implements the described mechanism:



 --main.lua config = {}--  (  -    ) ls = require "libLoadSave"--  ls.load()--  --    for k,v in pairs(config) do print(string.format('%s = %d',k,v)) end --  config.num = config.num + 1--   config.time = os.time()--   ls.save()-- 


Creating / Writing / Reading a file line by line



Consider the option of working with files in which the following sequence of actions will be performed:





 local write_data = "Very important entry in the log\n"--     local fileName = 'log.txt'--  local filePath = system.pathForFile(fileName, system.CachesDirectory )--   local file = io.open(filePath, "a")-- ,     ,    -  file:write( write_data )-- io.close( file )--  


Since we are considering this example, intending to later implement the project log, then pay attention to the fact that I store the file in the cache directory, you can also store it in a temporary directory. This is due to the fact that logs have the greatest value as hardware-dependent data, for example, on one device you have an error and not on the other, and synchronization of logs in a general way will have a bad effect on their informativeness. Storing logs in a temporary folder makes it possible to avoid significant drift in the size occupied by the application ( if you keep logs for years ), since every time the application is restarted, the logs will be deleted ( this is not always the case, more often the logs are deleted when the phone is restarted or as a result of any cleaners ) .



The second step in the implementation of the logs will be to consider how to read the file line by line - this is useful for reading data from the log:



 local fileName = 'log.txt'--  local filePath = system.pathForFile(fileName, system.CachesDirectory )--   local file = io.open( filePath, "r" )--    for line in file:lines() do--     print( line )--    end io.close( file )--  


Now, just like last time, we are implementing a convenient logging mechanism for transfer and reuse. First the library code (libLog.lua):



 ----------------------------------- --      -- ----------------------------------- local M = { log_file = system.pathForFile('log.txt', system.CachesDirectory )--   } --   M.write = function(msg) local file = io.open(M.log_file, "a")--      if file then--    local dt = os.date("%x %X")--- local write_str = string.format('%s %q\n',dt,msg) file:write( write_str )--   io.close( file )--  end end --    M.read = function() local file = io.open( M.log_file, "r" )--    --   if file then for line in file:lines() do--     print( line )--    end io.close( file )--  end end return M 


Consider main.lua



 --main.lua LOG = require "libLog"--  --   local write_data = "Very important entry in the log"--     for i = 1,10 do LOG.write(write_data..' - '..i) end --     LOG.read() 


date and time



In many developed languages ​​like C # or Delphi, working with date and time is accomplished through the use of dozens of ready-made functions that, on the one hand, still cannot foresee everything and have to “finish” something, on the other hand, the developer’s fragile brain is forced to keep this whole zoo of possibilities. In Lua, everything is very simple:





So Only two functions, but they are used very flexibly and this allows to solve most of the necessary tasks for working with date and time. There is another additional function indirectly related to time - os.clock, this function allows you to determine the execution time of the code. Consider all these features in detail.



os.time ()



In the simplest version, the function is called without parameters and returns the current system timestamp ( UNIX time / POSIX time ) is the number of seconds that have passed since the beginning of the UNIX era ( 01/01/1970 at 00:00:00 UTC ).



 print(os.time())-->>1513065913 (   ) 


In the extended format, the os.time function is able to return the timestamp of any point in time after the beginning of the UNIX era. To do this, pass the table as a parameter to the function in a special format: date table. There is nothing “magical” in this table; below I give its description:



year 1970-3000

month 01-12

day 01-31

hour 00-23

min 00-59

sec 00-59

isdst true if summer time



Those. if you fill this table and pass it os.time, the function will return a timestamp for the set time. Parameters year, month, day are required.There are also time frames within which parameters can be entered; they extend from the beginning of the UNIX era to ( 12/31/3000 23: 59: 59UTC ) if entered above and below these limits, the function returns nil. Code example:



 print(os.time{year = 1990, day=1, month=1, hour=3, min = 0, sec = 0}) -->> 631152000 print(os.time{year = 2000, day=1, month=1, min = 0, sec = 0}) -->> 946717200 print(os.time{year = 2010, day=1, month=1}) -->> 1262336400 


Note the time parameters: hour, minute, second , the default values ​​are 12:00:00. In such an application, the os.time function is convenient to use for comparing dates; it is much more convenient than doing a complex detour on the date table parameters. You can also use the result of this function to calculate "how much is left before .." or "how much is passed from .." , the only inconvenience will be that it is still necessary to convert hours, minutes and seconds from seconds to days manually, but it is not difficult . I will give an example that calculate the remaining time before the New Year in days of the hour, minutes and seconds:



 --   local year = os.date('*t').year + 1-- ""     --     local before_NY = os.time{year = year, month = 1, day = 1, hour = 0} - os.time() local s_day, s_hour, s_min = 24*60*60, 60*60, 60--   , ,  local day = math.floor(before_NY / s_day)--  local hour = math.floor((before_NY - day * s_day) / s_hour)--  local min = math.floor((before_NY - day * s_day - hour * s_hour) / s_min)--  local sec = before_NY - day * s_day - hour * s_hour - min * s_min--  print(string.format('Until the New Year there are %d days %d hours %d minutes %d seconds', day, hour, min, sec)) 


os.date



Despite the name, the os.date function is an inverse function of os.time, i.e. from the low-level timestamp format, it receives a higher-level date table format, in the returned result of the os.date function there are fields that are not required. The os.time functions are fields: yday is the day of the year ( 1..365 or 366 for leap years ) and wday is the day weeks ( 1 - Sunday, 2 - Monday ... 7 - Saturday ). The function takes 2 parameters:







Go to examples of using os.date:



 --[[   ]] --    date table print_dt = function(t) local s = '' for k,v in pairs(t) do--   local vv = type(v)=='boolean'--  boolean and (v and 'true' or 'false')--  or v--  boolean   v s = s .. string.format('%s=%s, ',k,vv)--  end print(s) end --[[   os.date    -  ]] print_dt(os.date('*t'))--     -->> hour=21, min=4, wday=3, day=12, month=12, year=2017, sec=14, yday=346, isdst=false, print_dt(os.date('!*t'))--     -->> hour=18, min=4, wday=3, day=12, month=12, year=2017, sec=14, yday=346, isdst=false, print_dt(os.date('!*t',1000000000))---    UNIX- -->> hour=1, min=46, wday=1, day=9, month=9, year=2001, sec=40, yday=252, isdst=false, --[[   os.date    -  ]] print(os.date('Now: %c'))--Now: 12/12/17 21:04:14 print(os.date('Date: %x'))--Now: 12/12/17 print(os.date('Time: %X'))--Now: 21:04:14 print(os.date('2^9 sec UNIX-time: %c',2000000000))--Now: 2^9 sec UNIX-time: 05/18/33 06:33:20 


os.clock



Often, in the process of creating an application, performance problems arise and it is not always easy to understand which particular code fragment heavily loads the entire application, or, for example, leads to long program launches. It is also possible using the function to choose which version of the implementation of the same function to choose. The os.clock function returns the time in seconds ( accuracy up to 4 decimal places ), which the processor spent on working on the application since the start of the application. Those.if you minimize the application to memory and do not use it, the time will be suspended until it returns to the application. An example of code comparing the effectiveness of two variants of performing one task, an example of a naturally comic one ( all without the tests will guess what works faster ).



 --       1..N func1 = function(N)--  return (N*(N+1))/2 end func2 = function(N)-- " " local sum = 0 for i=1,N do sum = sum + i end return sum end local N = 1000000000--    1  N local x = os.clock() func1(N) print('funt1: '..(os.clock() - x)..' sec')-->> funt1: 0 sec x = os.clock() func2(N) print('funt2: '..(os.clock() - x)..' sec')--> funt2: 6.293 sec 


Functions



In the process of studying this article, I have repeatedly talked about functions and some examples had them on board, but I decided to move the qualitative analysis of this concept to the end of the article, since the basis now available is better suited for understanding some subtleties. Let's start with the theory. A function is a fragment of program code (subroutine) that can be accessed from another place in the program. In most cases, an identifier is associated with a function, but many languages ​​also allow unnamed functions.



Anonymous functions



Lua allows for unnamed ( impersonal, anonymous ) functions - this is universally used if one function requires another function as one of the arguments and if this fragment no longer needs you with a light heart to create an anonymous function. I will give an example:



 func1 = function(func)--      func()--     end --  func1 func1(function()--   print('Hi from an anonymous function!') end) 


Function declaration order



You’ve probably already noticed, but to summarize, functions in Lua can be declared in two ways:



Assigning its value to a variable



 name1 = function() end 


Direct announcement



 function name2() end 


You can use any method, but the first one is better, since there is a small difference - at the time of initialization of the module (file), the entire local context is executed at the same time, i.e. if there are any variable assignments outside the functions, they will be assigned once (the function declared as a variable will be initialized), but if the function is declared the second way, the function will be initialized at the first use.



Function call order



The function is called by specifying its name and in parameter transfer brackets; if you do not specify the brackets, the function will simply store its data type and call address, you can simply print a function to the print and it will not be called:



 function func1() print('Hi') end print(func1)-->> function: 005DBA00 (     ) print(func1())-->      -    


The second print did not print anything, since the function has no return parameters. There is another option for organizing a function call. In functions, multiple input parameters may be required and they can either be passed through a comma or they can organize a function so that it expects only one parameter at the input - table, and all parameters are placed in the key = value format:



 --    func1 = function(a,b,c) print(a,b,c) end --    func2 = function(p) print(pa,pb,pc) end -- func1 func1(1,2,3)-->> 1 2 3 -- func2 func2{-->> 4 5 6 a = 4, b = 5, c = 6 } 


At first glance it may seem that the 2 way is “some kind of long is to write a lot”, but in practice it is much more convenient, I will give arguments in defense of the “long way”, you may be able to convince you that it is good:





I want to believe that your choice if the parameter in the function more than one will always tend to the second option, but it is up to you to decide.



Return results



Everything is simple, so that the function would return something at the moment when the result is obtained, use the return operator after which the variable (or several variables) will go. As in the case of passing the parameters of the function, in the case of returning the results of execution, I would recommend that you use the result tables in which all the results are placed:



 --    func1 = function(a,b,c) return a+b, b+c, c+a--  end --    func2 = function(p) return{ p1 = p.a+pb,--  p2 = p.b+pc, p3 = p.c+pa} end -- func1 val1, val2, val3 = func1(1,2,3) print(val1, val2, val3)-->> 3 5 4 -- func2 val = func2{ a = 4, b = 5, c = 6 } print(val.p1, val.p2, val.p3)-->> 9 11 10 


Regular expressions and captures



You have come across the concepts of regular expressions and captures several times during the study of this article, in particular when studying the following functions: string.match, string.gmacth, string.sub, string.gsub, string.find . How does this work?I will cite a few terms.

A regular expression is a string consisting of a sequence of characters that defines a pattern for parsing a text string into its constituent parts.



A regular expression can consist of control structures, continuous sequences of characters and hooks.



The control structure is a regular expression pattern that can replace one or more characters, i.e. for example,% d is any number. A complete list of control structures is presented below:





It is possible to combine several control structures into one; for this, they must be placed in square brackets "[]". Please note that for most control structures there are 2 mutually exclusive options, for example,% w and% W in fact, you can invert any structure with the “^” cap symbol, i.e. ^% w analogue% W and vice versa ^% W analogue% w. Also, most of the standard control structures can be organized manually, for example, for% d analogue [0-9], for% w - [0-9a-zA-Z], for brevity, where possible, it is better to use reserved standard structures.



Continuous sequence- This is a strict sequence of characters, which should be in a strictly defined place of a regular expression. Here is an example of the string “param1: 1234, param2: 54321”, use the following regular expression to parse it “% d +” ( means any number of consecutive characters, but no less than 1 ). This regular expression will find the string "1234" and the function will return the result, if we need to get the value of the second parameter, we can either start the search a second time (using the corresponding function parameter), or simply specify a continuous sequencecharacters, namely "params2:" and the regular expression will look like: "params2:% d +", but if you go further and simplify it by discarding too much, it will remain "2:% d +". It is worth noting that after reducing the length of a continuous sequence, the universality of a regular expression will increase, and with a larger number of parameters, a miss can occur.



A capture is the placement in a variable of certain parts of a string by parsing it with a regular expression. To organize a capture, it is enough to put some of the regular expression in parentheses "()". In a single regular expression, there may be multiple captures. Example of capture: there is an initial string " ip: 123.22.345.23", it is necessary to extract all 4 numeric parts of the ip address, apply the following regular expression as an argument of the string.match function" ip: (% d +)%. (% d +)%. (% d +)%. (% d +) "the code will be look like this:



 local st = "ip:123.22.345.23" local reg = "ip:(%d+)%.(%d+)%.(%d+)%.(%d+)" local d1,d2,d3,d4 = string.match(st, reg) print(d1,d2,d3,d4)-->> '123' '22' '345' '23' 


Please note that the point symbol between captures must be escaped, since this symbol is a control structure and means " any character ", if this is not done the numbers can be separated by any symbol and the capture will still work. There are other characters that need to be escaped from the list of the regular expression:



(). % + - *? [] ^ $



IMPORTANT : In regular expressions there is a strict need to fulfill all the conditions, i.e. if at least one condition for parsing a line is not met, the entire capture is not performed, and with all variables get the value nil.



How to make captures more universal, for example, to leave the possibility that the third ip number will be absent for some reason? In fact, everything is simple - now we use in the "+" paired with the control structure% d, and this assumes the presence of at least one number , if you remove +, the condition will be like this - there must be strictly one character , but if instead of "+ "put the symbol" * "then the condition will be - the capture of any number of numbers, including none. Checking:



 local st = "ip:123.22..23" local reg = "ip:(%d+)%.(%d+)%.(%d*)%.(%d+)" local d1,d2,d3,d4 = string.match(st, reg) print(d1,d2,d3,d4)-->> '123' '22' '' '23' 


Everything worked great. Moving on to the next complication is that if the string contains a number in hexadecimal format, i.e. along the digits may contain characters abcdefABCDEF. For this task, we combine several control structures into one with the help of "[]", control characters + * need to be put out of square brackets, or they will be considered the same part of the total structure. In order not to list all characters ( if there are characters in succession ) you can group them and list them with a hyphen as follows: “a-fA-F”



 local st = "code = 12f3a2e2ffd423A;" local reg = "code = ([a-fA-F%d]+)" local code = string.match(st, reg) print(code)-->> '12f3a2e2ffd423A' 


Now consider the option to simplify this regular season. Let's try to go from the reverse look not for specific characters, but to capture all characters except the terminating character, in this case the ";". As stated above, in order to initiate the inversion of “everything except”, it is necessary to put the cover symbol “^” in front of the control character. Also for this particular case, it is possible to greatly simplify a continuous sequence before capturing everything to "=", if this is not done, the first character of the string "code" will be captured, since it also responds to the regular sequence, and if you put only a space "" an extra fragment will be captured "=".



See the code:



 local st = "code = 12f3a2e2ffd423A;" local reg = "= ([^;]+)" local code = string.match(st, reg) print(code)-->> '12f3a2e2ffd423A' 


Inside the capture, I can be present as a sequence with a symbol on several separate or grouped control structures, i.e. Captures of this type are also correct:



 "(buka[%d]+%s*%.[%w]*[^%+]+)". 


Regular expressions in Lua are a serious part of the system and it is not uncommon to significantly simplify many things. Here are some tips on how to use regular expressions:





Lastly, I’ll give an example of a function that makes a "weak" validation check email.



 --  email check_email = function(email) local a,b,c = string.match(email,'([^@ ]+)@([^%.][^%.]+)%.(..+)') return a ~= nil and b ~= nil and c ~= nil end 


This expression works according to the following rule:





At first glance, there are plenty of ways to enter a bad email, but in practice this is an acceptable strictness of checking this parameter.



Randomness management



Very often in real applications it is required to introduce some randomness, for example, to choose one of the predetermined values ​​or set a random direction, color, etc. To solve these problems in most languages ​​there is a pseudo-random number generator.



Mathematically create a truly random sequence, so far it is considered impossible, there are hardware solutions to solve this problem, but this is a separate story. In Lua, a generator is used to obtain pseudo-random numbers using the math.random () function. The generator operates according to a certain algorithm once in a while the sequence starts repeating, the length of the sequence can be quite large, but there is a minus each time the application starts the sequence starts anew, i.e. if, for example, when entering the application, the program will receive a random number from 1 to 100, then each time we will see the same result if we put this action at the entrance to the application:



 print(math.random(1,100))--   1..N  1) 


There will always be 1, it is certainly very uncomfortable in real-world problems. In an attempt to solve this problem, you can use the second function designed to control math.random, this function is math.randomseed - with it we can change the first step of a random sequence and the generation will start not from one, but from another ( always the same ) number.



 local seed = 1234567890 math.randomseed(seed) print(math.random(1,100))--   1..N  4) 


Now every time we enter the application, we see the number 4. Something is wrong again! Obviously the same seed must not be fed to the generator input via math.randomseed. How to get it? You can try to use the already known function os.time () as a seed, in this case, if you enter the application less than once a second, we will receive a different number:



 math.randomseed(os.time()) print(math.random(100))--       1  


If you want to change the seed at about 10,000 faster, you can use this method:



 socket = require( "socket" ) local seed = (socket.gettime()*10000)%10^9 math.randomseed(seed) print(math.random(100))--        100  


An inquisitive reader will take this example apart.



Lastly, consider all the options for using math.random



 local n,m = 100,200 math.random(n)--  1  n math.random(n,m)--  n  m math.random(-m,m)--     math.random(-m,-n)--      ,        math.random()--     0  1 ( 16   ) 


ABOUT THE FUTURE : In the process of studying the Corona SDK, we will experiment with introducing entropy into the number generation mechanism, for example, receiving fluctuation signals from an accelerometer sensor or other sensors, this method is truly random, but not mathematical, and since the quantization (polling) time of all sensors is kilohertz, and the execution of the for loop (with a small number of commands) may take much less time, problems with this method will be no less significant, we will have to either slow down the cycle to the quanto time sensor, or use additional sources of entropy.



It should be noted that in matters of creating a very realistic chance it is not worth tormenting yourself, even the notable workaholic mathematician John von Neumann used to say:
"Anyone who has a weakness for arithmetic methods for obtaining random numbers is a sinner beyond doubt."




Mathematical functions



In the previous section, the functions random and randomseed from the math functions library were described. There are other functions in this library that will come in handy sooner or later.





In addition to functions in the math library, there are also 2 constants - math.pi and math.huge . We already got acquainted with math.pi in the first part of the article and this is just a constant storing the well-known variable (Pi), the accuracy of the constant is sufficient for most cases - 3.1415926535898. The constant math.huge is the more mysterious essence of its feature - it is always greater than or equal to any numerical value.



Careful attitude to someone else ...



In this brief and concluding section, we’ll not talk about the fact that a rented car should be treated as one’s own; it’s about standard language functions. At this point I changed my mind to add 2 more sections to the article and deleted them, I really want to finish and sleep: ( In many languages, it is difficult to spoil the standard functions, in Lua there is nothing simpler. Assign any function to an arbitrary value. Let me give an example:



 print = 1--  print print('Hello World')-- "attempt to call global 'print' (a number value)" 


As you can see, spoiling something that is not in this language is very simple. Reserved words like end, begin, then cannot be spoiled in this way, but the rest of the functions are completely defenseless against the manifestation of petty carelessness. Such errors will not be easy to find, especially if there are tens of thousands of lines in a project and a lot of people are working on it. I can only give one piece of advice. Always use code editors with Lua code highlighting and preferably Corona SDK functions, and if what you decide to do with a variable name or function is already used somewhere, think up a new name .



If the example with the initialization of print seems to you somewhat " far- fetched, " I will give a much more realistic example, which has even more consequences and becomes even more complicated.



The developer decided to enter a logical flag in the program for assigning true ( true ) which will allow printing to the console debugging information. Without hesitation, he decided that all translit names ( like OTLADKA) for his project does not work at all and called the variable debug. I assigned it to the true variable, checked that everything works and continued to develop, in an hour, a day or more, one of his colleagues noticed that the Corona SDK had completely broken and did not give an error message at all, even if you wrote complete nonsense in the code. It turned out that the debug function is a standard Lua function, which not only can be useful for you in debugging, is also used by the crown itself, and its elimination completely breaks the system for debugging project errors. I think this is a pretty good example confirming the idea that careful attitude to someone else is needed ...



Conclusion



If you studied all three parts of the article, then you got most of what you need to know in order to confidently create your own projects on the Corona SDK. I considered some of the questions in too much detail, others probably didn’t pay enough attention, somewhere, maybe I was wrong or not accurate, but I would like to believe that there will be people that this information will help. Many things from the additional libraries of the language were left out of this article due to the fact that some Lua functions have a more acceptable replacement from the Corona SDK functions. In the following articles, we will no longer study Lua, namely the Corona SDK, i.e. the functionality of which is in its pure form in the language is not. You certainly will find many articles in which we will write games and do other useful things.



I would like to express my gratitude for the help provided in writing the article to those people who comment and point out inaccuracies, as well as to the developers of the engine, with whom I had the honor to communicate and consult through their groups on social networks ( VK and facebook ) and Slack channel (admission free) .



Thank you all and good luck! Sincerely, Denis Goncharov aka execom

email: x@x-id.ru

PS: Thank you and those who read all this.

Source: https://habr.com/ru/post/344562/



All Articles