Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are
spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the
password reset link.
Entire forum
➜ MUSHclient
➜ Lua
➜ Comparing a function to another
Comparing a function to another
|
It is now over 60 days since the last post. This thread is closed.
Refresh page
Pages: 1 2
Posted by
| Ksajal
(17 posts) Bio
|
Date
| Sun 25 Apr 2010 08:56 PM (UTC) |
Message
| I am trying to find a way to queue function to be executed at the prompt. So far, it looks like this:
function prompt:queue (f)
self ["queued"] [#self ["queued"]+1] = f
end --function
I use it like this:
prompt:queue (function () affs:add ("affliction")end)
I execute the function stored like this:
for k, func in ipairs (prompt ["queued"])do
func ()
end --for
I need to find a way to check if I have already queued a function. The only way that I can think of is supplying an extra argument to the queue method, an unique id for the function queued so I can compare it with other queued functions.
Any other ideas? This seems rather difficult, as I need to remember all the function ids that I have supplied each time I use the queue for the same function. | Top |
|
Posted by
| Twisol
USA (2,257 posts) Bio
|
Date
| Reply #1 on Sun 25 Apr 2010 09:24 PM (UTC) |
Message
| So basically you have a prompt "service", and functions can be "registered" to be executed on each prompt? And you also want to be able to tell if a function has been queued. Well, it's not too hard. :) Here's an example of what I might do (and have done before):
local queue = {}
function Register(func)
if queue[func] then -- check if it's in the queue
return nil, "Already queued"
end
-- position, callback
local entry = {#queue+1, func}
table.insert(queue, entry) -- adds to the end of the queue
queue[func] = entry -- marks that it's in the queue
end
function Unregister(func)
if not queue[func] then
return
end
local entry = queue[func] -- get the entry back
table.remove(queue, entry[1]) -- get the position and dequeue it
queue[func] = nil -- mark that it's not in the queue
end
function DoStuff()
local next_entry = table.remove(queue, 1) -- get from the front
local func = next_entry[2]
-- do stuff!
-- make sure to update the positions of the rest of the entries
for _,entry in ipairs(queue) do
-- shift the position marker
entry[1] = entry[1] - 1
end
end
It looks a little complex, but it's not. A queue like this would look something like this in memory...
queue = {
[1] = {1, function_foo}, -- entry 1
[2] = {2, function_bar}, -- entry 2
[3] = {3, function_baz}, -- entry 3
[function_foo] = {1, function_foo} -- same table as entry 1
[function_bar] = {2, function_bar} -- same table as entry 2
[function_baz] = {3, function_baz} -- same table as entry 3
}
Every time you take an item off the queue, you make sure that the rest of the entries' positions are updated, so you can basically just use table.remove(queue, queue[function_foo][1]) to remove the entry wherever it is now.
This is how you say you're using it now:
prompt:queue (function ()
affs:add ("affliction")
end)
You create the function and pass it to the queue in one go. But this gives you no way to refer to the function again, as you said. You'd want to do this instead:
function_foo = function ()
affs:add ("affliction")
end
prompt:queue(function_foo)
That lets you use some "prompt:unqueue" function later, just passing in the original function:
prompt:unqueue(function_foo)
Also, since you seem to be calling all of the queued functions at once, you probably don't need to update the entries' positions in the "DoStuff" function, because the queue will be empty afterwards anyways. It's just a general example of how to do a listener queue. :) |
'Soludra' on Achaea
Blog: http://jonathan.com/
GitHub: http://github.com/Twisol | Top |
|
Posted by
| Ksajal
(17 posts) Bio
|
Date
| Reply #2 on Sun 25 Apr 2010 09:45 PM (UTC) Amended on Sun 25 Apr 2010 10:09 PM (UTC) by Ksajal
|
Message
| I like the code.
But this means I will still have to create an unique name for each function I am registering.
And you forgot to delete the variable containing the function from the memory when you unregister the function.
EDIT:
I think I'm gonna use it like this, in case an unique name for each function is the only way:
function prompt:queue (f, id)
for k, v in ipairs (self ["queued"] do
if v==id then
return
end --if
end --for
self ["queued"] [#self ["queued']] = id
self ["ids"] [id] = f
end --function
I only need to use it in a for loop:
for k, unique_id in ipairs (prompt ["queued"]) do
self ["ids"] [unique_id] ()
end --for
I'm only storing the function in one place.
Thank you for the help! | Top |
|
Posted by
| Twisol
USA (2,257 posts) Bio
|
Date
| Reply #3 on Sun 25 Apr 2010 10:17 PM (UTC) |
Message
|
Ksajal said: But this means I will still have to create an unique name for each function I am registering.
If by "name" you mean variable, then yes. If you're giving away a function with no way to refer to it later, there's no reason to expect to be able to deregister it.
Ksajal said: And you forgot to delete the variable containing the function from the memory when you unregister the function.
I don't see what you mean, sorry. I table.remove and set to nil in Deregister.
Oh, I see. In DoStuff, for the one being used.
Your string ID works, certainly, and I'm glad you have a solution. Different functions will never clash, though, while anyone can pass in an already-used string ID. ;) |
'Soludra' on Achaea
Blog: http://jonathan.com/
GitHub: http://github.com/Twisol | Top |
|
Posted by
| Nick Gammon
Australia (23,120 posts) Bio
Forum Administrator |
Date
| Reply #4 on Sun 25 Apr 2010 10:37 PM (UTC) |
Message
|
Ksajal said:
But this means I will still have to create an unique name for each function I am registering.
What is the problem exactly? You can compare functions directly.
print (print == print) --> true
print (print == tostring) --> false
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|
Posted by
| Ksajal
(17 posts) Bio
|
Date
| Reply #5 on Sun 25 Apr 2010 10:50 PM (UTC) |
Message
|
Twisol said:
Your string ID works, certainly, and I'm glad you have a solution. Different functions will never clash, though, while anyone can pass in an already-used string ID. ;)
I'm usually calling prompt:queue from other functions, like this:
function affs:add_queue (name, val)
prompt:queue (function () affs:add (name, val)end, "affs_add_"..name)
end --function
Or if I need to do some very special stuff:
prompt:queue (function () some stuff end, "user_"..tostring (os.time ()))
They won't ever clash, me thinks.
Nick Gammon said:
What is the problem exactly? You can compare functions directly.
I've tried in my case, it doesn't work
one = function () Note ("") end
two = function () Note ("") end
Note (one == two)-->false
| Top |
|
Posted by
| Nick Gammon
Australia (23,120 posts) Bio
Forum Administrator |
Date
| Reply #6 on Sun 25 Apr 2010 11:00 PM (UTC) |
Message
|
Ksajal said:
Nick Gammon said:
What is the problem exactly? You can compare functions directly.
I've tried in my case, it doesn't work
one = function () Note ("") end
two = function () Note ("") end
Note (one == two)-->false
They are two functions that happen to do the same thing.
one = function () Note ("") end
two = one
Note (one == two) --> true
In your case, stop inlining the functions if you can. eg. instead of:
prompt:queue (function () affs:add ("affliction")end)
do:
-- define function once
function add_affliction ()
affs:add ("affliction")
end
-- queue it if required
if something then
prompt:queue (add_affliction)
end -- if
By only defining functions once, you now can just test if they are the same.
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|
Posted by
| Ksajal
(17 posts) Bio
|
Date
| Reply #7 on Sun 25 Apr 2010 11:16 PM (UTC) |
Message
| I think I understand what you mean, but I have hundreds of afflictions, hundreds of cures, hundreds of defenses, and so on, declaring a function for each is just too much.
I've tested my unique ids table solution, it works exactly as it should so far, I'm gonna stick with it. | Top |
|
Posted by
| Twisol
USA (2,257 posts) Bio
|
Date
| Reply #8 on Sun 25 Apr 2010 11:30 PM (UTC) Amended on Sun 25 Apr 2010 11:32 PM (UTC) by Twisol
|
Message
|
Ksajal said:I've tried in my case, it doesn't work
one = function () Note ("") end
two = function () Note ("") end
Note (one == two)-->false
Well, of course. They're two separate instances of a function. In other languages, you wouldn't expect for "new Object() == new Object()".
If you want a really messed up way to do it you can do this. Nick's going to kill me just for suggesting such an outlandish approach:
string.dump(function() end) == string.dump(function() end)
I can't test it right now - posting from my phone - but that should work for most simple cases. I can't guarantee it will if you start throwing in upvalues and modifed environments (setfenv), but it would be interesting to experiment with. |
'Soludra' on Achaea
Blog: http://jonathan.com/
GitHub: http://github.com/Twisol | Top |
|
Posted by
| Twisol
USA (2,257 posts) Bio
|
Date
| Reply #9 on Sun 25 Apr 2010 11:37 PM (UTC) |
Message
|
Ksajal said:
I think I understand what you mean, but I have hundreds of afflictions, hundreds of cures, hundreds of defenses, and so on, declaring a function for each is just too much.
I've tested my unique ids table solution, it works exactly as it should so far, I'm gonna stick with it.
You're not the first to write a curing system, and others have dealt with that problem already, more or less. Store the functions in your own tables, so they're not loose. Look at ACS2, a somewhat outdated system for Achaea, to see how they've done some of this. I don't recommend it for style or best practices, but it does work... |
'Soludra' on Achaea
Blog: http://jonathan.com/
GitHub: http://github.com/Twisol | Top |
|
Posted by
| Nick Gammon
Australia (23,120 posts) Bio
Forum Administrator |
Date
| Reply #10 on Mon 26 Apr 2010 01:33 AM (UTC) |
Message
|
Twisol said:
If you want a really messed up way to do it you can do this. Nick's going to kill me just for suggesting such an outlandish approach:
string.dump(function() end) == string.dump(function() end)
Apart from the aesthetic considerations, it just doesn't work:
s1 = string.dump (function () print "hi there" end)
s2 = string.dump (function () print "hi there" end)
print (s1 == s2) --> false
s1 = string.dump (function () end)
s2 = string.dump (function () end)
print (s1 == s2) --> false
Ksajal said:
I think I understand what you mean, but I have hundreds of afflictions, hundreds of cures, hundreds of defenses, and so on, declaring a function for each is just too much.
But you have hundreds of something, right? Because you are testing if one is equal to the other?
Generally when you seem to have hit a limitation of the language you need to revisit what you are doing. For example, if you are processing afflictions, and you want to queue up a cure, do a simple keyed lookup (or sequential lookup) of your queue of things you are planning to cure, to see if it is there already. But not by trying to compare functions per se. Stick to affliction names or cure names, or something more high level. |
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|
Posted by
| Ksajal
(17 posts) Bio
|
Date
| Reply #11 on Mon 26 Apr 2010 11:32 AM (UTC) Amended on Mon 26 Apr 2010 01:54 PM (UTC) by Ksajal
|
Message
|
Nick Gammon said:
Generally when you seem to have hit a limitation of the language you need to revisit what you are doing. For example, if you are processing afflictions, and you want to queue up a cure, do a simple keyed lookup (or sequential lookup) of your queue of things you are planning to cure, to see if it is there already. But not by trying to compare functions per se. Stick to affliction names or cure names, or something more high level.
This is what I am doing:
-I have a vector with all the afflictions, in the order I want to cure them, from the most dangerous to the least dangerous.
-I have a module affs which adds the affliction when I get the affliction message: affs:add (affliction) (the new affliction is stored in the the affs ["current"] table).
The line for an affliction can be an illusion (another player used a skill to make you see the line). And if it is an illusion, there is a possibility that you detect the illusion before the prompt. You detect it based on a % chance (there is a skill), or by checking against different things (like if the illusion was supposed to prone you, you can check for prone at the next prompt-the prompt shows without a doubt when you are prone-, and a few other means).
Sometimes you can be certain that an affliction line is not an illusion (for that I use affs:add), and other times you cannot (I use the queue for that).
I am not using the queue only for afflictions and I sometimes need the queue to execute a set of functions that are only used with that trigger, like this (just an example):
prompt:queue (function () EnableTrigger ("name") affs:add ("affliction") Note ("AFFLICTION")end)
And I need to be sure I am not queued the same function twice.
When I detect an illusion, I delete certain queued function (for that I use another table entry in the prompt queue), which I previously marked for deletion when I stored them into the queue (this is the previous function, marked for deletion):
prompt:queue (function () EnableTrigger ("name") affs:add ("affliction") Note ("AFFLICTION")end, "delete_it_if_illusion")
That is a little besides the point, the problem still remains, I need an unique name for the function for both comparing with previous entries in the queue, and for storing it for removal in case of an illusion. Problem which I solved by assigning an unique id for the function queued, like this ("affs_add_paralysis" is the id):
prompt:queue (function () affs:add ("paralysis") end, "affs_add_paralysis", "delete_it_if_illusion")
| Top |
|
Posted by
| David Haley
USA (3,881 posts) Bio
|
Date
| Reply #12 on Mon 26 Apr 2010 02:01 PM (UTC) |
Message
| All of your operations appear to be data-driven, meaning that you can define the operation based on the data it operates on, and you are not forced to define it as the functional operation itself.
It's unclear to me why (or even if) you need to queue whole functions and not the data they're operating on. For example, why this:
prompt:queue (function () affs:add ("paralysis") end, "affs_add_paralysis", "delete_it_if_illusion")
and not this:
prompt:queue ("paralysis", "delete_it_if_illusion")
(I'm not sure why you need the other parameter so I'm leaving it there for now.)
If you need to have a case where the note is printed, you could do:
prompt:queue ({aff="paralysis", print_note=false, enable_trigger=false}, "delete_it_if_illusion")
and
prompt:queue ({aff="paralysis", print_note=true, enable_trigger=true}, "delete_it_if_illusion")
etc.
Now you can easily test equality of two affliction chunks. After all you are trying to store data in your queues, not whole functional operations. |
David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone
http://david.the-haleys.org | Top |
|
Posted by
| Ksajal
(17 posts) Bio
|
Date
| Reply #13 on Mon 26 Apr 2010 02:18 PM (UTC) Amended on Mon 26 Apr 2010 02:36 PM (UTC) by Ksajal
|
Message
| Example of uses:
--the function
function prompt:queue (f, unique_id, if_I_remove_it_in_case_of_illusion)
for k, v in ipairs (self ["queued"] do
if v==id then
return
end --if
end --for
self ["queued"] [#self ["queued']] = id
self ["ids"] [id] = f
if if_I_remove_it_in_case_of_illusion then
self ["check"] [id] = true
end --if
end--function
--uses
prompt:queue (function () system:cured_asleep ()end, "cured_asleep", "remove_it_if_illusion")
--or
prompt:queue (function () system:cured_asleep flags:add_check ("recklessness") ()end, "some_unique_id", true)
--or
prompt:queue (function ()
system:cured ("slitthroat")
if bals:get ("elixir")==0.5 and not string.find (flags:get ("applying_salve") or "nil", "health") then fst:elixir () end
if bals:get ("purg")==0.5 then fst:purg ()end
if bals:get ("herb")==0.5 then fst:herb ()end
end, "another_unique_id", true)
--or
prompt:queue (function () defs:lostdef (defense) end, "defs_lostdef_defense", true)
I'm not only storing affliction names. | Top |
|
Posted by
| David Haley
USA (3,881 posts) Bio
|
Date
| Reply #14 on Mon 26 Apr 2010 02:36 PM (UTC) |
Message
| My suggestion works with more than just names, but it does seem like your functions are complex enough to not be just data. But then the question is why exactly can't you give these functions proper names or at least values that you can directly compare? |
David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone
http://david.the-haleys.org | Top |
|
The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).
To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.
49,412 views.
This is page 1, subject is 2 pages long: 1 2
It is now over 60 days since the last post. This thread is closed.
Refresh page
top