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

Making functions run several times while one works

Started by CCJJSax, 27 August 2016 - 06:49 AM
CCJJSax #1
Posted 27 August 2016 - 08:49 AM
I've been messing around with coroutines and the parallel APIs and I can't figure out a good way to do this. I have a function that takes several seconds to get done, but I want to have it constantly updating the UI so I can have buttons and such on it.

this is my understanding of the APIs

Spoiler

parallel.waitForAll(func, func2) -- all the functions run once but it waits for all of them

parallel.waitForAny(func,func2) -- continues once one finishes
-- my testing so far has it doing a few lines of that function and stopping

H4X0RZ #2
Posted 27 August 2016 - 08:54 AM
You could wrap the function inside an anonymous function which loops and executes the actual function.


parallel.waitForAny(func, function() for i=1,5 do func2() end end)
Edited on 27 August 2016 - 06:54 AM
CCJJSax #3
Posted 27 August 2016 - 09:16 AM
You could wrap the function inside an anonymous function which loops and executes the actual function.


parallel.waitForAny(func, function() for i=1,5 do func2() end end)

with that is the long one func or func2?
H4X0RZ #4
Posted 27 August 2016 - 09:52 AM
You could wrap the function inside an anonymous function which loops and executes the actual function.


parallel.waitForAny(func, function() for i=1,5 do func2() end end)

with that is the long one func or func2?

func would be the long one. Also, you could/should replace the for loop with a while(true) do loop, so it really executes until the long function is done.
CCJJSax #5
Posted 27 August 2016 - 06:23 PM
–Snip

func would be the long one. Also, you could/should replace the for loop with a while(true) do loop, so it really executes until the long function is done.

I'll try it again. I thought that was the answer so I tested it out but it seemed to move forward then run the function2 while loop and waited for the while true do to get done, which is problematic
CCJJSax #6
Posted 27 August 2016 - 06:54 PM
I tried that and the turtle just acts really weird. It's not running through the full func. It seems like it randomly picks if it's going to go up or down instead of the full thing.
valithor #7
Posted 27 August 2016 - 08:25 PM
I'll try it again. I thought that was the answer so I tested it out but it seemed to move forward then run the function2 while loop and waited for the while true do to get done, which is problematic

It doesn't actually run both functions at the same time. Anytime one of the two functions yield it will go on to the next one that you pass to parallel.waitForAny. So… if your second function never yields the first one will never get to run again.

Anytime you are using coroutines/parallel api make sure all of the functions you are using actually have coroutine.yield, os.pullEvent, or os.pullEventRaw inside of it somewhere.
CCJJSax #8
Posted 28 August 2016 - 03:10 AM
It doesn't actually run both functions at the same time. Anytime one of the two functions yield it will go on to the next one that you pass to parallel.waitForAny. So… if your second function never yields the first one will never get to run again.

Anytime you are using coroutines/parallel api make sure all of the functions you are using actually have coroutine.yield, os.pullEvent, or os.pullEventRaw inside of it somewhere.

Can that be put just anywhere in eacharge function? My longer one goes through a lot of steps. Do I have to put the yield after every step or is the end ok?
valithor #9
Posted 28 August 2016 - 04:37 AM
It doesn't actually run both functions at the same time. Anytime one of the two functions yield it will go on to the next one that you pass to parallel.waitForAny. So… if your second function never yields the first one will never get to run again.

Anytime you are using coroutines/parallel api make sure all of the functions you are using actually have coroutine.yield, os.pullEvent, or os.pullEventRaw inside of it somewhere.

Can that be put just anywhere in eacharge function? My longer one goes through a lot of steps. Do I have to put the yield after every step or is the end ok?

I would need to see the function to give more specific advice, but after each individual step would be a little overkill. You would definitely want at least one yield/sleep in the while loop of the second function. If the first one doesn't have any yields in it, then you could space them out (assuming you are doing a lot of looping in that function), like every thousand loops or something.
Edited on 28 August 2016 - 02:50 AM
CCJJSax #10
Posted 28 August 2016 - 08:06 AM
– snip –

I would need to see the function to give more specific advice, but after each individual step would be a little overkill. You would definitely want at least one yield/sleep in the while loop of the second function. If the first one doesn't have any yields in it, then you could space them out (assuming you are doing a lot of looping in that function), like every thousand loops or something.

Makes sense. I will try what you said as well. Just so you can see my test program, I'll post it.
SpoilerI can see many mistakes in this already given your advice. too many yields for one.


local str = 1
lm = false

term.clear()
term.setCursorPos(1, 1)

function testLoop()
 term.setCursorPos(1,1)
 term.write(math.random(1, 50))
end

function loopMe(n)
 lm = true
 term.setCursorPos(1,2)
 turtle.forward()
 turtle.up()
 turtle.down()
 turtle.down()
 turtle.turnLeft()
 turtle.turnRight()
 turtle.turnRight()
 turtle.turnLeft()
 turtle.up()
 term.setCursorPos(1,2)
 if i == n then lm = true end
 str = str + 1 
end

function writer()
 print(str)
end

-- while str < 3 do  -- this was a previous attempt.  it works the best so far but still not well
--  parallel.waitForAny(function() loopMe(3) term.setCursorPos(1, 4) return end, function() for i = 1, 10 do testLoop() writer() sleep(0.1) end end)
--  sleep(.1)
-- end

print("done")

while str < 2 do
 local co1 = coroutine.create(loopMe)
 coroutine.resume(co1, loopMe)
 while lm == false do
  local co2 =  coroutine.wrap(writer)
  local co3 =  coroutine.wrap(testLoop)
  coroutine.resume(co2, writer)
  coroutine.yield(co2)
  coroutine.resume(co3, testLoop)
  coroutine.yield(co3)
  term.setCursorPos(1,5)
  print(coroutine.status(co1), coroutine.status(co2), coroutine.status(co3))
  sleep(.1)
 end
end

valithor #11
Posted 28 August 2016 - 08:28 AM
-snip
There is a handful of problems with the test code posted, mostly having to do with the use of coroutines.
coroutine.create(func) – This function will return what I am going to call a thread. I will touch on threads in a second.
coroutine.resume(thread, arg1, arg2, etc…) – This function will resume a thread. It will run the passed thread until it reaches a coroutine.yield (os.pullEvent and os.pullEventRaw both call coroutine.yield). The arguments passed after the thread in coroutine.resume will be what will be received by coroutine.yield.
coroutine.yield(filter) – This will cause the thread to yield allowing you to come back to it later without the requested information in the filter. The filter is returned by the coroutine.resume that resumed the thread.
example for create, resume, and yield:

function hello()
print("hi")
coroutine.yield("filter example")
print("hello")
coroutine.yield("hello")
print("another message")
end
local co = coroutine.create(hello)
local filter = coroutine.resume(co) --# the first time you resume a coroutine it starts from the very beginning, it doesn't need args.
print(filter) --# will print "filter example"
filter = coroutine.resume(co,"filter") --# this is resuming at the point of the first yield
print(filter) --# will print("hello")
coroutine.resume(co,"hello")
print(coroutine.status(co)) --# will print dead because it is at the end of the function
For what you are wanting though, it would be easier to just use parallel api, as you would need to make a full coroutine manager to do it.
Try something like this as a test:

function testLoop()
term.setCursorPos(1,1)
term.write(math.random(1, 50))
end
function loopMe(n)
lm = true
term.setCursorPos(1,2)
turtle.forward()
turtle.up()
turtle.down()
turtle.down()
turtle.turnLeft()
turtle.turnRight()
turtle.turnRight()
turtle.turnLeft()
turtle.up()
term.setCursorPos(1,2)
if i == n then lm = true end
str = str + 1
end
parallel.waitForAny(function() loopMe(3) end,function() while true do testLoop() sleep(.05) end)
I haven't tested that but I believe it should work for what you are wanting.
Edited on 28 August 2016 - 06:34 AM
CCJJSax #12
Posted 28 August 2016 - 08:44 AM
That code has some unexpected symbol on the parallel line… I'm not sure where though. as far as I can tell it looks good.

edit: it just needed another end at the end

edit 2: That actually works great. so it seems like H4x0rz 's code wasn't working just because of the sleep? Such a small difference.
Edited on 28 August 2016 - 06:48 AM