(Lua) Why is my producer-consumer Lua coroutines experiment not yielding the expected resault? - lua

I have created the following two coroutines a producer and a consumer in an attempt to learn/understand the coroutines.
function count01to10()
for i = 1, 10 do
coroutine.yield(i)
end
end
function printNumber(number)
while number ~= nil do
print("Counter: ", number)
coroutine.yield()
end
end
function main()
local number = 0
print("Creating coroutines")
local counter = coroutine.create(count01to10)
local printer = coroutine.create(printNumber)
print("Executing coroutines")
while (10 > number) do
isSuccessuful, number = coroutine.resume(counter)
print("counter: ", coroutine.status(counter))
coroutine.resume(printer, number)
print("printer: ", coroutine.status(printer))
end
print("Finished")
end
main()
The output is:
Creating coroutines
Executing coroutines
counter: suspended
Counter: 1
printer: suspended
counter: suspended
Counter: 1
printer: suspended
...
Counter: 1
printer: suspended
Finished
I am expecting the ouput to print out the numbers 1 to 10. Why is this not happening and is that a proper way to use coroutines?

A coroutine resumes at the same point where it yield (or just after it), not at the beginning.
Your code for printNumber does not change number, so the output you get is not surprising.
To fix this, use number=coroutine.yield() in printNumber.
The arguments passed to resume are returned by yield.

Related

Luau Couroutine stopping thread

I'm trying to make a OnChange Event Listener, I thought of using coroutines to poll the value in a loop, and see if it changed, so
function Changed(Value)
local StartingValue = Value
while true do
if StartingValue ~= Value then
print(Value)
StartingValue = Value
break
end
end
end
local n = 0
local co = coroutine.wrap(function()
Changed(n)
coroutine.yield()
end)
co()
n = (n + 1)
print("Script ended")
Is the Code I thought of for now, But it gets stuck in the Coroutine.. nothing happens after co()
What's going on?
The function Changed() never returns.
At the very beginning you're assigning the Value to the StartingValue, and the break is inside of the if statement that is only executed when the values are different. But they're not because of that initial assignment. So you have the infinite loop that has no means of ending.

I made a (very simple) programming language in Lua, but I can only execute one command

Hello people of stackoverflow, I've made a (very) simple programming language, it kind of looks like minecraft commands. Here is the code
function wait(cmdSleepGetNum)-- you can name this function
--what ever you want, it doesnt matter
local start = os.time()
repeat until os.time() > start + cmdSleepGetNum
end
local input = io.read()
if input == "/help" then
print"you'll find it out"
else if input == "/say" then
print"what do you want to say?"
local cmdSay = io.read()
print(cmdSay)
else if input == "/stop" then
os.exit()
else if input == "/sleep" then
print"how long?"
local cmdSleepGetNum = io.read()
wait(cmdSleepGetNum)
else if input == "/rand" then
local randNum = math.random()
print(randNum)
end
end
end
end
end
Now I know what you are thinking "what is the problem here?" the problem is that I can only execute one command and after that command is finished by the Lua interpreter, I cannot execute any other commands.
for example:
/rand
0.84018771715471 (the /rand command is executed and prints out a random number)
/rand
stdin:1: unexpected symbol near '/' (this happens when i try to execute another command)
If you replace else if with elseif, you won't need so many end's,
You can get rid of if's altogether, is you use a table,
As #Egor Skriptunoff said, you need to create a main loop to run many commands,
I suggest that you add an optional argument to /sleep and /say.
local function wait (cmdSleepGetNum) -- you can name this function
-- whatever you want, it doesn't matter.
local start = os.time ()
repeat until os.time () > start + cmdSleepGetNum
end
local commands = {
help = function ()
print "you'll find it out"
end,
say = function (arg)
if arg == '' then
print 'what do you want to say?'
arg = io.read ()
end
print (arg)
end,
stop = function ()
os.exit ()
end,
sleep = function (arg)
if arg == '' then
print 'how long?'
arg = tonumber (io.read ())
end
wait (tonumber (arg))
end,
rand = function ()
local randNum = math.random ()
print (randNum)
end,
[false] = function () -- fallback.
print 'Unknown command'
end
}
-- Main loop:
while true do
io.write '> '
local key, _, arg = io.read ():match '^%s*/(%S+)(%s*(.*))$' -- you can type /sleep 1, etc. in one line.
local command = key and key ~= '' and commands [key] or commands [false]
command (arg)
end

ComputerCraft tracking Lua "io" library exceptions

I am currently trying to debug a Minecraft-based turtle farming script with functions and API functions.
When I am executing I get an 'io' exception (which is understandable as I am trying to write to a log for each loop), but it doesn't tell me which script it is in or the line number. Are there any solutions apart from printing throughout program execution?
Exception I have been getting:
io:107: index expected, got nil
main turtle-farming script:
--[[main script for farming procedure]]
--load functions responsible for positioning/repositioning/farming
os.loadAPI("APIS/positioners");
os.loadAPI("APIS/farmers");
--action turtle start farming condition
assert(positioners.check_start_pos(), "Could not initialise the turtle for the farming procedure\nCould not identify it's starting location\n(are there birch planks directly above and beneath it?");
--check Netherrack can be found from start block, declare 'dir' also as local
local init_result, dir = positioners.find_next_neth()
assert(init_result, "Could not find the appropriate direction to travel in from\n the turtle starting position, is the farm set up correctly?");
local loop_vars = {["count"] = 0, ["blockId"] = nil, ["dir"] = nil, ["prevManvr"] = nil, ["action"] = ""};
--**input** assertion tests for required fuel level and seed requirements
assert(turtle.getFuelLevel() > 50, "Fuel level is below required level to start farming, - terminating");
--given 'result' == true can start farming
turtle.forward();
--open log file (overwrite) and save to io class handle
log = io.open("logs/proc_log", "w");
--start main farming process loop
while turtle.getFuelLevel() > 0 do
--reset action variable
loop_vars["action"] = "";
--sort log variables
loop_vars["count"] = loop_vars["count"] + 1;
--identify the block above (returns inv slot(1-4) it matches with or false if it doesn't)
loop_vars["blockId"] = positioners.id();
if blockId == 1 then
loop_vars["action"] = "Netherrack above ";
if loop_vars["dir"] == nil then
print("Netherrack found, planting");
farmers.plant();
--shift turtle forward such that it can evaluate a new block. Ensure that moves forward in case of obstruction
assert(turtle.forward(), "The turtle is impeded from moving forwards in farming route");
--log statement
loop_vars["action"] = loop_vars["action"].."plant and move forward";
else
--initiate change in direction procedure
assert(positioners.change_direction(loop_vars["dir"]), "Failed to complete turtle change direction procedure");
--reset dir
loop_vars["dir"] = nil;
loop_vars["action"] = loop_vars["action"].." actioned 'positioners.change_position(dir)'.";
end;
elseif blockId == 3 then
turtle.forward();
--log statement
loop_vars["action"] = "Proceeding past marble block";
elseif blockId == false then
print("Finding next Netherrack block.");
--retrace back to the most recent 'legitimate' block");
turtle.back();
--call function to reposition turtle for next farming line
result, loop_vars["dir"] = positioners.find_next_neth(loop_vars["prevMan"]);
assert(result, "A new netherrack block could not be found in any direction.");
--assign dir result to prevMan (information for next positioners.change_position() call
loop_vars["prevMan"] = loop_vars["dir"];
--log statement
loop_vars["action"] = "No 'legitimate' block above, 'positioners.find_next_neth() successful,\nassigning dir accordingly.";
else
--log statement
log.write("Unhandled exception in farm_proc main loop");
log.close();
error("positioners.find_next_neth() did not find an appropriate block above it.");
end;
print("farm_proc loop");
--write current turn to log file
log.write("Turn no: "..loop_vars["count"].."\n Id()== "..tostring(loop_vars["blockId"]).."\n Action: "..loop_vars["action"].."\n Previous maneuver: "..loop_vars["prevMan"].."\n\n");
end;
--updating log
log.write("Error - turtle ran out of fuel\n");
log.close();
error("Turtle ran out of fuel whilst planting - terminated");
'positioners' api (movement/detection based tasks):
function find_next_neth(prevMan)
--[[Find direction of the next overhead block of Netherrack from turtle's current pos,
Order;
N
E
W
S
Error (no direction found)]]
--define numbers corresponding to directions
local dirs = {"Back", "Left", "Right", "Forward"};
--create a loop counter to ensure does not exceed full checking loop
local counter = 4;
--loop through movement proc. until netherrack (slot 1) is found above it (if at all)
while true do
print("Looping, current loop counter is: ", counter);
local idAhead = id_ahead();
if counter == 4 then
--placeholder, script should never need to find forwards
elseif counter == 3 then
turtle.turnLeft();
elseif counter == 2 then
turtle.turnRight();
turtle.turnRight();
elseif counter == 1 then
turtle.turnRight();
--decremented counter has reached it's minimum
elseif counter < 1 then
return false, nil;
else
error("For find_next_neth iteration counter out of range");
end;
--decrement counter variable
counter = counter - 1;
--boolean condition to determine whether function is complete, incomplete or broken. 'prevMan' as a logical statement will return true if var has value, and false if not
local testPrev = (not prevMan == dir[(math.abs(counter - 4) + 1))] or not prevMan);
local validBlock = (idAhead == 1 or idAhead == 3)
if testPrev and validBlock then
--all tests are valid - break loop to return
break;
end;
--report success and return result
print("Nether found!\n");
return true, dirs[counter];
end;
end;
function change_direction(dir)
--intialise a base error string
local errMsg = "Could not action positioners.change_direction(dir, prevMan) ";
if dir == "East" then
assert(turtle.forward(), errMsg.."Obstruction to route");
--this should finalise a 180deg turn to the left
turtle.turnLeft()
elseif dir == "West" then
assert(turtle.forward(), errMsg.."Obstruction to route");
--180deg turn left
turtle.turnRight()
elseif dir == "North" then
--no action
elseif dir == "South" then
error("Return to start not yet implemented");
else
error("positioners.change_direction() parameter not as expected");
end;
return true;
end;
function id(slot)
--[[Determines if netherrack is currently above turtle.
Return false means the block
above does not match slots 1-4 of the
turtle's inv. Takes optional int
for specific block slot index
in turtle compare]]
--checking argument
if slot ~= nil then
--select slot given as argument
turtle.select(slot);
if turtle.compareUp() then
--don't return int because specific material requested
return true;
else
return false;
end;
end;
counter = 1
while counter < 5 do
turtle.select(counter);
--identify whether the current selected slot item matches the blovk overhead
if turtle.compareUp() then
--return block above's corresponding slot index
return counter
end;
counter = counter + 1;
end;
--return number indicating unidentfied block
return false;
end;
function id_ahead(slot)
if turtle.forward() then
--save value of id call for later return
local rtn = id(slot)
turtle.back();
return rtn
else
error("turtle could not move forwards in id_ahead(slot)");
end;
end;
function check_start_pos()
--set selected slot to birch planks (no currently selected method implemented so cannot revert this change internally)
turtle.select(2);
--save boolean variables for comparison of blocks above and below position
local up = turtle.compareUp();
local down = turtle.compareDown();
--test if above boolean values are true (turtle in designated start position)
if up and down then
return true;
else
print("Not in appropriate farming start position");
return false;
end;
end;
(The 'farmers' api simply checks current seeds and then plants a crop through farmers.plant())
I believe I have found the solution - the debug library, specifically debug.traceback() allows you to introspect the call stack more closely when catching an error
debug library index

Lock between Lua lanes

I am trying to use locks between 2 Lua lanes,but observed that both the lanes are entering lock_func simultaneously..Below is the snippet
Code Snippet
==================
require"lanes"
local linda = lanes.linda()
lock_func = lanes.genlock(linda,"M",1)
local function lock_func()
print("Lock Acquired")
while(true) do
end
end
local function readerThread()
print("readerThread acquiring lock")
lock_func()
end
local function writerThread()
print("writerThread acquiring lock")
lock_func()
end
Thread1= lanes.gen("*",{globals = _G},writerThread)
Thread2= lanes.gen("*",{globals = _G},readerThread)
T1 = Thread1()
T2 = Thread2()
T1:join()
T2:join()
From the ouput below we can see both the lanes have entered the lock_func function simultaneously
output
==================
writerThread acquiring lock
Lock Acquired
readerThread acquiring lock
Lock Acquired
Is there any problem with the implementation of the lock from the above code?
The implementation of locks in lua can be done as in the below code snippet. Only the print from writer or reader lane will be executed from the below code, as which ever lane acquiring the lock will go into an infinite loop(just to see locks are working as expected) & other lane will wait for the lock to get released.
require"lanes"
local linda = lanes.linda()
f = lanes.genlock(linda,"M",1)
local function readerThread()
require"lanes"
f(1)
print("readerThread acquiring lock")
while(true) do
end
f(-1)
end
local function writerThread()
require"lanes"
f(1)
print("writerThread acquiring lock")
while(true) do
end
f(-1)
end
Thread1= lanes.gen("*",{globals = _G},writerThread)
Thread2= lanes.gen("*",{globals = _G},readerThread)
T1 = Thread1()
T2 = Thread2()
T1:join()
T2:join()

Redis: Lua script to return every other nth element of a sorted set

I am trying to put together a lua script to be called from Redis (via an EVAL call) in order to return every other nth element of a sorted set (nth being the rank in the set, not the score).
There are very few online examples of Lua scripts that can be used to build upon, would anyone be able to point me in the right direction?
local function copyNOtherElements(table, interval, startpos)
local elemno = 1
local rettab = {}
for k, v in ipairs(table) do
if k >= startpos and (k - startpos) % interval == 0 then
rettab[elemno] = v
elemno = elemno + 1
end
end
return rettab
end
Sorry about formatting, typing on a phone. that's assuming the table is a 1 based array
For future readers, adding Redis into the previous answer, and a bit more efficient code to iterate the Nth elements:
local function zrange_pick(zset_key, step, start, stop)
-- The next four lines can be removed along with the start/stop params if not needed as in OP Q.
if start == nil than
start = 0
if end == nil than
end = -1
local set_by_score = redis.call('ZRANGE', zset_key, start, end)
local result = {}
for n = 1, #set_by_score, step do
table.insert(result, set_by_score[n])
end
return result
end

Resources