Posted by
| Nick Gammon
Australia (23,122 posts) Bio
Forum Administrator |
Message
| It is probably time to move from Lua 5.0 to Lua 5.1, now that Lua 5.1 has had time to settle down.
This document is an attempt to alert scripters to things to watch out for to make their scripts more easily changed over to Lua 5.1.
The improvements in Lua 5.1 over 5.0 are:
- New module system making modular coding easier (see "require" and "module")
- Incremental garbage collection
- New "vararg" mechanism (ie. for functions that take a "..." argument)
- New syntax for long strings (multiline strings and comments with [[ ... ]] around them)
- Mod operator (%) for arithmetic and length operator (#) for strings and tables.
- All data types can have metatables (in the C interface only however)
- New function "string.match", which returns the first matched capture (like string.find but without the start and end position)
- New function "string.reverse" that reverses its argument.
- New function "load", which loads Lua code progressively by calling a function to concatenate together data.
- New function "coroutine.running" which returns the currently-running coroutine, if any.
- New function "math.modf" which returns the integral and fractional part of a number.
- New maths functions sinh, cosh, and tanh for hyperbolic functions.
Generally speaking, most scripts should just continue to run without any problems. There are some areas which may require attention. I have found that the Lua chat module (itself pretty large) ran without changes under Lua 5.1.
Changes in the language syntax
- Vararg system
Functions that take a variable number of arguments no longer set up an "arg" table. Instead there is a new syntax "..." which can be used inside a function to represent the sequence of extra arguments.
An example of its use is to take a variable number of arguments and pass them down to another function, like this:
-- Lua 5.1 version
function ferror (fmt, ...)
error (string.format (fmt, ...), 2)
end -- ferror
In Lua 5.0 this would have been done this way:
-- Lua 5.0 version
function ferror (fmt, ...)
error (string.format (fmt, unpack (arg)), 2)
end -- ferror
The new method basically saves having to create a temporary table (arg) and then unpack that back into a list of arguments, for cases like this where the arg table isn't really required.
There is a new function "select" that lets you obtain the count of extra arguments, and also return a sequence from any position onwards. Thus you could make your "arg" table return by doing this:
function f (...)
local arg = {...}; arg.n = select ("#", ...) --> emulate old behaviour
print ("n =", arg.n) --> count of args
print (arg [1]) --> argument 1
print (arg [2]) --> argument 2
end -- f
- Nested strings and comments
In Lua 5.0 you could have multi-line strings, but they could not be nested (in other words you would have trouble making a string that had a multi-line string in it, and the same for multi-line comments.
Now you can do this by using the [==[ ... ]==] syntax. This can be useful to comment out a block of code that already has comments in it, or make a multi-line string with comments in it.
For example:
a = [=[
This is an example of code:
b = [[
a multi-line comment
goes here ]]
]=]
print (a)
You can continue to nest such things by adding more "=" signs (eg. [====[ ... ]====] ). Each one is paired with its corresponding partner.
- You can now use % as the modulus operator. eg.
- You can now use "#" as the length operator for tables and strings. eg.
print (#"Nick Gammon") --> 11
t = { 1, 2, 3 }
print (#t) --> 3
- Strings now have an automatic metatable containing "__index" which points to the string table.
What this means is that these two techniques now have identical results:
and ...
s:upper () --> where s is a string (note the colon, not a dot)
For example:
s = "nick"
print (string.upper (s)) --> NICK
print (s:upper ()) --> NICK
Changes in the libraries
- The global function _TRACEBACK has been removed. Scripts that referred to that should now use debug.traceback.
- New function "load". This calls a supplied function to gradually build up a Lua chunk. This could be useful for writing a preprocessor to read Lua code from a file and preprocess it before being compiled.
- Function string.gfind was renamed string.gmatch. Now if you attempt to use string.gfind you will get this error message: 'string.gfind' was renamed to 'string.gmatch'
To get the old behaviour back you could add this to the initialization part of your script:
string.gfind = string.gmatch
However it is probably just as easy to do a quick find-and-replace.
- New function string.match - this is similar to string.find but doesn't return the indexes of the match, which can be more convenient.
For example:
print (string.find ("the quick brown fox", "(q%a+)")) --> 5 9 quick
print (string.match ("the quick brown fox", "(q%a+)")) --> quick
This would be handy where you don't care what the index numbers are.
- When string.gsub is called with a function as its third argument, whenever this function returns nil or false the replacement string is the whole match, instead of the empty string.
This is useful because of this sort of case:
replacements = {
["nice"] = "windy",
["walk"] = "stroll",
}
s = "a nice long walk"
result = string.gsub (s, "%a+",
function (str)
return replacements [str] --> **** see below ****
end
)
Previously under Lua 5.0 the marked line would have read:
return replacements [str] or str
It seems unlikely that you want to have nil become an empty string, so this behaviour is more natural. If you want the empty string you can simply return an empty string.
- string.gsub can now be called with a table as its third argument, in which case matching items in the string are replaced by doing a table lookup. Thus the above example could be rewritten as:
s = "a nice long walk"
result = string.gsub (s, "%a+",
{
["nice"] = "windy",
["walk"] = "stroll",
}
)
This is even simpler then having to write a function, if simple replacement of one string by another is all that is required.
- Function table.setn was removed.
Attempts to set the length of a table with table.setn will now fail with the error message: 'setn' is obsolete
In the past table.setn was supposed to let you make numerically-indexed tables with gaps (ie. nil values) in the middle. They seem to have decided not to encourage this behaviour.
- Function table.getn corresponds to the new length operator (#); use the operator instead of the function.
You can now get the length of a table (or a string) with the # operator. For example:
t = {"nice", "day", "for", "a", "walk"}
print (#t) --> 5
- There is a new function "table.maxn" that finds the maximum numeric entry, in case you have a table with holes in it. eg.
t = {[1] = "hi", [10] = "there"}
print ("n = ", #t) --> n = 1
print ("maxn = ", table.maxn (t)) --> maxn = 10
Be warned that this function simply does a linear scan of every item in the table. It simply finds the largest key which is a number.
- Function loadlib was renamed package.loadlib.
If you want the old behaviour back you would need to do this:
loadlib = package.loadlib
- Function math.mod was renamed math.fmod.
If you attempt to use math.mod it will be nil. You can either reword it as math.fmod or use the new % (modulus) operator.
- Functions table.foreach and table.foreachi are deprecated. You can use a for loop with pairs or ipairs instead.
They may be deprecated however they still work. You are advised to rework loops as advised in case they are removed from future versions. eg. Instead of:
do:
for k, v in pairs (t) do
print (k, v)
end -- for
- The syntax "for k, v in t do" where t is a table is deprecated and no longer works. Use pairs (t) instead (as in example above).
- There were substantial changes in function 'require' due to the new module system. However, the new behavior is mostly compatible with the old, but 'require' gets the path from package.path instead of from LUA_PATH.
The new module system attempts to simplify loading multiple modules, in a more powerful way than the old "requires" function. Just as an example to see what it is doing, try doing this:
require "blah"
module 'blah' not found:
no field package.preload['blah']
no file '.\blah.lua'
no file 'C:\MUSHclient\lua\blah.lua'
no file 'C:\MUSHclient\lua\blah\init.lua'
no file 'C:\MUSHclient\blah.lua'
no file 'C:\MUSHclient\blah\init.lua'
no file '.\blah.dll'
no file 'C:\MUSHclient\blah.dll'
no file 'C:\MUSHclient\loadall.dll'
Basically what it is doing is to try to locate your module "blah" by substituting it repeatedly into this string and trying to open the appropriate file:
.\?.lua;!lua\?.lua;!lua\?\init.lua;!?.lua;!?\init.lua
The character "!" represents "the path to the executable", which is why it is "C:\MUSHclient\" in my example.
If it cannot find the .lua file it tries to find a .dll file by substitution in this string:
.\?.dll;!?.dll;!loadall.dll
In the case of a DLL it then attempts to find an entry point named "luaopen_?" where ? is the module name (eg. loaopen_blah in my example).
There is more to it than that, read the reference manual for more information.
- Function collectgarbage has different arguments.
This function is a generic interface to the garbage collector. It performs different functions according to its first argument, opt:
- "stop": stops the garbage collector.
- "restart": restarts the garbage collector.
- "collect": performs a full garbage-collection cycle.
- "count": returns the total memory in use by Lua (in Kbytes).
- "step": performs a garbage-collection step. The step "size" is controlled by arg (larger values mean more steps) in a non-specified way. If you want to control the step size you must experimentally tune the value of arg. Returns true if the step finished a collection cycle.
- "setpause": sets arg/100 as the new value for the pause of the collector.
- "setstepmul": sets arg/100 as the new value for the step multiplier of the collector.
- Function gcinfo is deprecated; use collectgarbage("count") instead.
- Numeric conversion in string.format now works for larger numbers. Previously doing this had a practical limit of 30-bit numbers:
print (string.format ("%d", 2^30)) --> 1073741824
print (string.format ("%d", 2^31)) --> -2147483648
Clearly the second answer is incorrect because internally a 32-bit number has wrapped around and become negative.
In the new version this is achieved with 64-bit numbers so we can now go up to 2^62, like this:
print (string.format ("%d", 2^62)) --> 4611686018427387904
print (string.format ("%d", 2^63)) --> -9223372036854775808
- The function coroutine.status now returns another value, namely "normal" under certain circumstances.
Changes in the API
These affect C programmers who are writing their own Lua modules. I won't go into all of them here, see the reference manual.
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|