Search code examples
iolua

lua factorial, io.read reading a number


I was looking at a lua factorial function, and the part I don't understand is why is there "*n" in the io.read argument?? I know that io.read returns a string value but what is the "*n" for?? It change the value to number but how does it work? I found out that i can also do "*number", so does it work with anything that is *n.....?

function fact(n) 
    if n < 0 then
        return "undefine"
    elseif n==0 then
     return 1 
     else 
        return n * fact(n-1) 
     end 
end

print("Enter a number:") 
a = io.read("*n") -- reads a number *n == *number
print(fact(a))

Solution

  • The only documented formats are:

    • no format: See "*l"
    • number: reads a string with up to this number of bytes, returning nil on end of file. If number is zero, it reads nothing and returns an empty string, or nil on end of file.
    • "*n": reads a number; this is the only format that returns a number instead of a string.
    • "*a": reads the whole file, starting at the current position. On end of file, it returns the empty string.
    • "*l": reads the next line skipping the end of line, returning nil on end of file. This is the default format.
    • "*L": reads the next line keeping the end of line (if present), returning nil on end of file.

    Looking at the sources (liolib.c, 424ff.), the implementation is much more accepting:

    1. Checks whether there is an argument and otherwise reads a line.
    2. Checks for a value of type "number", in which case it reads the indicated number of bytes.
    3. Converts the argument to a string.
    4. Compares the first two characters with the documented formats, without checking the string's size.

    The last point is not a bug for strings with less than two characters (excluding possibly the empty string), because Lua-strings have a 0-terminator for interoperability that is not part of the length.


    Taking a look at the "*n"-format, that's the code for it:

    static int read_number (lua_State *L, FILE *f) {
      lua_Number d;
      if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) {
        lua_pushnumber(L, d);
        return 1;
      }
      else {
       lua_pushnil(L);  /* "result" to be removed */
       return 0;  /* read fails */
      }
    }
    
    1. Try to read a number with fscanf using format LUA_NUMBER_SCAN (configurable, standard is "%lf", see luaconf.h).
    2. Return the number or nil on failure.