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

[LUA] Any suggestions to make my program better?

Started by Felkami, 21 February 2013 - 02:14 PM
Felkami #1
Posted 21 February 2013 - 03:14 PM
Hello! I just wrote my first program in LUA program ever. It's an automatic slaughterbot that feeds and brutally murders the livestock. I was wondering if anyone would mind glancing at the code and suggesting where I might be able to improve things. Including whitespace and comments, the code is at 331 lines.

The code uses the Feeder addon from Misc. Peripherals.

The Inventory() method is the weak point, I think. It adds up all the empty space in all the slots and compares that to a number to decide if the inventory is full. I'm not sure if there's a better way to do that.

I'd also like to get away from using the misc. peripherals feeder. It blocks redstone current and I'd rather use a modem. Any ideas how I might feed animals without it?

Here it is: http://pastebin.com/yERQWcbP
Lyqyd #2
Posted 21 February 2013 - 04:27 PM
Split into new topic.
Bubba #3
Posted 21 February 2013 - 05:38 PM
Have you had programming experience prior to Lua? I notice that you used terms some programming linguistic terms, which I don't see many new programmers do.

Whether you've had prior experience or not though, I think that overall your code is excellent! In fact, the only real suggestions that I do have are just semantics.

Here's a few things that I noticed:
- The first thing I noticed is that you're not localizing functions. Not a huge biggy, but unless you want those functions clogging up your global environment then you should probably do some localization.
- The second thing that immediately jumped out to me was coroutines. I don't mind using coroutines when it's appropriate, but I think it's a bit misplaced here.
–Alternative:

--Currently, I see that you are using sleep(60), which of course blocks input unless you use coroutines. But what if we use timers instead...
--Example of timer/input usage
local function sleepInput(time)
  local timerID = os.startTimer(time)
  while true do
	local event = {os.pullEvent()}
	if event[1] == "timer" and event[2] == timerID then
	  return true, "Sleep"
	elseif event[1] == "key" and event[2] == keys.enter then
	  return true, "Key"
	end
  end
end
Why is this at all preferable? Well to be honest, it is only my opinion that it simplifies things a good deal in comparison to using coroutines (not that coroutines are particularly complicated, but it keeps relevant code in a singular location/function as opposed to splitting the code amongst several functions).

- Dangling variables:
I see that you are familiar with tables from the Save() function, but you don't initially store your variables in a table for convenient saving. Why not take all those variables and stick them into a variable table rather than having to manually do it during the save function? This would also simplify restoration of the variables due to the fact that you wouldn't have to type out each variable again.

Honestly, I don't really have anything else. Your Inventory function is fine in my eyes, but I suppose you could quicken it a bit by having it return immediately upon discovering that there is in fact enough space as opposed to going through the entire inventory twice. No need to actually be aware of exactly how much space is left, just that you have enough to do the job.

Nice work :)/>

Edit: One thing I do to keep my programs a little neater is to store functions and variables inside of tables like so:

local vars = {
  importantVars = {5,4,3,2,1};
  saveFile = "hello.txt";
  save = function(self)
	local f = fs.open(self.saveFile, "w")
	f.write(textutils.serialize(self.importantVars))
	f.close()
  end;
}

--Now just to call it
vars.importantVars[1] = 3 --Set a variable to a different value
vars:save() --Save it

I don't know if you want to use this, but I find that it helps enormously when I to write longer programs.
Felkami #4
Posted 21 February 2013 - 06:10 PM
Thanks for the compliments, Bubba. :-)

Yes, I have previous coding experience, but certainly not professional level. I just enjoy writing programs! And those suggestions are the exact reason I made this post. :-) LUA is a new language for me, previous to this I've coded primarily in Java, so mistakes are a given.

Lacking the localizations on my functions is a rather big deal and I thank you very much for that–I used the function tutorial on the computercraft wiki and all its examples don't use localization either, hence my failure. Also, good catch on that inventory improvement.

Is there a performance advantage to using a vartable? I essentially have 3 groups of major variables: main() variables, user-set variables, and global tracking variables. Each group is relatively small, and combining them all into a single vartable would involve a fair bit of rewrite considering their dispersal. The easiest would be sticking them all as global, but that'd be a bit sloppy. Unless I misunderstand what you're suggesting.
Bubba #5
Posted 21 February 2013 - 06:25 PM
Is there a performance advantage to using a vartable? I essentially have 3 groups of major variables: main() variables, user-set variables, and global tracking variables. Each group is relatively small, and combining them all into a single vartable would involve a fair bit of rewrite considering their dispersal. The easiest would be sticking them all as global, but that'd be a bit sloppy. Unless I misunderstand what you're suggesting.

Nope, there's no performance advantage whatsoever. Just ease of design when it comes to larger programs. I don't really mean to suggest that you go and rewrite those sections - it works perfectly fine as it is. The only reason I mentioned it is because a large program would be considerably more difficult to save/reload if you have a lot of variables and use the same technique, and it may save you some writing/debugging time if you plan on do so :)/>

Essentially due to the size of your program there isn't really anything to criticize, but in a larger coding project I can imagine some of those techniques being more difficult to sustain. Hence the reason I suggested var tables and the like.
JokerRH #6
Posted 22 February 2013 - 10:43 AM
This looks pretty good.
The only thing I just found is a really simple thing.
If you're looking at lua's definition of boolean variables, nil is false and anything else is true. So at one point you wrote something like
endind = ending or false
So in case ending is nil this line says
ending = false or false
So you could remove that line…

I don't think this will change anything notable, it's just "for the look" :D/>
Felkami #7
Posted 22 February 2013 - 01:43 PM
JokerRH,

The things we learn! I'll pull that out, one less line of code.
ChunLing #8
Posted 23 February 2013 - 09:52 AM
Well…actually false and nil are distinct values. A nil value basically means that there isn't even a memory allocation associated with an identifier, whereas false means that there is a memory allocation of a boolean type and it has the value false.

However, as long as you don't use direct comparison to a boolean of false, like: if ending == false then … end, there isn't a big difference, since for conditionals nil counts as false. It just doesn't equate for comparison.
Felkami #9
Posted 23 February 2013 - 11:52 AM
Here's something freakish.

I went and changed all the functions to local, now if a function isn't defined above a function calling it in the program, it acts as if that function doesn't exist.

So, if my first function defined is Test1() and it calls function Test2(), but function Test2() is defined after Test1(), I get an "attempt to call nil" error, but if Test2() is defined before Test1() everything runs peachy.

Is this normal functionality for LUA?
Bubba #10
Posted 23 February 2013 - 12:02 PM
Here's something freakish.

I went and changed all the functions to local, now if a function isn't defined above a function calling it in the program, it acts as if that function doesn't exist.

So, if my first function defined is Test1() and it calls function Test2(), but function Test2() is defined after Test1(), I get an "attempt to call nil" error, but if Test2() is defined before Test1() everything runs peachy.

Is this normal functionality for LUA?

Indeed it is. If you need to call a function that you have not created yet, then you need to define it first like so:

local a, b
a = function() print(b()) end
b = function() return "TEST" end

This guy explains it pretty well.
Felkami #11
Posted 23 February 2013 - 12:43 PM
That's pretty terrible. I can definitely foresee that limitation causing issues with things like recursive algorithms.

Okay, thanks for the link, I understand now. Lua apparently took a similar spiritual path as Lisp in treating everything as an object. I'll have to remember that for the future.