Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Error in method

edited March 14 in Questions Posts: 28

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

  • Posts: 28

    Sorry about the lack of indentation. When I pasted the code, the indentation was there; when it posted, it was gone!

  • SimeonSimeon Admin Mod
    Posts: 4,352

    @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).

  • Posts: 28

    Thanks, Simeon.

  • edited March 14 Posts: 1,979
    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, eg `if 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 using `elevatorQ[i] = { }`
  • Posts: 1,979

    Off-topic, anyone else had issues with back tick code notation sometimes not working in the forum?

  • Posts: 1,979

    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, eg if 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 using elevatorQ[i] = { }

  • Posts: 1,979

    ^ weird, copied and pasted the same comment, and the back-tick notation works the second time, but not the first.

  • dave1707dave1707 Mod
    Posts: 5,821

    @yojimbo2000 What is back tick code notation. I would try it, but I'm not sure what it is.

  • Posts: 28

    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.

  • dave1707dave1707 Mod
    Posts: 5,821

    @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.

  • Posts: 28

    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)?

  • Posts: 28

    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?

  • dave1707dave1707 Mod
    Posts: 5,821

    In your above code, you say elevatorQ[i] [1]:moveUp() causes the problem. You also said that the variable i had a valid value. Since you get a nil error message, then that means elevatorQ[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.

  • Posts: 28

    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.

  • dave1707dave1707 Mod
    Posts: 5,821

    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.

  • dave1707dave1707 Mod
    edited March 14 Posts: 5,821

    @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.

  • edited March 14 Posts: 28

    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:

    function test()
        elevatorQ:addTo(floor1Q:releaseFrom(1))
        print("test")
    end
    

    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:

    function test2()
        elevator1:moveTo(205)
        print("test2")
    end
    

    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?

  • Posts: 425

    could you post all of your code?

  • edited March 14 Posts: 28

    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:

    -- Elevator2
    
    -- Use this function to perform your initial setup
    function setup()   
        -- setup touch buttons
        addPersonButton = Button("Add Person")
        addPersonButton.pos = vec2(100, HEIGHT - 70)
        addPersonButton.action = function () addPerson() end
        otherButton = Button("Test")
        otherButton.pos = vec2(300, HEIGHT - 70)
        otherButton.action = function () test() end
        anotherButton = Button("Test 2")
        anotherButton.pos = vec2(400, HEIGHT - 70)
        anotherButton.action = function () test2() end
    
        -- setup globals
        persons = {}
        setCallButtons()
        elevator1 = Elevator(510, 25)
        floor1Q = Queue(100, 445)
        floor2Q = Queue(100, 445)
        floor3Q = Queue(100, 445)
        floor4Q = Queue(100, 445)
        elevatorQ = Queue(12, 660)
    end
    
    -- This function gets called once every frame
    function draw()
        -- This sets a dark background color 
        background(40, 40, 50)
    
        -- draw functions
        drawBuilding()
        drawCallButtons()
        addPersonButton:draw()
        otherButton:draw()
        anotherButton:draw()
        elevator1:draw()
        drawPeople()
    
        -- move functions
        elevator1:move()
        movePeople()
    end
    
    function touched(touch)   
        if touch.state == ENDED then
            addPersonButton:touched(touch)
            otherButton:touched(touch)
            anotherButton:touched(touch)
        end
    end
    
  • Posts: 28

    Next is the Draw_Functions tab:

    function drawBuilding()
        pushStyle()
    
        fill(200, 200, 200, 255)
        rect(20, 20, 710, 730)
        stroke(0, 0, 0, 255)
        strokeWidth(5)
        line(25, 25, 500, 25)
        line(25, 205, 500, 205)
        line(25, 385, 500, 385)
        line(25, 565, 500, 565)
        fill(150, 150, 150, 255)
        strokeWidth(4)
        rect(50, 205, 70, 140)
        rect(50, 385, 70, 140)
        rect(50, 565, 70, 140)
        rect(190, 205, 70, 140)
        rect(190, 385, 70, 140)
        rect(190, 565, 70, 140)
        rect(330, 205, 70, 140)
        rect(330, 385, 70, 140)
        rect(330, 565, 70, 140)
        fill(220, 220, 220, 255)
        strokeWidth(1)
        ellipse(108, 270, 15)
        ellipse(108, 450, 15)
        ellipse(108, 630, 15)
        ellipse(248, 270, 15)
        ellipse(248, 450, 15)
        ellipse(248, 630, 15)
        ellipse(388, 270, 15)
        ellipse(388, 450, 15)
        ellipse(388, 630, 15)
    
        popStyle()
    end
    
    function drawCallButtons()
        pushStyle()
        -- draw call button arrows
        strokeWidth(3)
        stroke(0, 0, 0, 255)
    
        -- level 1 up
        if lev1Up then
            lev1UpMesh.colors = {colorLit, colorLit, colorLit}
        else lev1UpMesh.colors = {colorDark, colorDark, colorDark}
        end
        lev1UpMesh:draw()
        line(480, 180, 500, 180)
        line(500, 180, 490, 195)
        line(480, 180, 490, 195)
    
        -- level 2 up
        if lev2Up then
            lev2UpMesh.colors = {colorLit, colorLit, colorLit}
        else lev2UpMesh.colors = {colorDark, colorDark, colorDark}
        end
        lev2UpMesh:draw()
        line(480, 360, 500, 360)
        line(500, 360, 490, 375)
        line(480, 360, 490, 375)
    
        -- level 2 down
        if lev2Down then
            lev2DownMesh.colors = {colorLit, colorLit, colorLit}
        else lev2DownMesh.colors = {colorDark, colorDark, colorDark}
        end
        lev2DownMesh:draw()
        line(480, 355, 500, 355)
        line(500, 355, 490, 340)
        line(480, 355, 490, 340)
    
        -- level 3 up
        if lev3Up then
            lev3UpMesh.colors = {colorLit, colorLit, colorLit}
        else lev3UpMesh.colors = {colorDark, colorDark, colorDark}
        end
        lev3UpMesh:draw()
        line(480, 540, 500, 540)
        line(500, 540, 490, 555)
        line(480, 540, 490, 555)
    
        -- level 3 down
        if lev3Down then
            lev3DownMesh.colors = {colorLit, colorLit, colorLit}
        else lev3DownMesh.colors = {colorDark, colorDark, colorDark}
        end
        lev3DownMesh:draw()
        line(480, 535, 500, 535)
        line(500, 535, 490, 520)
        line(480, 535, 490, 520)
    
        -- level 4 down
        if lev4Down then
            lev4DownMesh.colors = {colorLit, colorLit, colorLit}
        else lev4DownMesh.colors = {colorDark, colorDark, colorDark}
        end
        lev4DownMesh:draw()
        line(480, 715, 500, 715)
        line(500, 715, 490, 700)
        line(480, 715, 490, 700)
        popStyle()
    end
    
    function drawPeople()
        if persons[1] ~= nil then
            for i = 1, #persons do   
                persons[i]:draw()
            end 
        end
    end
    
    
  • Posts: 28

    Next, the Other_Functions tab:

    function setCallButtons()
        -- call button states
        lev1Up = false
        lev2Down = false
        lev2Up = false
        lev3Down = false
        lev3Up = false
        lev4Down = false
    
        -- call button colors
        colorLit = color(0, 0, 255, 255)
        colorDark = color(150, 150, 150, 255)
    
        -- call button meshes
        lev1UpMesh = mesh()
        lev1UpMesh.vertices = {vec2(480,180),vec2(500,180),vec2(490,195)}
        lev1UpMesh.colors = {colorDark, colorDark, colorDark}
        lev2UpMesh = mesh()
        lev2UpMesh.vertices = {vec2(480,360),vec2(500,360),vec2(490,375)}
        lev2UpMesh.colors = {colorDark, colorDark, colorDark}
        lev2DownMesh = mesh()
        lev2DownMesh.vertices = {vec2(480,355),vec2(500,355),vec2(490,340)}
        lev2DownMesh.colors = {colorDark, colorDark, colorDark}
        lev3UpMesh = mesh()
        lev3UpMesh.vertices = {vec2(480,540),vec2(500,540),vec2(490,555)}
        lev3UpMesh.colors = {colorDark, colorDark, colorDark}
        lev3DownMesh = mesh()
        lev3DownMesh.vertices = {vec2(480,535),vec2(500,535),vec2(490,520)}
        lev3DownMesh.colors = {colorDark, colorDark, colorDark}
        lev4DownMesh = mesh()
        lev4DownMesh.vertices = {vec2(480,715),vec2(500,715),vec2(490,700)}
        lev4DownMesh.colors = {colorDark, colorDark, colorDark}
    end
    
    function addPerson()
        table.insert(persons, Person(30, 30))
        floor1Q:addTo(persons[#persons])
    end
    
    function movePeople()
        if persons[1] ~= nil then
            for i = 1, #persons do
                persons[i]:move()
            end
        end
    end
    
    function test()
        elevatorQ:addTo(floor1Q:releaseFrom(1))
        print("test")
    end
    
    function test2()
        elevator1:moveTo(205)
        print("test2")
    end
    
  • Posts: 28

    Next, the Person class:

    Person = class()
    
    function Person:init(x, y)
        -- location of draw origin
        self.x = x
        self.y = y
        self.targetx = 150
    
        -- current motion state
        -- 1 - moving toward elevator wait queue
        -- 2 - waiting for elevator to arrive
        -- 3 - moving into elevator queue
        -- 4 - waiting for elevator to reach floor
        -- 5 - moving toward destination room
        -- 6 - waiting in room
        self.state = 1
    
        -- flag to change state
        self.bumpState = false
    
        -- floor currently on
        self.currentFloor = 1
    
        -- destination floor
        self.destFloor = math.random(2, 4)
        self.destRoom = math.random(1, 3)
    end
    
    function Person:draw()
        -- draw shape of person
        pushStyle()
        smooth()
        fill(255, 255, 255, 131)
        stroke(0, 0, 0, 255)
        strokeWidth(3)
        rect(self.x, self.y, 50, 80)
        strokeWidth(2)
        ellipse(self.x + 25, self.y + 103, 50, 50)
        popStyle()
    
        -- check for need to change state
        if self.bumpState == true then
            self.state = self.state + 1
            self.bumpState = false
        end
    
    
    end
    
    function Person:move()
        if self.x < self.targetx then
            self.x = self.x + 1
        end
        if self.x > self.targetx then
            self.x = self.x - 1
        end
    end
    
    --function Person:moveRight()
        --self.x = self.x + 1
    --end
    
    --function Person:moveLeft()
        --self.x = self.x - 1
    --end
    
    function Person:moveUp()
        self.y = self.y + 1
    end
    
    function Person:moveDown()
        self.y = self.y - 1
    end
    
    function Person:touched(touch)
        -- Codea does not automatically call this method
    end
    
  • Posts: 28

    Next, the Queue class (I hope the problem isn't here, but it probably is):

    Queue = class()
    
    function Queue:init(a, b)
        -- a sets queue length
        -- b sets place moving person stops
        self.length = a
        self.firstX = b
    
        self.qtable = {}
        for i = 1, self.length do
            self.qtable[i] = {}
            for j = 1, 2 do
                self.qtable[i] [j] = 0
            end
        end
        j = self.firstX
        for i = 1, self.length do
            self.qtable[i] [2] = j
            j = j - 10
        end
    end
    
    function Queue:addTo(p)
        -- this function will add a person to the queue in the first open spot
        local person = p
        if person ~= false then
            for i = 1, self.length do
                if self.qtable[i] [1] == 0 then
                    self.qtable[i] [1] = person
                    person.targetx = self.qtable[i] [2]
                    break
                end
            end
        end
    end
    
    function Queue:releaseFrom(x)
        local position = x
        local counter = 0
        for i = 1, self.length do
            if self.qtable[i] [1] ~= 0 then
                counter = counter + 1
            end
        end
        local diff = counter - position + 2
        local person = self.qtable[position] [1]
        self.qtable[position] [1] = 0
        for i = position, diff do
            local j = i + 1
            local nextPerson = self.qtable[j] [1]
            if nextPerson ~= 0 then
                nextPerson.targetx = self.qtable[i] [2]
            end
            self.qtable[i] [1] = nextPerson
            self.qtable[j] [1] = 0
        end
        if person ~= 0 then
        return person
        else return false
        end
    end
    
    function Queue:numberIn()
        local number = 0
        for i = 1, self.length do
            if self.qtable[i] [1] ~= 0 then
                number = number + 1
            end
        end
        return number
    end
    
    function Queue:draw()
        -- Codea does not automatically call this method
    end
    
    function Queue:touched(touch)
        -- Codea does not automatically call this method
    end
    
  • Posts: 28

    Next, the Elevator class:

    Elevator = class()
    
    function Elevator:init(x, y)
        -- you can accept and set parameters here
        self.x = x
        self.y = y
        self.currentFloor = 1
        self.destFloor = 1
        self.targety = 25
    
        -- floor button lights off
        self.light1 = false
        self.light2 = false
        self.light3 = false
        self.light4 = false
    
        -- current motion state
        -- 1 - idle
        -- 2 - door opening
        -- 3 - door closing
        -- 4 - moving up
        -- 5 - moving down
    
        self.state = 1
    
        self.doorHeight = 180
    end
    
    function Elevator:draw()
        pushStyle()
        stroke(0, 0, 0, 255)
        strokeWidth(5)
    
        -- door opening
        if self.state == 2 then
            self.doorHeight = self.doorHeight - 2
            if self.doorHeight == 0 then
                self.state = 1
            end
        end
    
        -- door closing
        if self.state == 3 then
            self.doorHeight = self.doorHeight + 2
            if self.doorHeight == 180 then
                self.state = 1
            end
        end
    
        -- draw elevator outline
        line(self.x, self.y, self.x + 215, self.y)
        line(self.x + 215, self.y, self.x + 215, self.y + 180)
        line(self.x + 215, self.y + 180, self.x, self.y + 180)
        -- line for door
        line(self.x, self.y, self.x, self.y + self.doorHeight)
    
        -- draw floor button lights
        strokeWidth(2)
        fill(150, 150, 150, 255)
        ellipse(self.x + 15, self.y + 85, 20, 20)
        ellipse(self.x + 15, self.y + 110, 20, 20)
        ellipse(self.x + 15, self.y + 135, 20, 20)
        ellipse(self.x + 15, self.y + 160, 20, 20)
        fill(0, 255, 0, 255)
        if self.light1 then
            ellipse(self.x + 15, self.y + 85, 20, 20)
        end
        if self.light2 then
            ellipse(self.x + 15, self.y + 110, 20, 20)
        end
        if self.light3 then
            ellipse(self.x + 15, self.y + 135, 20, 20)
        end
        if self.light4 then
            ellipse(self.x + 15, self.y + 160, 20, 20)
        end
        popStyle()
    end
    
    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()
                end
            end
        end
    end
    
    function Elevator:moveTo(y)
        self.targety = y
    end
    
    
    
  • Posts: 28

    Next are the roundRect tab and Button classes I got from tutorials and example programs. Here's roundRect:

    function roundRect(x,y,w,h,r)
        pushStyle()
    
        insetPos = vec2(x+r,y+r)
        insetSize = vec2(w-2*r,h-2*r)
    
        --Copy fill into stroke
        local red,green,blue,a = fill()
        stroke(red,green,blue,a)
    
        noSmooth()
        rectMode(CORNER)
        rect(insetPos.x,insetPos.y,insetSize.x,insetSize.y)
    
        if r > 0 then
            smooth()
            lineCapMode(ROUND)
            strokeWidth(r*2)
    
            line(insetPos.x, insetPos.y, 
                 insetPos.x + insetSize.x, insetPos.y)
            line(insetPos.x, insetPos.y,
                 insetPos.x, insetPos.y + insetSize.y)
            line(insetPos.x, insetPos.y + insetSize.y,
                 insetPos.x + insetSize.x, insetPos.y + insetSize.y)
            line(insetPos.x + insetSize.x, insetPos.y,
                 insetPos.x + insetSize.x, insetPos.y + insetSize.y)            
        end
        popStyle()
    end
    
  • Posts: 28

    And here's the Button class:

    Button = class()
    
    function Button:init(displayName)
        -- you can accept and set parameters here
        self.displayName = displayName
    
        self.pos = vec2(0,0)
        self.size = vec2(0,0)
        self.action = nil
        self.color = color(124, 123, 126, 255)
    end
    
    function Button:draw()
        -- Codea does not automatically call this method
        pushStyle()
        fill(self.color)
    
        font("ArialRoundedMTBold")
        fontSize(22)
    
        -- use name for size
        local w,h = textSize(self.displayName)
        w = w + 20
        h = h + 30
    
        roundRect(self.pos.x - w/2,
                  self.pos.y - h/2,
                  w,h,30)
    
        self.size = vec2(w,h)
    
        textMode(CENTER)
        fill(54, 65, 96, 255)
        text(self.displayName,self.pos.x+2,self.pos.y-2)
        fill(255, 255, 255, 255)
        text(self.displayName,self.pos.x,self.pos.y)
    
        popStyle()
    end
    
    function Button:hit(p)
        local l = self.pos.x - self.size.x/2
        local r = self.pos.x + self.size.x/2
        local t = self.pos.y + self.size.y/2
        local b = self.pos.y - self.size.y/2
        if p.x > l and p.x < r and
           p.y > b and p.y < t then
            return true
        end
    
        return false
    end
    
    function Button:touched(touch)
        -- Codea does not automatically call this method
        if touch.state == ENDED and
           self:hit(vec2(touch.x,touch.y)) then
            if self.action then
                self.action()
            end
        end
    end
    
  • Posts: 28

    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!

  • dave1707dave1707 Mod
    Posts: 5,821

    @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.

  • Posts: 1,979

    @Eustace

    You're not accessing elevatorQ correctly.

    elevatorQ is an instance of your Queue class.

    So you cannot directly index it with elevatorQ[i][1] or whatever.

    I think what you mean to index is the qtable of elevatorQ, no?

    So it should be elevatorQ.qtable[i][1].

  • Posts: 28

    I'm not sure what you mean by value of i.
    If I replace the inner for/do loop with:

    for i = 1, elevatorQ:numberIn() do
                --elevatorQ[i] [1]:moveUp()
                print(i)
                end
    

    ...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.

  • dave1707dave1707 Mod
    Posts: 5,821

    @Eustace My mistake. I left the print statement in the wrong spot when I was printing something else. See what @yojimbo2000 has to say.

  • Posts: 1,979

    @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 called qtable. That is what you should be trying to subscript with elevatorQ.qtable[i][1]

  • Posts: 28

    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.

  • Posts: 28

    Thanks to all of you, really. What a great forum!

  • Posts: 1,979

    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

    if elevatorQ.qtable[i] and elevatorQ.qtable[i][1] then elevatorQ.qtable[i][1]:moveUp() end
    

    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).

  • Posts: 28

    That looks like a useful check, thanks.

  • Posts: 1,979

    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).

Sign In or Register to comment.