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

power monitoring program help

Started by asnarr204, 26 February 2015 - 08:31 PM
asnarr204 #1
Posted 26 February 2015 - 09:31 PM
I am playing with my own custom modpack and am really new to computercraft and lua. I have written a program to monitor my bases power, control an automated mining system, and a few other things. In this program, I am currently working on trying to get the power to be split up with commas for example 4925000000 would become 4,925,000,000. Here is the program: (don't worry about the rest of the program, just the addComma() function) http://pastebin.com/Z7GtthKi.

At first for the output of the addComma() funciton, I used numA, numB, etc instead of stringA, stringB, etc. The problem was, I would get 4,925,0,0 instead of 4,925,000,000. After replacing numA, etc with stringA, etc for the output however, I would get this error: "control:109: attempt to concatenate nil and string". Can someone help me with this, I'm not sure why this error would be occuring.
Edited on 26 February 2015 - 08:32 PM
GopherAtl #2
Posted 26 February 2015 - 09:45 PM
If I may propose a shorter alternative…


local function comma(num)
  local num=tostring(num)
  local split=#num%3
  local groups={}
  local lead,tail=num:sub(1,split), num:sub(split+1)
  if split>0 then
	groups[1]=lead
  end
  tail:gsub("(%d%d%d)",function(g) table.insert(groups,g) end)
  return table.concat(groups,",")
end

this will work whether you pass a number or a string, and will work for any arbitrarily large numbers, not just to the billions. Though lua's numbers can't actually get bigger than quintillions (1,000,000,000,000,000,000) and start losing precision on the lowest digits in the trillions.
Edited on 26 February 2015 - 08:51 PM
asnarr204 #3
Posted 26 February 2015 - 09:58 PM
Thanks! So, how does this work? I am really new, so I don't understand much of it I'd like to learn as much as I can.
Edited on 26 February 2015 - 09:06 PM
GopherAtl #4
Posted 26 February 2015 - 10:20 PM
Thanks, but how does this work? I am really new, so I don't understand much of it I'd like to learn as much as I can. Also how do I assign a variable to the result?

Sorry, I should've explained it as it went along. I'll step through it now…


local function comma(num)
  local num=tostring(num)
  local split=#num%3
we turn num into a string, just in case it wasn't already; if it was a string already, this won't have any effect at all.

Then we take the length of that string (#num) % 3, which divides it by 3 and gives us the remainder. So, ex, if it was a 10-digit string, 3 goes into 10 3 times, with a remainder of 1, so 10%3 would be 1. This will be the number of extra digits at the front, that aren't filling a comma group of 3 digits.

  local groups={}
  local lead,tail=num:sub(1,split), num:sub(split+1)
we make the ourselves a table, groups, which we'll be using in a minute.
Then we split the input num into two strings, stored in lead and tail. Lead will be the first "split" digits, split being our remainder from before, and is the numbers coming before the first comma. Tail is just the rest of the string, everything after the lead digits.

  if split>0 then
	groups[1]=lead
  end
if split wasn't 0, lead has those extra digits, and we stick them at the start of our table groups.

  tail:gsub("(%d%d%d)",function(g) table.insert(groups,g) end)
This is where it gets tricky. gsub is one of the functions using lua patterns. We're applying it to tail (hence, tail:gsub) with the pattern "(%d%d%d)"
the () just define what's called a capture group. %d matches a single digit, 0-9. So the pattern matches one capture group, defined as 3 digits. gsub will repeatedly apply this pattern to the input until it runs out, so if tail was "111222333" it would match "111", "222", and "333." If there were extra digits, like "1112223334" the extra at the end would not be matched at all; this is why we had to split those off first into the variable head.

For each match made to the pattern, gsub will call the function passed as it's second argument, sending the capture groups (what was inside ()s in the pattern) to that function. Our function is defined right there, "function(g) table.insert(groups,g) end" which just takes the matched value and adds it to the end of the groups table. So if input was "111222333", the function will be called 3 times, with "111", "222", and "333", and in the end the table groups will have {"111","222","333"}




  return table.concat(groups,",")
end
The last bit calls table.concat, which loops over the table as an array gluing the items together into a single string, with the second argument used as a separator. So, if groups was {"1","222","333"} it will output the string "1,222,333"
this will work whether you pass a number or a string, and will work for any arbitrarily large numbers, not just to the billions. Though lua's numbers can't actually get bigger than quintillions (1,000,000,000,000,000,000) and start losing precision on the lowest digits in the trillions.

More on lua patterns can be found here, they're incredibly powerful, but a little bit tricky to get your head wrapped around at first.

I didn't know which parts you were having trouble following, so I just explained all the parts, to be on the safe side.


:Edit: forgot your 2nd question, but I can't answer because I don't understand it. What do you mean "assign a variable to the result?"

you would just call the function and catch the returned value, ex


  local myNumber=1234567890
  local withCommas = comma(myNumber)
  print("before: "..myNumber..", after: "..withCommas)
Edited on 26 February 2015 - 09:24 PM
Lyqyd #5
Posted 27 February 2015 - 01:39 AM
Of course, there are always fun/silly string pattern abuse tricks like:


local function comma(num)
  num = tostring(num)
  num = num:reverse()
  num = string.match(num:gsub("[^%.]*$", function(str) return str:gsub("(%d%d%d)", "%1,") end), "(.-),?$")
  num = num:reverse()
  return num
end

This is essentially equivalent to Gopher's, but takes a shortcut/simplification by reversing the string first, and then finding groups of three contiguous numerals without a comma following them and adding a comma. When it has added all of the commas, it reverses the string again to make it come out right. This function does also take the extra step of ensuring that it does not add commas to any decimal digits, if present.