Scripting and Arrays

Posted by Gore on Tue 10 Feb 2004 12:19 AM — 14 posts, 58,490 views.

#0
Hello, I'm trying to write a script for my Mud.. Pretty much, I've an alias that calls my script, to add a wildcard to an Array called MilkVialList(). Here's what I've got so far..


Quote:

<Aliases>
  <alias
   script="milking_vialadd"
   match="mvialadd *"
   enabled="y"
   group="milking"
   sequence="100"
  >
  </alias>
</aliases>



That's my alias.. I'm unsure of where to start with the Subroutine..

here's what my code was in ZMud:

Quote:

#ALIAS milkvialadd {#ADDITEM milkingviallist %1;#ECHO {New vial added - %1}}



Pretty much.. not sure how much you guys know about ZMud, but in zmud there's not arrays.. there's strings, which you can have multiple items in the string. #additem would add %1 to the end of the string, but I'm not sure how to add items to new dimensions in an array, (sorry if I didn't use the correct lingo)

Any help on this would be heavily appreciated, thanks
USA #1
Pretty simple actually. There are however some VBscript complications. The first problem is that normal arrays are not permanent. If you need to store the list when you close mushclient, things get *way* more complicated. This brings us to the second problem, I really don't get why, but to make something like zMud uses you could use 'join' and 'split', however arrays generated using 'split' are different in some obscure way from those created using normal methods. You can't split a string into an existing array, even if the array is created using 'name()' or 'redim name', both of which are *supposed* to produce dynamic arrays that can change sizes. Worse, arrays produced by 'split' are a fixed size, so also can't be resized to add or delete new items.

Anyway the normal method would be:

'Placing this *outside* of any subs makes it global to the entire script file.
'  NOTE: varaibles used *in* triggers and aliases using 'send to script' are
'  also automatically global, so it is very important to use completely unique
'  names that can't conflict with values other aliases or your main script expect
'  to remain the same.
redim MilkVialList

sub milking_vialadd (name, output, wilds)
  ub = ubound(MilkVialList)
  redim MilkVialList(ub + 1)
  MilkVialList(ub + 1) = wilds(1)
end sub

The way to do it by using mushclient variables:

sub milking_vialadd (name, output, wilds)
  MVL = getvariable("MilkVialList")
  MVL = MVL + "," & wilds(1)
  setvariable "MilkVialList", MVL
end sub

The real problem is when you try to 'remove' an item. In the case of the array, you can delete the last item in the list easy by simply doing:

ub = ubound(MilkVialList)
if ub > 0 then
  redim MilkVialList(ub - 1)
end if

However, the get/set type requires either: 1) 'split'ing it into an array and then copying all but the last item into a new array, then 'join'ing that into the new string. or 2) using 'InStrRev' to find the last ',' and then a 'left' command to grab everything up to the character before that ','. If you have to remove an item from the middle of the list.... Then the problem grows exponentially, since there is no simple way to collapse an array and remove a single value from the middle of it. They should have made arrays in VBScript as collections instead, since then they would have supported the .sort, .add and .remove functions.

JavaScript however does treat them like collections and has the ability to use those functions directly with the array. However, Java is missing some other features that imho are used a lot more often. Either way you end up having to build your own scripts to make up for the stuff they didn't bother to include in the language. :(
Amended on Tue 10 Feb 2004 02:39 AM by Shadowfyr
#2
Yeah I declare most of my script variables outside of my subs (I.E. they are in the first part of my script file), at any rate.. Thanks for your help, seems like this will take a little work to do..
Australia Forum Administrator #3
I confronted this "remove from middle of list" problem recently. See this post:

http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=649

My solution (which I did fairly quickly) was to use "for each" and build up a text array manually (rather than using Join), omitting the entry which I didn't want.

It wasn't too bad.

Also see that post for general ideas about maintaining an array of things.
#4
Hey what's up, I've encountered another problem.. here's the alias, variables, subroutine and error message when I do mvenomadd curare

Quote:

<aliases>
  <alias
   script="milk_venomadd"
   match="mvenomadd *"
   enabled="y"
   group="milking"
   sequence="100"
  >
  </alias>
</aliases>

'Declaring Milking Variables
  Dim MilkViallist(), MilkVenomlist()

Sub Milk_Venomadd (a,b,wildcard)
  ub = ubound(MilkVenomlist)
  redim MilkVenomlist(ub+1)
  MilkVenomlist(ub+1) = wildcard(1)
  World.Note "Vial " & MilkVenomlist(1) & " has been added."
End Sub

Error number: -2146828279
Event:        Execution of line 55 column 3
Description:  Subscript out of range: 'ubound'
Called by:    Function/Sub: milk_venomadd called by alias
Reason: processing alias ""



Any help with this would be appreciated, I couldn't find any documentation on "ubound" So I'm not sure what the problem is. Thanks :D
USA #5
Ok.. I haven't a clue... VBScript handles dynamic arrays in a very stupid manner. Not only does it insist that they are variant() types, instead of arrays, but an empty one does not appear either empty or return a valid ubound...

lbound - lower array boundary. This is always 0, unless you use an obscure command to make it 1. (This effects all arrays they I think, so screws up more than it helped to fix..)

ubound - upper array boundary. This is the position of the last element in the array.

Arrays created as>

dim name() - No 'lbound', no 'ubound', shows a typename of 'variant()'. It will crash with an array out of bounds.

redim name() - Not allowed.

dim name(0) - Makes an array with '1' element. However, you cannot 'redim' it to make it bigger.

redim name(0) - Creates a dynamic array with '1' element. This will fix your problem, but requires a test to see if it is empty:

'Declaring Milking Variables
  redim MilkViallist(0), MilkVenomlist(0)

Sub Milk_Venomadd (a,b,wildcard)
  ub = ubound(MilkVenomlist)
  if ub = 0 and isempty(MilkVenomlist) then
    MilkVenomlist(0) = wildcard(1)
  else
    redim Preserve MilkVenomlist(ub+1)
    MilkVenomlist(ub+1) = wildcard(1)
  end if
  World.Note "Vial " & MilkVenomlist(ubound(MilkVenomlist)) & " has been added."
End Sub

I also changed the last line, so it now shows the last item added, even if the item is merely element (0) and you only replaced the contents. The way you had it originally would have failed with another 'array out of bounds' error, since you where printing the second element (1), even when that item didn't yet exits. Oops!

Basic rule. Dim is used for 'fixed size' arrays. While you can make dynamic ones, it is a lot easier to use 'redim' instead. It saves some confusion, since I haven't found any useful way to find out if one created with name() is actually empty or not. either way you have to test for that and the above version is imho easier to identify as using a true dynamic array. ;)

Oh, also a *VERY IMPORTANT* note. I added the 'Preserve' keyword to the redim in the sub. This is required to preserve the arrays contents. If you forget it, then VBScript will increase/decrease the size, but erase the contents... Forgot about that before. :(
Amended on Wed 11 Feb 2004 11:35 PM by Shadowfyr
#6
Finally got some time to fiddle with this script, and for some reason, when I have preserve after reDim, it won't preserve the contents of the array, I removed preserve and it works semi-ok from what I can see right now. Any ideas?
USA #7
Well.. That is really odd. Without the 'preserve' command it should add a new item, but delete everything already in the array. I have no idea why it wouldn't be working. At this point I don't have any more clue why it won't work.. :(
Greece #8
I don't think you can redimension an array if you make it static. The way i've always done it is:
Dim myArray()

Sub OnLoadThingy()
Redim myArray(0)
End Sub

Sub sbMySub()
Redim Preserve myArray(intWhatever)
End Sub

try it that way, it should work.
#9
Erm, I actually wanted to reply but... This is a test.
#10
Ahem... I honestly didn't touch anything, but this is Ked, not Gore. The forums somehow logged me in as someone else after I copy/pasted/followed a link from another post. Not sure if I should put it up here, but this looks like a forum bug.
Australia Forum Administrator #11
I am guessing you followed a link in the thread:

http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=4028

The link was:


http://www.gammon.com.au/forum/bbshowpost.php?token=05694299e7b2bc802919682df171fd1d&bbsubject_id=3754


What has happened here is that Gore posted a URL which included his session token, effectively making the forum think you were him.

This might appear in the URL if you do not have cookies enabled, so the forum has to pass down your login information as part of the URL itself.

These tokens (which are just random numbers) will expire when you log off, so that link would have been invalid (at least as far as letting you become someone else was concerned) after a while. It has expired now, so I am posting the full token in this message.

However what you need to do (Gore, are you reading this?) is when posting forum links, remove the token part. eg. change:

http://www.gammon.com.au/forum/bbshowpost.php?token=05694299e7b2bc802919682df171fd1d&bbsubject_id=3754

to:

http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=3754


Another approach is to start another browser window, and without logging onto the forum, find the message you want to get the URL for, which will not have a token as part of the URL, as that session is not a logged-in one.
Russia #12
Yes, that's exactly what I did
#13
ack, wicked sorry about that :P kinda funny though heh