Having trouble with variable number of arguments

Posted by Cage_fire_2000 on Wed 10 Sep 2008 03:08 PM — 18 posts, 64,314 views.

USA #0
Ok, from what I gathered from the manuals, you can define a function with a varying number of arguments like the example in the one manual:


    printResult = ""
    
    function print (...)
      for i,v in ipairs(arg) do
        printResult = printResult .. tostring(v) .. "\t"
      end
      printResult = printResult .. "\n"
    end


but when I try something similar to that, 'arg' is always nil, what am I doing wrong?

Netherlands #1
Funny that you ask. I had the same issue, and asked in the #lua irc channel around a week ago. Explanation is as follows:

The PIL is outdated, and describes Lua 5.0.x and not 5.1.x. In Lua 5.1.x the syntax changed so that arg does not exist anymore. Rather, you can get the various arguments through the {...} specifier. In my case, I solved it as follows:

printResult = ""
    
    function print (...)
      local arg = {...}
      for i,v in ipairs(arg) do
        printResult = printResult .. tostring(v) .. "\t"
      end
      printResult = printResult .. "\n"
    end


This because a literal replacement of all instances of arg by {...} does not work for some reason.
USA #2
Thanks
USA #3
Quote:
This because a literal replacement of all instances of arg by {...} does not work for some reason.


function foo (...)
print (...)  -- works fine
for _,v in ipairs{...} do  -- works fine. Sugar for ipairs({...})
print (v)
end
end
print ( {...}[1] )  --does not work.


But then again, {1,2,3}[1] doesn't work either. I guess you can't index a table that is in the process of being constructed. :)


USA #4
That's because syntactically you can index identifiers, not table constructors. I guess it's the difference between the value and the symbol name (i.e. identifier).

Also, it's more efficient to assign {...} to a temporary variable: otherwise, you're creating a new table every time.
Australia Forum Administrator #5
This works:


print ( ({ 1, 2, 3, 4 }) [1] )    --> 1


The extra brackets seem to force it to stop constructing the table and make it useable as such.

Similarly:


print ( "testing" : upper ())   -- Error: ')' expected near ':'

but:

print ( ("testing") : upper ())  --> TESTING


USA #6
That's interesting. It looks like you can index on expressions (of course -- not just identifiers, silly me) and the curlies are table constructors whereas the parentheses bring you to the expression level.
Netherlands #7
Hrm, I don't really see the reasoning why one (with parentheses) would work while the other would not (without parentheses). My reasoning is as follows: when you get the closing curly } the table has been completed for as far initialization is concerned, so it should be indexable. I see no other way to 'further' initialize it once it's been closed.

Likewise, the way to specify keys with ["xxx"] confuses me. Why does "xxx" not work? I've always taken it as a weird but necessary quirk of Lua's to work around, but it -does- bug me.
USA #8
I agree that a table constructor should basically be an expression syntactically, because as-is you can assign it to variables etc.

Specifying keys with ["xxx"] is a very highly desirable feature, because [xxx] means something quite different from "xxx" -- the former means the variable xxx and the latter means the string. It would be kind of disastrous if you could only index by strings...

There is syntactic sugar for the string form: t.xxx is the same as t["xxx"].
Netherlands #9
I know that t.xxx == t["xxx"]. However, either I misunderstand your explanation, or you misunderstood my question. I understand the difference between t["xxx"] and t[xxx] very well. However, given this:


local xxx = "q"
a = { ["xxx"] = "a" }  -- assigns "a" to a.xxx
b = { xxx = "a" }      -- assigns "a" to b.xxx
c = { [xxx] = "a" }    -- assigns "a" to c.q
d = { "xxx" = "a" }    -- error: '}' expected near '='


I'd expect the last table to work and have (d.xxx == "a") as a result. Why is -that- assignment not supported?
Amended on Thu 11 Sep 2008 06:38 PM by Worstje
USA #10
Oh. I thought you were talking about indexing in general, not keys in constructors.

Well, I think the reason is that if it sees a string value, it inserts it as into a list. Generally, the Lua authors do not like syntax that would require a complex grammar. You could infer that a string followed by an equal sign actually means a key, but that makes things more complicated. Furthermore it makes the syntax more visually complicated by adding extra sigils, which is another thing they tend to not like.

As for why ["xxx"] is allowed in the constructor, that is because it is a more general form of specifying a key by any value whatsoever, including "by accident" string values. It doesn't really strike me as odd that this is allowed but the straight string isn't.

Why is this really an issue though? Is the idea that the string form is more explicitly a string key than the identifier form?
Netherlands #11
Because it makes using tables as pre-initialized data-structures rather annoying. I switch between Lua and a bunch of other languages quite often, and I keep doing it wrong.

The form used in table a has two characters I never need to type in other languages, and I do it wrong atleast twice a week (to the point where I can dream the error I mentioned). The form in table b actually looks to me (in a simple glance) as if it should be assigning the contents of a variable, rather than a literal xxx. It directly opposes my gutfeeling.

While 'language X should be changed to be more like language Y' is a horrible (and flawed) argument, I find it hard to say that this syntax is very logical to me. I'm not too knowledgeable, but it seems to me you would only start to try and interpret what you've just read once you read the comma/ending brace that signifies the end of the previous entry. Lua is a very spartan scripting language, and I respect that (as much as I might hate it half the time), but in this case the decision not to support {"xx" = y} syntax boggles me kind of.
USA #12
I dunno, I guess I've never really found it counterintuitive. That might be because in Perl, you specify string keys in hash constructors as literals, not as strings. I also don't construct tables manually that often at all. Either I have a table that I want to save and use a serializer, in which case I don't care what it looks like under the hood, or I create a table and later populate its fields. From what you've written I can see where you're coming from though.

I don't know if there's a deeper reason behind their decision. Sometimes they have pretty good reasons (e.g. syntactic ambiguity) for not wanting such-and-such syntax. You could try bringing it up on the Lua mailing list. You might get a useful answer as to why they don't do it, or you might even get it considered as a feature request. :-)
Netherlands #13
Heh, I might do it. I'm not too fond of mailinglists though, but who knows. ^_^
Australia Forum Administrator #14
Quote:

Hrm, I don't really see the reasoning why one (with parentheses) would work while the other would not (without parentheses).


I asked that question on the Lua mailing list, as I was a bit puzzled too. Roberto Ierusalimschy replied as follows:




Because Lua does not enforce the use of semicollons, it needs other means to recognize statement boundaries. So, it is helpful to reduce the number of tokens that can start a statement. For instance, if the first syntax were allowed, people would expect the next one to be allowed too:


   {f, g, h}[1]("hi")    -- call 'f'


That would create ambiguities in the next fragment:


 a = i
 {f, g, h}[1]("hi")


It could mean any of these:


 a = i; {f, g, h}[1]("hi")
 a = i{f, g, h}[1]("hi")


Forcing the use of parentheses reduce the cases of ambiguity.


Australia Forum Administrator #15
Quote:

c = { [xxx] = "a" } -- assigns "a" to c.q
d = { "xxx" = "a" } -- error: '}' expected near '='


I'd expect the last table to work and have (d.xxx == "a") as a result. Why is -that- assignment not supported?


I think the answer would be, it would be ambiguous.

On the face of it, it would seem reasonable to support stuff like:


t = {
     "a" = 22,    -- t ["a"] = 22
     45 = 68,   -- t [45] = 68
     true = 99,  -- t [true] = 99
    2 + 2 = 55,  -- t [4] = 55
    }


Effectively by allowing any sort of constant there. However then you would expect any expression there too (like in the case of 2 + 2).

Now you hit a big problem:


foo = 42
t = {
     foo = 99,  -- t [42] = 99
    }


The entry "foo" is an expression, which happens to be a variable containing 42. However we are already used to that actually meaning:


foo = 42
t = {
     foo = 99,  -- t ["foo"] = 99
    }


So, exceptions would have to be made, to be consistent with what currently happens. The whole thing would probably become a mess.
Netherlands #16
Good reason, I am satisfied. ^_^

(Although I still say it's the wrong way around.. dum dum.)
USA #17
Well, you wouldn't have to allow arbitrary expressions -- you could stick with identifiers and string literals only. Of course, then you have an inconsistency of sorts of only allowing string constants.