|
meddlesome Wanderer
Joined: 24 Aug 2012 Posts: 70
|
Posted: Fri Aug 24, 2012 2:06 am
paining over stringlist loop (SOLVED) |
Code: |
#VAR Qcounter %numitems(@NextUp)
#LOO 1,@Qcounter
{
#IF (%{i} <= %numitems(@NextUp))
{
#EXEC {%item(@NextUp, %{i})};#DELNITEM NextUp %{i}
}
}
|
I know this is all about my logic here, but I just don't know a way around it. I use this to create a spell casting queue. When I fire this under my alias, it will show that there are 11 items in the variable, but it will only fire 5 times. I am sure it is because I am taking the top item off the stack which is in turn confusing the loop counting algorithm. The only issue is that I have other #TRIGGER's that can and will fire (for example if I fumble the casting of a spell) that adds the items to the list while I am running this loop.
What can I do to correct this? |
|
Last edited by meddlesome on Fri Aug 24, 2012 5:20 am; edited 1 time in total |
|
|
|
MattLofton GURU
Joined: 23 Dec 2000 Posts: 4834 Location: USA
|
Posted: Fri Aug 24, 2012 2:42 am |
Use local variables to do all your data-working stuff, then assign the result back to the global variable. Not only are they faster, they help isolate the data you intend to work with so that other settings can't modify it.
|
|
_________________ EDIT: I didn't like my old signature |
|
|
|
meddlesome Wanderer
Joined: 24 Aug 2012 Posts: 70
|
Posted: Fri Aug 24, 2012 2:53 am |
To further explain what is happening...
Say I have 4 items in the list, 1|2|3|4.
When the loop is at index 1, then it point to value 1 in my list. Then I remove it, but the Index is still at 1. So the index get incremented to 2, but now points at value 3 instead of value 2. Then I remove it, and the value 4 moves up and the loop index is incremented then comes back and ends the loop based on the conditions of the Boolean because the number of items in the list is now half the size of what it was because the list has changed during this.
I have to run the loop another 2x to actually get all the values from the list to execute. |
|
|
|
meddlesome Wanderer
Joined: 24 Aug 2012 Posts: 70
|
Posted: Fri Aug 24, 2012 2:58 am |
So use two list. #FORALL with the first list, then delete everything from that list. Then move the dummy list into the first list and repeat the process. Sounds doable. Will give that a try. Or are you speaking about something else.
Thanks. |
|
|
|
MattLofton GURU
Joined: 23 Dec 2000 Posts: 4834 Location: USA
|
Posted: Fri Aug 24, 2012 3:03 am |
Or just one #WHILE/#UNTIL. No need to worry about where in the list you are, and you'll always be working on the first element...
|
|
_________________ EDIT: I didn't like my old signature |
|
|
|
meddlesome Wanderer
Joined: 24 Aug 2012 Posts: 70
|
Posted: Fri Aug 24, 2012 3:16 am |
Yeah, I just got that...Thanks, sometimes I just need a sounding board. Here is my untested solution, but I think it will work.
Code: |
#UNTIL (@NextUp ="") {#EXEC {%item(@NextUp, 1)};#DELNITEM NextUp 1} |
Thanks All |
|
|
|
mikeC130 Apprentice
Joined: 03 Jul 2006 Posts: 110
|
Posted: Fri Aug 24, 2012 3:27 am |
If you want to ignore stuff added to your queue while you are executing this, and you want to keep the original variable, then you can copy it over to a local variable. If you want to allow stuff to be added to the queue, then you can use the global variable, although you'll need to copy the contents as you go to another variable if you want to end up with a variable that shows everything that was executed, whether it was there originally or added later.
Get rid of the Loop, and use the #WHILE command, like so:
Code: |
#VAR Commandlist @NextUp
#VAR Executedcommands
#WHILE (%numitems(@NextUp)>0)
{
#EXEC {%item(@NextUp,1)}
#VAR Executedcommands %additem(%item(@NextUp,1))
#DELNITEM NextUp 1
}
|
This should scroll through all items in @NextUp, executing each one in turn, adding it to the list variable Executedcommands, and then deleting it from @NextUp. When you delete the last item in the stringlist, your while command will terminate. Commandlist holds the commands as they existed at the start of this code. Executedcommands will contain the list of commands as executed, which could be different from the list you started with if your other triggers added items to it.
ETA - Darn, I type WAY too slow...
Mike |
|
|
|
meddlesome Wanderer
Joined: 24 Aug 2012 Posts: 70
|
Posted: Fri Aug 24, 2012 4:35 am |
Close, but I see one error with this. A stringlist always has 1 item in it. It could be blank, or a string of text, but it is still always 1 item. This is shown by creating a new Stringlist varable, not assigning it a value and using #SHOW %numitems(@listVar).
Your solution would create an infinite loop. But if it didn't I could see it working that is if it didn't leave that last item from the list not getting executed because the condition of the while would be met. That is if you changed it to #WHILE (%numitems(@NextUp)<1).
The above turned out to work for me. I need to add a bit more logic to the outline of the procedure so that miscast spell are recast during this. I guess I could just trigger the miscast so that the queue is launched again and that would solve that. Other than that, it is a solution.
The idea was to create a list
Use one item on the list until there were no more items on the list.
The Key was determining when that happened. It turned out to be when @NextUp ="".
I had to change my logic from numerically processing the list in order, to treating it like a stack. |
|
|
|
Rahab Wizard
Joined: 22 Mar 2007 Posts: 2320
|
Posted: Fri Aug 24, 2012 12:43 pm |
You are incorrect--%numitems(@listVar) will return 0 if there are no items in the list. You can show this by doing:
Code: |
#VAR listVar {}; #SHOW %numitems(@listVar) |
You might want to check out pop(), which is a convenient way to return the first item in a list and delete it.
If you have other triggers which could be modifying @NextUp at the same time as this trigger (and it sounds like you have multiple such triggers?), then you really need to check out the #SECTION command. You have potential thread conflicts. Even MikeC's code has potential thread conflicts. Every trigger which might be modifying the same variables at the same time will need #SECTION commands to isolate the critical pieces of code. If you properly use #SECTION commands, you will not need MikeC's suggestion of multiple variables (which is not even preventing the thread conflict that you are concerned about).
Here is my suggested code:
Code: |
#LOCAL $next
#WHILE (TRUE)
{
#SECTION nextup
{
#IF (! %numitems(@NextUp)) {#BREAK}
$next = %pop(@NextUP)
}
#IF ($next) {#EXEC $next}
}
|
And in addition, you would need to surround any code in other triggers which might be testing or modifying @NextUp _at the same time_ with a #SECTION nextup {} command.
[edited]slightly modified the code to allow for values in @NextUp which evaluate to FALSE. |
|
|
|
meddlesome Wanderer
Joined: 24 Aug 2012 Posts: 70
|
Posted: Fri Aug 24, 2012 5:43 pm |
You are correct. It was late/early and I was confusing that with Stringlist starting with 1 not 0 as apposed to arrays. Thank you for pointing that out.
One of the main reasons I don't post too often. I get real smart, then really really stupid.
Thanks for the heads up on the #SECTION, I will use this. |
|
|
|
Rahab Wizard
Joined: 22 Mar 2007 Posts: 2320
|
Posted: Sat Aug 25, 2012 1:03 am |
A tip about using #SECTION: The reason it works is because it guarantees that only one Section with a particular name will run at a time. While the Section named "nextup" in this trigger is running, any other thread which tries to run a Section with the same name will pause until this Section is finished. For that reason, it is a good idea to include only as much code as necessary in #SECTION--enough to protect the commands which might conflict with other threads, but no more. That way you minimize the pauses introduced in other threads.
This may mean that the most effective #SECTION requires restructuring your code. That's what I did in my example. If I had left the %numitems() in the #WHILE test, I would have needed to include the entire #WHILE loop inside the #SECTION command; other threads would not be able to run "nextup" sections until this loop had emptied @NextUp. By restructuring it, I was able to include only the %numitems() and the %pop() functions inside the #SECTION. That way, the other threads can continue while this thread is running the #EXEC. |
|
|
|
|
|