This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
balispy's profile picture

java.lang.arrayindexoutofboundsexception?

Started by balispy, 15 July 2017 - 04:55 AM
balispy #1
Posted 15 July 2017 - 06:55 AM
http://i.imgur.com/3lA6qpG.png

Here's a picture of the error

https://pastebin.com/yf5bMew5

And here's my code.

I'm not really sure what's causing the issue so any help would be nice. Thanks!
Bomb Bloke #2
Posted 15 July 2017 - 07:48 AM
Every time you call a function, a new "instance" of that function goes on the end of the "function stack" (really a Java array with LuaJ, as is used by ComputerCraft). Every time a function completes, it's removed from the stack and execution continues from the point where the previous function instance left off.

Your list() function calls redstoneCheck(), which calls saveFile(), which calls list() which calls redstoneCheck() which calls saveFile() etc…

Because none of these function instances ever return, but rather just keep adding new instances to the top of the stack, eventually this infinite loop causes you run out of the memory allocated towards such shenanigans and the resulting error is thrown.
balispy #3
Posted 15 July 2017 - 06:28 PM
Every time you call a function, a new "instance" of that function goes on the end of the "function stack" (really a Java array with LuaJ, as is used by ComputerCraft). Every time a function completes, it's removed from the stack and execution continues from the point where the previous function instance left off.

Your list() function calls redstoneCheck(), which calls saveFile(), which calls list() which calls redstoneCheck() which calls saveFile() etc…

Because none of these function instances ever return, but rather just keep adding new instances to the top of the stack, eventually this infinite loop causes you run out of the memory allocated towards such shenanigans and the resulting error is thrown.



Ahhhh okay, that makes a lot of sense. Thanks! Is there any way you would suggest handling a bunch of functions like this? Is it a bad idea to have functions calling each other or should I just be fine as long as they don't loop? I'm really new to lua coming from only a little experience with java so I wanna create good habits lol
KingofGamesYami #4
Posted 15 July 2017 - 07:26 PM
Don't have functions call each other. There are certain circumstances where this should be used, but not many.
balispy #5
Posted 15 July 2017 - 07:33 PM
Don't have functions call each other.  There are certain circumstances where this should be used, but not many.



While I have you guys here, can you check my code again? I'm having trouble figuring out how to remove items from my main table with a delete function. I also don't really understand the whole "for name, data in pairs(item) do" thing and how it all works. Can someone explain that?

and I forgot, I need to find a way to convert the color input from the newItem() function to the actual color number. As of right now the function only takes in say color.magenta as "color.magenta" and saves it under the color key in the table. I need someway to convert that over to the number version of color.magenta so it can be added with the rest of the colors and set the bundled output.

I actually found a hacky way to do the delete, but it just removes all the info from the key and leaves the key there. Then I just skip over any keys that == nil. I'm sure there's a better way to handle it
Edited on 15 July 2017 - 11:33 PM
Bomb Bloke #6
Posted 16 July 2017 - 03:59 AM
Is it a bad idea to have functions calling each other or should I just be fine as long as they don't loop?

It's fine to have functions call each other (the stack can hold over a couple of hundred instances, after all), but it's usually a bad idea to form any sort of loop out of function calls.

Personally I discourage defining functions at all unless you're going to call them from multiple points in your script. If the code block is large enough then sure it might be worth bundling away, but don't try and use functions as comments. Creating a one-shot two-line function is of no benefit; it simply makes your code harder to read.

I also don't really understand the whole "for name, data in pairs(item) do" thing and how it all works. Can someone explain that?

The basic structure at work here is "for <values> in <function> do". The loop calls the provided function over and over, assigning its return values to the variables you supply, stopping once the function returns nil.

In this case, the function used is returned by "pairs(item)". pairs() accepts a table and returns another function that'll return values from that table each time it's called, finally returning nil once it runs out of unique table keys. Functions such as the ones generated by pairs() are dubbed "iterator functions", as they're most frequently used inside loops.

The first value returned is the name of each table key, and the second value returned is the data associated with that key. Hence another way you could write your deletion loop is:

    for i, data in pairs(item) do
        if data["num"] == p1 then
            item[i] = nil  -- Remove the table pointer from the "item" table.
            break          -- Exit the loop.
        end
    end

I need to find a way to convert the color input from the newItem() function to the actual color number. As of right now the function only takes in say color.magenta as "color.magenta" and saves it under the color key in the table. I need someway to convert that over to the number version of color.magenta so it can be added with the rest of the colors and set the bundled output.

I notice that you're overwriting the default "colors" table on line 4. It's generally better to avoid pre-existing APIs when choosing your variable names.

You'd be better off having your newItem() function accept strings representing keys in the original colors table. Let's say the user enters "magenta"; you can then later get the numeric color representation via colors[ item[ name ][ "colorString" ] ].

It's also worth noting that in a conditional statement, anything that isn't false / nil counts as true. This means, for example, that you can do:

repeat
  write("Enter a valid colour: ")
  colorString = read()
until colors[ colorString ]

This likewise means that code such as "if on == true then" is redundant (just do "if on then").

Truth be told, you'd really be better off indexing your "items" table by number rather than name. This'd allow you to use table.remove(), which'd re-index all the other table entries: allowing you to easily keep the numbers contiguous. It'd also mean that you wouldn't have to use loops to figure out where key number X is.

A couple of other quick tips, this sort of thing:

    repeat
        event, p1 = os.pullEvent()
    until event == "char"

… can be done by simply passing the desired event type to os.pullEvent():

        event, p1 = os.pullEvent("char")

If you want to convert "items" to a string (as you seem to be doing on line 108), it's much neater to run it through tostring() than it is to concatenate it.