4
votes

Continuing to learn Lua.

I have wrote a function that removes the first sentence from each line and returns the result as a table of modified lines, where the first sentence was removed. Strangely, table.insert behaves weird in such function.

function mypackage.remove_first(table_of_lines)
  local lns = table_of_lines
  local new_lns = {}
  for i=1,#lns do
    table.insert(new_lns,string.gsub(lns[i],"^[^.]+. ","",1))
  end
  return new_lns
end

Unexpectedly, this gave me the following error.

[string "function mypackage.remove_first(table_of_lines)..."]:5: bad argument #2 to 'insert' (number expected, got string)

Why is "number expected" in the first place?

From table.insert docs

Inserts element value at position pos in list, shifting up the elements list[pos], list[pos+1], ···, list[#list]. The default value for pos is #list+1, so that a call table.insert(t,x) inserts x at the end of list t.

Nothing is said about type requirements for table.insert. Ok, I decided to modify the example.

function mypackage.remove_first(table_of_lines)
  local lns = table_of_lines
  local new_lns = {}
  for i=1,#lns do
    local nofirst = string.gsub(lns[i],"^[^.]+. ","",1)
    table.insert(new_lns,nofirst)
  end
  return new_lns
end

And now everything works. Can you explain what is going on here?

1
string.gsub returns TWO values instead of one, and another (overloaded) version of table.insert is invokedEgor Skriptunoff
Hm, and one of the values is somehow invisible to print?minerals
@minerals: It's not invisible. When you have an expression that results in multiple values, and you assign it to fewer values, the extra ones are discarded. So local nofirst = string.gsub(...) will discard the second value.Nicol Bolas

1 Answers

5
votes

The problem is a bit complicated. It's a collision of three factors:

  1. string.gsub returns two parameters; the second parameter is the number of matches.

  2. table.insert can take 3 parameters. When it is given 3 parameters, the second parameter is expected to be an integer offset defining where to insert the object.

  3. When you do this: func1(func2()), all of the return values of func2 are passed to func1, so long as you don't pass arguments after func2 in func1's argument list. So func1(func2(), something_else) will get only 2 arguments.

Therefore, when you do table.insert(ins, string.gsub(...)), this will invoke the 3-argument version, which expects the second argument to be the index to insert the object into. Hence the problem.

If you want to ensure discarding, then you can wrap the expression in parenthesis:

table.insert(new_lns, (string.gsub(lns[i], "^[^.]+. ", "", 1)))