It looks like you're new here. If you want to get involved, click one of these buttons!
My program includes classes for Person, Queue, and Elevator objects. The Elevator class includes a method for moving an elevator up or down:
function Elevator:move()
if self.y > self.targety then
self.y = self.y - 1
if elevatorQ:numberIn() >= 1 then
for i = 1, elevatorQ:numberIn() do
elevatorQ[i] [1]:moveDown()
end
end
end
if self.y < self.targety then
self.y = self.y + 1
if elevatorQ:numberIn() >= 1 then
for i = 1, elevatorQ:numberIn() do
elevatorQ[i] [1]:moveUp() <--------- crashes here
end
end
end
end
Person objects have moveUp and moveDown methods, and I have a global Queue object called elevatorQ that contains references to Person objects that must move with the Elevator. When the code above runs, it crashes with the message "Attempt to index nil value" where I have indicated. elevator[i] [1] is the field that contains a reference to a Person. Can someone please tell me what I'm doing wrong here?
Comments
Sorry about the lack of indentation. When I pasted the code, the indentation was there; when it posted, it was gone!
@Eustace I've fixed it, you need to add
~~~
before and after the code to render it as code (you can edit your post to see what I added).Thanks, Simeon.
Off-topic, anyone else had issues with
back tick
code notation sometimes not working in the forum?let's try again: Either i or 1 is out of ranger of the elevator array. What does the
numberIn
method do? You can get the length of an array with#
, eg#elevatorQ
to get the length of the array's first dimension and then#elevatorQ[i]
to get the length of nested arrays. You can also check whether a nested array exists before trying to subscript it, egif elevatorQ[i] then
. If you add in these checks, your code will be safer. Though you should also try to find why you're trying to access it with an out of range index. Remember that each nested array must also be initialised before it can be accessed usingelevatorQ[i] = { }
^ weird, copied and pasted the same comment, and the back-tick notation works the second time, but not the first.
@yojimbo2000 What is
back tick
code notation. I would try it, but I'm not sure what it is.The numberIn method is needed because of the structure of the Queue objects. Each one is a table of tables. The first table contains an index key, and a table value. The tables then contain two index keys, first value to a reference to a Person object, the second to an integer value used elsewhere. When the Queue is constructed, the places where Person references will go is filled with zeros. The numberIn method goes through the Queue, counting Persons, until it finds a zero value.
Using the # would not give me the information I need. I already know the array length, I set that when I construct the Queue.
I'm not sure what you mean by "out of ranger of the elevator array". The elevatorQ has twelve places where references to Persons can be stored. When I run the method above, I have two Persons in the Queue. The numberIn method returns 2 (I tested that). So the innermost for/do loop should run twice, once for elevatorQ[1] [1], then again for elevatorQ[2] [1]. But it won't run even once. It won't run, even if I replace the i with a 1.
@Eustace The easiest way to figure out what's going on is to put a print statement just before the line causing the problem. Print out different values until you figure out what the cause is. It's hard to find a problem in someone else's code without having the code to run.
That's how I found out that numberIn was working right. I added print(elevatorQ:numberIn()) right before the inner for/do loop.
My problem right now is that I'm still really unfamiliar with Lua, so the possible things that could be wrong is a really long list, and the error messages don't mean much to me. Attempt to index nil value, for example. Is index being used as a verb in that sentence? It sounds like it. Does that mean that indexing is what happens when a value is replaced by another value in a table? Or is indexing what happens when a value is looked for (and in this case, perhaps, a nil is found)?
Is the thing I'm trying to do in this line allowed? I'm trying to call a Person method. As if the code read "aPersonObject:moveUp()". But instead of using a variable name to specify the Person object, I'm using the array location where the reference is stored, "elevatorQ[1] [1]:moveUp()". Is this allowed in Lua?
In your above code, you say
elevatorQ[i] [1]:moveUp()
causes the problem. You also said that the variablei
had a valid value. Since you get anil
error message, then that meanselevatorQ[i] [1]:moveUp()
isn't pointing to anything. You need to check on how you're creating an instance of whatever it's supposed to be pointing to. Like I said above, it's hard to debug someone else's code without having it to run.If I understand you correctly, you are saying that at the time the code runs, elevatorQ[i] [1] does not contain a reference to a Person object, so the Person class method moveUp() points to nil? What does that have to do with indexing?!? This error message is really confusing and frustrating to me.
Your elevatorQ[i][1]:moveUp() is being indexed by [i][1]. You have to look at elevatorQ and see how you're creating an instance using it. I'll try to give you an example later.
@Eustace Here's a simple example. I'm not sure if this will even help since I don't know what your classes look like.
Code removed. Not needed anymore.
I hope I'm not sounding too irritated here; I really appreciate that you are trying to help.
I was so happy and proud just yesterday - I thought I had gotten all the bugs out of this Queue class! I have working addTo() and removeFrom() methods. At least they appeared to be working. Sigh.
I am certainly creating instances of Persons and Queues. I know for sure about Persons, because each one has a visible, drawn appearance on the screen. I know for sure about Queues, because each one contains an x coordinate that the Person assigned to it uses as a target to move toward; and there they are, visibly marching toward their target once added to a Queue.
I have some buttons on the screen to generate events when pressed. The Add Person button creates an instance of a Person, and adds it to the floor1Q Queue. The person appears, and moves across the screen to it's assigned position. I hit the button a few more times; soon I have a line of Persons waiting at the elevator door.
The second button "moves" the person at the head of the floor1Q into elevatorQ. The function executed by this button looks like this:
When I hit the button, the person moves to the screen position assigned to elevatorQ[1]. Hit the button again, another person joins the first, offset by 10 pixels (each Queue position is 10 pixels off from the previous position, the x coordinate is stored in exampleQ[i] [2]).
Did I mention that the Queue objects are globals? They are created in the setup() function.
When I hit the third button, trouble. The function executed by this button looks like this:
The moveTo() method gives the elevator object a new target y coordinate. So far, so good. Now each time the draw() function runs, the elevator's y coordinate ticks one step closer to it's new target y. At least, that's what happens if I comment out the line that's crashing in the Elevator:move() method, the method I am having problems with, the method whose code I included in my original question.
So much is working! But I can't get my Elevator to move it's passengers. Not much of an elevator, is it?
could you post all of your code?
I'm not sure how best to do that. There's a lot of code here. Maybe if I do it one tab at a time?
Here is the main tab:
Next is the Draw_Functions tab:
Next, the Other_Functions tab:
Next, the Person class:
Next, the Queue class (I hope the problem isn't here, but it probably is):
Next, the Elevator class:
Next are the roundRect tab and Button classes I got from tutorials and example programs. Here's roundRect:
And here's the Button class:
That's everything. This is my second pass at writing this program; the first try used simple tables to contain Person objects - it got further along, the elevator kinda worked, people rode it up and got off at their floor, but the code just got buggier and buggier as I added to it. Time to refactor, and to make a better way to keep track of people - the Queue!
@Eustace I loaded your code. When I press add person, the person moves to the right. When I press test, the person gets on the elevator. When I press test 2 the program stops with the nil error. When I print the value of
I
before the line that gives the error, it has a value of nil. Is that the correct sequence of pressing the buttons.@Eustace
You're not accessing
elevatorQ
correctly.elevatorQ
is an instance of yourQueue
class.So you cannot directly index it with
elevatorQ[i][1]
or whatever.I think what you mean to index is the
qtable
ofelevatorQ
, no?So it should be
elevatorQ.qtable[i][1]
.I'm not sure what you mean by value of i.
If I replace the inner for/do loop with:
...so that the problem line is commented out, then when the code runs the value of i is printed out over and over as the elevator rises without its passengers.
Do you mean the value stored at elevatorQ[i] [1]? I haven't tried that. I'm not sure how print() would handle a reference to an object.
@Eustace My mistake. I left the print statement in the wrong spot when I was printing something else. See what @yojimbo2000 has to say.
@Eustace
elevatorQ
is not a table. So you cannot subscript it like a table. It is an instance of a class, which contains a table calledqtable
. That is what you should be trying to subscript withelevatorQ.qtable[i][1]
Somehow I knew that when the answer appeared, two things would happen:
1. I would feel a bit dim for not realizing what the problem was, and
2. I would learn something valuable about Lua.
Thanks, yojimbo2000.
Thanks to all of you, really. What a great forum!
you're welcome, another pair of eyes on some code is often all that's required.
Lua's flexibility with types does make it easy to work with, but does also lead to errors like this one, which would be caught by the compiler in languages that have type-safety.
With nested arrays you can do a series of tests before you try to subscript eg
this is more concise then checking whether the two indices are in range and will prevent the "Attempt to index nil value" error, as Lua will stop evaluating the expression as soon as it hits the first failing clause (although it will just fail silently if either index is invalid, which might also lead to unintended side effects).
That looks like a useful check, thanks.
At least with your code, you knew exactly what line the error was. So "unsafe" code (as in, easy to make crash) can be more communicative than code that fails silently (it doesn't crash, but doesn't tell you there's an issue either).