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

Parallel Api Woes

Started by campicus, 15 October 2013 - 11:59 PM
campicus #1
Posted 16 October 2013 - 01:59 AM
I am trying to get my program to run a redstone pulsing loop with the ability to quit at any time. I cannot for the life of me get this to work, help please!

I don't get an error or anything, the program just hangs.


local quit = false

function pulse()
  --"pulsing program here"
end

function reset()
  local event, key = os.pullEvent("char")
  if key == "q" then
    quit = true
  end
end

while true do
  parallel.waitForAny(pulse(), reset())
  if quit then
    fs.delete("pulserLog")
    os.reboot()
  end
end
immibis #2
Posted 16 October 2013 - 02:19 AM
This:

  parallel.waitForAny(pulse(), reset())
should be this:

  parallel.waitForAny(pulse, reset)
campicus #3
Posted 16 October 2013 - 08:58 AM
You sir, are, as always, amazing!! Bask in the warmth of my commas!
theoriginalbit #4
Posted 16 October 2013 - 11:21 AM
ok so as an expansion on what immibis has said, and an explanation as to why your code didn't work.

The parallel API requires function pointers so that it can turn them into coroutines and run them.

What you're doing is you're calling the functions before passing them to the parallel api, so pulse is being called and your program immediately gets caught in pulse's infinite loop, if pulse was to actually exit, the program would then continue onto reset, which would run in it's loop, if reset was to exit, the program would continue and then call the parallel function with whatever values both of the previous functions (pulse and reset) returned.

What is a function pointer?
SpoilerA function is stored on a piece of memory on our computers, the variable/the function name that is tied to our functions contain a pointer to their address in memory. Try it, run the following code and you will see the memory address of the function. It should output with something like function: 3f9873ad where the numbers and letters will be different each time you run it.

function test()
end
print(test)

So if we would have the following

local function reset()
  --# code here
end
the function pointer is stored in the variable reset, that is why doing this

local reset
reset = function()
  --# code here
end
is the same as defining in the previous way

Off-topic Side-note: What is that second function definition done in that way?!
SpoilerLocal function declaration methods
There are 3 methods to define a local function

1.

local function reset()
  --# code
end

2.

local reset = function()
  --# code
end

3.

local reset
reset = function()
  --# code
end

When looking at the above 3, you may think that #2 and #3 are no different, but they are, in fact very different! Believe it or not #1 and #3 are actually the same, #1 is just syntactical sugar for #3. In a normal situation you wouldn't find a need or difference between the 3, but there is when you're wanting to make a function that uses some recursion.

Effects on recursive local functions
In #2 recursion would not be possible. In example 1 and 3, the variable the function is stored in is created before the function is stored in it, this allows the function to be able to recursively call itself as there is already the function defined in the local space, whereas with #2 the variable is not yet defined in the local space when the function is compiled, meaning that when the function runs it cannot see itself in the local space and thus tries to reference 'reset' in the global space (which could make for some bad/unexpected bugs if there is a function called reset, or an error if there isn't). These effects are even greatly different when cyclic-recursion comes into play.

Effects on cyclic-recursive local functions
When dealing with cyclic-recursion you can only define them with the third method, take this example

local function w()
  print("world")
  h()
end

local function h()
  write("hello ")
  w()
end

h()
when the function w is compiled, there is no variable h in the local scope, so when attempting to call it, it will error out (if there is not variable h in the global scope) as such when dealing with cyclic-recursion we must use forward declarations of our functions

local w, h --# forward local declaration of w and h

function w() --# or w = function()
  print("world")
  h()
end

function h()
  write("hello ")
  w()
end

h()
the above code will now work correctly due to the fact that both w and h are now defined in the local space. note that you no longer need to put local on the function declarations due to the fact that the name is already in the local space.

Final Note
Obviously the above problems do not apply to non-localised functions as they're in the global space. Both methods of declaring a global function are identical and do not have an effect on how they function, it is merely syntactical sugar and personal preference as to which declaration method you use (excluding when declaring in a table).

Good reference;
PIL: Non-Global Functions
Edited on 12 February 2014 - 04:54 AM
TheOddByte #5
Posted 16 October 2013 - 12:59 PM
ok so as an expansion on what immibis has said, and an explanation as to why your code didn't work.

The parallel API requires function pointers so that it can turn them into coroutines and run them.

What you're doing is you're calling the functions before passing them to the parallel api, so pulse is being called and your program immediately gets caught in pulse's infinite loop, if pulse was to actually exit the program would then continue onto reset, which would run in it's loop, if reset was to exit the program would continue and then call the parallel function with whatever values both of the previous functions returned.

What is a function pointer?
SpoilerA function is stored on a piece of memory on our computers, the variable/the function name that is tied to our functions contain a pointer to their address in memory. Try it, run the following code and you will see the memory address of the function. It should output with something like function: 3f9873ad where the numbers and letters will be different each time you run it.

function test()
end
print(test)

So if we would have the following

local function reset()
  --# code here
end
the function pointer is stored in the variable reset, that is why doing this

local reset
reset = function()
  --# code here
end
is the same as defining in the previous way

Off-topic Side-note: What is that second function definition done in that way?!
SpoilerLocal function declaration methods
There are 3 methods to define a local function

1.

local function reset()
  --# code
end

2.

local reset = function()
  --# code
end

3.

local reset
reset = function()
  --# code
end

When looking at the above 3, you may think that #2 and #3 are no different, but they are, in fact very different! Believe it or not #1 and #3 are actually the same, #1 is just syntactical sugar for #3. In a normal situation you wouldn't find a need or difference between the 3, but there is when you're wanting to make a function that uses some recursion.

Effects on recursive local functions
In #2 recursion would not be possible. In example 1 and 3, the variable the function is stored in is created before the function is stored in it, this allows the function to be able to recursively call itself as there is already the function defined in the local space, whereas with #2 the variable is not yet defined in the local space when the function is compiled, meaning that when the function runs it cannot see itself in the local space and thus tries to reference 'reset' in the global space (which could make for some bad/unexpected bugs if there is a function called reset, or an error if there isn't). These effects are even greatly different when cyclic-recursion comes into play.

Effects on cyclic-recursive local functions
When dealing with cyclic-recursion you can only define them with the third method, take this example

local function w()
  print("world")
  h()
end

local function h()
  write("hello ")
  w()
end

h()
when the function w is compiled, there is no variable in the local scope, so when attempting to call it, it will error out (if there is not variable h in the global scope) as such when dealing with cyclic-recursion we must use forward declarations of our functions

local w, h --# forward local declaration of w and h

function w() --# or w = function()
  print("world")
  h()
end

function h()
  write("hello ")
  w()
end

h()

Good reference;
PIL: Non-Global Functions
Even though immibis and you have already helped him I'm just wondering how you can turn something simple into something advanced and amazing? :P/>

@campicus
You can also do this to exit an loop so you know

local quit = false

while not quit do
	-- Code
end
It may not be useful for your code, But sometimes it can be proven useful

Also, If you are using 'os.reboot' to just get out of the program I would suggest you used 'error' since 'os.reboot' can be bad if the user has an os installed or something

Example function

local function exitProgram(text)
	term.clear()
	term.setCursorPos(1,1)
	print(text.."\n)
    error()
end
This would ofcourse exit the program then clear the screen and print some text when it's been exited
campicus #6
Posted 16 October 2013 - 07:23 PM
Thanks for all the help guys! I love this community, it is a rare thing to see so many people so will to put their time and effort into helping others, for nothing in return :)/>