Snippet Dictionary (in zinc)

weaaddar

New Member
Reaction score
6
Updated::
Dictionary is an associative array, like Vexorian's table. Unlike Table however, Dictionary keeps track of elements stored in it and provides a means to iterate through the collection. This is basically a linked list and a hashtable combined. There is a readonly array struct that will inline, and doesn't allow mutation.
The macro is instanced currently for 3 types
Dict_integer
Dict_string
Dict_handle // kind of useless.
Thankfully it is a macro so you can instance your own. just at the bottom of Dictionary library instance to your hearts content. The macro takes 3 parameters
//! runtextmacro DictTemplate (T,HashFunc,DefaultVal)
where T is the type, HashFunc is a function to generate a hash, and defaultVal is the default for the type.

Another included type is Set. Set is like a unitgroup but for any type T. Set provides a readonly node as well, and a fast contain operator. It too is all constant time operations except for its clear op.
More text in the script header.
JASS:
//! zinc
// Thanks goes to Jesus4lyf for his clever optimizations and showing
// the light of typecast abuses.
// Also thanks to Vexorian for Zinc and JassHelper. Zinc makes writing Jass
// enjoyable and quick.
//=============================================================================
//                              Dict_T
//=============================================================================
    // Dictionary is an associative array that keeps track of elements
    // stored in the array. All its operations are o(1) unless stated otherwise.
    // Hashtable calls # are displayed in [] next to the method description.
    // Property Index::
    // ----------------
    // Count->integer  
    //      returns number of elements in this dictionary.
    // 
    // Value->integer
    //      returns the last value retrieved by the TryGetValue method.
    //
    // operator[T key]->integer [1 ht load]
    //      reutrns the Value associated with this key. 0 if it fails.
    // 
    // operator[T key]=integer [max 1 ht loads,1 ht save]
    //      associate key with value in the dictionary.
    
    // Method Index::
    // --------------
    // Contains(T key)->boolean [1 ht load]
    //      returns if this key is in the dictionary.
    //
    // TryGetValue(T key)->boolean [1 ht load]
    //      returns if this key is in the dictionary, and sets Value to the
    //  the value associated with the key.
    //
    // Add(T key,integer value)->boolean [max 1 ht load, 1 ht save]
    //      tries to add key,value to the dictionary, if and only if the key
    // isn't already part of the collection. Returns true if key was added.
    //
    // Remove(T key)->integer [max 1 ht remove, 1 ht load]
    //      tries to remove this key from the collection. Returns the value
    // that was associated if it succeeded, 0 if it failed. 
    //
    // GetReadOnlyNode()->RO_DictNode_T
    //      returns a new read only dict Node see further documentation For
    // the enumeration paradigm (it kind of sucks!)
    // 
    // Clear() (O(n) operation)  [1 ht child hashtable flush]
    //      Clears the collection of all elements in it.
//=============================================================================
//                                  Set_T
//=============================================================================
    // Set is a uniqueness enforcing collection structure. Only one
    // instance of any object can be stored in the collection at a time.
    // It is like a unitgroup but for any type T that you can instance the macro for.
    // Internally it is a thin-wrapper for a dictionary, and thus should inline
    // around the calls to dictionary.
    
    // Property Index::
    // ----------------
    // Count->integer  
    //      returns number of elements in this dictionary.
    // 
    
    // Method Index::
    // --------------
    // Contains(T object)->boolean [1 ht load]
    //      returns if this object is in the dictionary.
    //
    // Add(T object)->boolean [max 1 ht load, 1 ht save]
    //      tries to add object to the dictionary, if and only if the object
    // isn't already part of the set. Returns if the object was added.
    //
    // Remove(T object)->boolean [max 1 ht remove, 1 ht load]
    //      tries to remove object from the collection. Returns if the
    // the object was removed.
    //
    // GetReadOnlyNode()->RO_SetNode_T
    //      returns a new read only set Node see further documentation For
    // the enumeration paradigm (it kind of sucks!)
    // 
    // Clear() (O(n) operation)  [1 ht child hashtable flush]
    //      Clears the collection of all elements in it.
//=============================================================================
//                              RO_Set/DictNode_T
//=============================================================================
    // Read only set/dict node is a safe way to enumerate through a collection,
    // it is high performance as it will inline, and doesn't use any hashtable
    // calls. They are also array structs meaning that no create or destroy
    // method should ever be called (may also be an error!)
    
    //Unfortunately, the enumeration paradigm is pretty awful
// Zinc -- this is actually pretty clean to be honest this is almost
// as good as it gets!
/* here for copy and paste usage::
    RO_DictNode_integer node;
    integer key;
    integer value;
    for(node = dict.GetReadOnlyNode(); node != dict; node = node.Next)
    {
        key = node.Key;
        value = node.value;
        // code goes here
    }
*/
// Vjass -- this ofcourse is much uglier
/*
    local RO_DictNode_integer node
    local integer key
    local integer value
    loop
        exitwhen node == dict
        set key = node.Key
        set value = node.Value
        // code goes here
        set node = node.Next
    endloop
*/
    // RO_DictNode_T Property Index::
    // ==============================
    // Key-> T
    //      returns the key at this point in the enumeration.
    // Value -> integer
    //      returns the value at this point in the enumeration.
    
    // RO_SetNode_T Property Index::
    // =============================
    // Object -> T
    //      returns the object stored at this point in the enumeration.
    
    // Shared Properties::
    // ====================
    // Next -> thistype
    //      returns the next node in a safe readonly wrapper.
    
//=============================================================================


library Dictionary
{
    hashtable Table = InitHashtable();
    //! textmacro DictTemplate takes T,HashFunc,DefaultVal
    // Node is private, and should stay that way.
    // A consumer should never have to know about this class.
    
    struct Node_$T$
    {
        thistype Next;
        thistype Prev;
        $T$ Key;
        integer Value;
        static method create($T$ key,integer value)->thistype
        {
            thistype n = thistype.allocate();
            n.Key = key;
            n.Value = value;
            return n;
        }
    }
    
    public struct RO_SetNode_$T$[]
    {
        method operator Next()->thistype
        {
            return Node_$T$(this).Next;
        }
        method operator Object()->$T$
        {
            return Node_$T$(this).Key;
        }
    }
    
    public struct RO_DictNode_$T$[]
    {
        method operator Next()->thistype
        {
            return Node_$T$(this).Next;
        }
        method operator Key()->$T$
        {
            return Node_$T$(this).Key;
        }
        method operator Value()->integer
        {
            return Node_$T$(this).Value;
        }
    }

    
    public struct Dict_$T$
    {
        private integer m_count;
        
        private method Fetch($T$ key)->Node_$T$
        {
            return LoadInteger(Table,integer(this),$HashFunc$(key));
        }
        
        method Contains($T$ key)->boolean
        {
            return Fetch(key) > 0;
        }
        
        private method operator Prev()->Node_$T$
        {
            return Node_$T$(this).Prev;
        }
        private method operator Prev=(Node_$T$ node)
        {
            Node_$T$(this).Prev = node;
        }
        
        private method operator Next()->Node_$T$
        {
            return Node_$T$(this).Next;
        }
        
        method operator Count()->integer
        {
            return m_count;
        }
        
        method operator Value()->integer
        {
            return Node_$T$(this).Value;
        }
        
        private method operator Value=(integer value)
        {
            Node_$T$(this).Value = value;
        }
        
        private method Insert($T$ key,integer value)
        {
            integer ung = 6;
            Node_$T$ node = Node_$T$.create(key,value);
            Prev.Next = node;
            node.Next = this;
            Prev = node;
            m_count+=1;
            SaveInteger(Table,integer(this),$HashFunc$(key),node);            
        } 
        

        
        method operator[]($T$ key)->integer
        {
            return Fetch(key).Value;
        }
        
        method operator[]=($T$ key,integer value)
        {
            Node_$T$ node = Fetch(key);
            if(node == 0)
            {
                Insert(key,value);
                return;
            }
            node.Value = value;
        }
        
        method TryGetValue($T$ key)->boolean
        {
            Node_$T$ node = Fetch(key);
            this.Value = node.Value;
            return node > 0;
        }
        
        method Add($T$ key,integer value)->boolean
        {
            Node_$T$ node;
            if(Contains(key)) return false;
            Insert(key,value);
            return true;
        }
        
        method Remove($T$ key)->integer
        {
            Node_$T$ node = Fetch(key);
            integer retVal = node.Value;
            if(node != 0)
            {
                RemoveSavedInteger(Table,integer(this),$HashFunc$(key));
                node.Prev.Next = node.Next;
                node.Next.Prev = node.Prev;
                node.destroy();
                m_count-=1;
            }
            return retVal;
        }

        method GetReadOnlyNode()->RO_DictNode_$T$
        {
            return RO_DictNode_$T$(Next);
        }
        method Clear()
        {
            Node_$T$ node = this.Next;
            while(node != this)
            {
                node.destroy();
                node = node.Next;
            }
            FlushChildHashtable(Table,integer(this));
            m_count = 0;
        }
        
        static method create()->thistype
        {
            Node_$T$ n = Node_$T$.create($DefaultVal$,0);
            n.Next = n;
            n.Prev = n;
            return n;
        }
        
        private method onDestroy()
        {
            Clear();
        }
    }
    
    public struct Set_$T$
    {
        method operator Count()->integer
        {
            return Dict_$T$(this).Count;
        }
        
        method Add($T$ object)->boolean
        {
            return Dict_$T$(this).Add(object,-1);
        }
        
        method Remove($T$ object)->boolean
        {
            return Dict_$T$(this).Remove(object) == -1;
        }
        
        method Contains($T$ object)->boolean
        {
            return Dict_$T$(this).Contains(object);
        }
        
        method Clear()
        {
            Dict_$T$(this).Clear();
        }
        
        private method onDestroy()
        {
            Dict_$T$(this).destroy();
        }
        
        method GetReadOnlyNode()->RO_SetNode_$T$
        {
            return RO_SetNode_$T$(Node_$T$(this).Next);
        }

        static method create()->thistype
        {
            return Dict_$T$.create();
        }
    }
    //! endtextmacro 
    //! runtextmacro DictTemplate("integer","","0")
    //! runtextmacro DictTemplate("handle","GetHandleId","null")
    //! runtextmacro DictTemplate("string","StringHash","null")
}
//! endzinc
And here is a usage example as to why you might try some of the features.
TryGetValue is basically a contains and fetch in the same go. It is perfect for usage in memoization, as sometimes 0 is an acceptable value, and testing for 0 is not inductive as to whether this was because the dictionary failed to retrieve, or because thats what was stored there.
Here is fibonacci. You can try running the Fib method which has exponential runtime, and quickly blow out your op limit, or you can use the faster MemoizedFib which can compute any value in a worse case of o(n).
Also demonstrated is how to enumerate through the values of the dictionary. Using the for loop feature of zinc. Its slightly miserable and a bit long, but it will compile cleanly to some nice jass.
You'll note that you don't have to destroy the readonly node at the end, you are done as it is an array struct and isn't "instanced".
JASS:
library fib
{
    Dict_integer Dict;
    public function Fib(integer n)->integer
    {
        if(n < 2) return n;
        return Fib(n-1)+Fib(n-2);
    }

    public function MemoizedFib(integer n)->integer
    {
        if(Dict.TryGetValue(n)) return Dict.Value;
        if(n < 2)
        {
            Dict[n] = n;
            return n;
        }
        
         Dict[n] = MemoizedFib(n-1)+MemoizedFib(n-2);
         return Dict[n];
    }
    function onInit()
    {
        RO_DictNode_integer node;
        Dict = Dict_integer.create();
        BJDebugMsg(I2S(MemoizedFib(15)));
        for(node = Dict.GetReadOnlyNode(); node != Dict; node = node.Next)
        {
            BJDebugMsg("key= " + I2S(node.Key) + "value= " + I2S(node.Value));
        }
    }
}
 

Jesus4Lyf

Good Idea™
Reaction score
397
Ugh, was interested until I opened the code and found it was all zinc, which I need to find time to get used to still.
JASS:
DictEnumerator_integer enumerator = Dict.GetEnumerator();
integer value;
integer key;
boolean firstTime = true;
while(firstTime or enumerator.MoveNext()) // zinc doesn't support a do-while loop :/
{
     key = enumerator.Key;
     value = enumerator.Value;
}

I am sure that is an infinite loop. :)

PS. Are you sure you mean enumerator and not iterator? The sample code looks like an iterator to me.
What particular use inspired you to make this? A fast Fibonacci is cool and all, but where in writing maps does this really become useful?
 

weaaddar

New Member
Reaction score
6
Yes it is an iterator, but .Net calls this an enumerator, so I'm broken in convention. And Zinc isn't really that much different then Vjass. The enumerator loop is missing one step and I'll add it which is set firstTime = false.

As for why, I was thinking of moving ItemMenu over to Gex's HashSet, but I realized I couldn't use it. I found a generic hashtable/dictionary to be more useful in general terms.

Warcraft 3 is difficult to program in because of its weak support for collections. There aren't standards, and having a dictionary is just something that can be very useful. I find that especially when debugging I like to see what I've stored in associative arrays, and Table's lack of peering in, so to speak makes it annoying to work with.

For instance, GetItemCost my old version of it or Cohandlar always does an expensive operation when you ask for the cost of an item that costs 0 gold and 0 lumber. Sinc eit uses table Cohandlar/me do something like::
JASS:
integer gc = GoldCosts[itemId];
integer lc = LumberCosts[itemId];
if(gc != 0 || lc != 0)
{
// return what they asked for
}
// create a shop, create a hero, set player 15's gold and lumber to 50,000, add the item to the shop, order the hero to buy it.
// cache results, return what they asked for.

With table we can now do something easier like::
JASS:
if(GoldCosts.TryGetValue(itemId) && LumberCosts.TryGetValue(itemId))
{
//return GoldCosts.Value or whatever
}


And now you can also go through all the costs found and maybe modify them to give a better values or whatever.
 

Jesus4Lyf

Good Idea™
Reaction score
397
Sorry about the edits, but...
What particular use inspired you to make this? A fast Fibonacci is cool and all, but where in writing maps does this really become useful?
This is what I'm most interested in, it will tell me if this is useful or not. In practical ways.

It just seems to me that in any reasonable situation, you use one or the other, not both.

>Zinc isn't really that much different then Vjass
I know, I'm just not used to it. I miss vJass's [LJASS]endif[/LJASS], [LJASS]endloop[/LJASS] and [LJASS] endfunction[/LJASS], etc. Big chunky bolded words saying "THIS HAS ENDED!". Not used to Zinc. :p
JASS:
method MoveNext()->boolean

Oh my gosh, you really are broken in convention. You .NET programmer, you!!.. MICROSOFT, YOU DID THIS TO US!... :banghead:

This is actually terrible to read since you do break conventions like methodNames versus FunctionNames, when you said "Fetch(key)" I assumed it was a function, since Zinc can't distinguish. I think you should address this, but that's my opinion.

On more real issues, it is possible for values attached to an integer and a string which happens to have the same hash as that integer to collide. Will this be fixed?
Edit: No it's not, my bad. :p
 

weaaddar

New Member
Reaction score
6
I completely dispise the convention of member functions having lowercase letters in Vjass. I find myself having to modify every library (not many thankfully) I use to fix this grevious sin. Java/C#/Python all believe that the notation is method names start with capitals, member values start lowercased. I believe that member values should start with underscore but thats unsupported by Zinc. So I stick with the m_, thats pretty popualr in C++. Local variables are lower cased, Class names are capitaled, and Constants are all-uppered.

And yeah like Table each dictionary uses a different Parent Key. I don't support 2d notation.

I can switch invocation of Fetch which is pretty often to this.Fetch but I like the whole not having to use . anymore when using privates. :p

Edit:
Because of its low cost, you can treat it like a handle group with a free attach system at the worst case. Or you can use it as a hashset and just store a dummy value in the index.
Like::
JASS:
Dict_handle units = Dict_handle.create();
units.Add(CreateUnit(Player(0),'hfoo',0,0,0),0);
units.Add(CreateUnit(Player(1),'hfoo',0,0,0),0);
units.Add(CreateUnit(Player(2),'hfoo',0,0,0),0);
units.Add(CreateUnit(Player(3),'hfoo',0,0,0),0);
 

Jesus4Lyf

Good Idea™
Reaction score
397
Java/C#/Python all believe that the notation is method names start with capitals
No, Java doesn't.

I like Capitalised method names, but not in vJass since functions exist.

>I can switch invocation of Fetch which is pretty often to this.Fetch but I like the whole not having to use . anymore when using privates.
I dunno ey. There's an obvious problem with your convention though. Up to you what you do about it/if you do anything about it.
What particular use inspired you to make this? A fast Fibonacci is cool and all, but where in writing maps does this really become useful?
This is what I'm most interested in, it will tell me if this is useful or not. In practical ways.
Rules:
Try to make your system/snippet generally useful. If you think that nobody can use your system/snippet, don't submit it.
I am asking so I don't graveyard it (third time).
 

weaaddar

New Member
Reaction score
6
I posted a couple of suggested uses, and what I'm using it for. I'm using it as an upgrade in my new version GetItemCost for now. I'm also using it in itemMenu (still fixing one horrible bug so I can't release yet) so that I can keep track of what units have been made heroes.

Its an upgraded version of table that lets me keep track of items stored, knows how much I stored in it, and lets me traverse the collection easily. At the very worst you can use it as a handle attach system that knows how many are attached.


Are you seriously telling me you see no need what so ever for a dictionray? I use hashtables all the time at work, again I love having more collections in my toolkit.
 

Jesus4Lyf

Good Idea™
Reaction score
397
>I can keep track of what units have been made heroes.
Unit group?

>Are you seriously telling me you see no need what so ever for a dictionray?
In JASS I always expected I'd need one, and then never did. That's why I was asking.

If I need to attach a list of things to a handle, it is usually a timer to perform actions on a list of items. For that, I use a singularly linked list, which I write when I need it. I don't need to check if an item is in that list, just iterate through it.

If I need to check if a unit is in a group, I use an AIDS list or a unit group. I can iterate through that. These don't need keys.

There are two situations I would need this:
  • I need to attach an unknown number of items to a handle, and be able to check if an item is in that list of attached items.
  • I need to attach to non-serializable keys on an item, and iterate over all valid keys (and I have no way of knowing which keys are valid for a given dictionary).

In the first instance, if it is units, I attach a unit group (much more efficient). I have never come across the first instance, except in hit-units-in-a-line spell, in which I indeed use unit groups.

In the second instance, direct hashtable use is better, and I have not come across a situation in which I need to both attach to non-serializable keys on an item (create a many-to-many relationship with data attached to that relationship) and need to iterate over all relationships of an item. I suppose if I needed to iterate over it, then I would be in need of this. But this actually creates quite an awkward interface.

Let's say I need to attach, to every unit in my map, a disposition to every other unit. I can attach, to each unit, one of your dictionaries. I can then look up every unit in that dictionary. However, I cannot see which units like a unit. This is because in your collection you may iterate over all items attached, but not over all dictionaries an item has been attached to. So in the one situation I can see this being useful, it is lacking.
Edit: Just realised the above doesn't make sense. I need to think a little more. :p
Edit2: In this situation, I would serialise the keys using a unit indexer. Then I have a 2d array. ... ah, this only works if there is a fixed number of units... hmm but I won't need to iterate over the collection.

I'm not sure. I'm not sold that this is practical. If I could think of a situation where I would need this...

PS Edit: By the way, your documentation is lacking. When reading it I struggled to follow, because it didn't document any create/destroy method. ;)
 

weaaddar

New Member
Reaction score
6
Err, I actually need the dictionary not just as a unit group but as an attachment as well. Without dictionary I would need a unitgroup (which is annoying to traverse, however fast filtering is. It breaks up code flow even with anonymous functions), and table so that I can say Hero.GetAssociatedStruct(myUnit); //haven't figured out a good name for this method.
Admittedly though there is a unit group, but there isn't a trigger group. WHen I create a Hero struct, I create like 7 triggers for that unit. It'd be nice to say if I deallocate the hero, I can just say hey disable this set of triggers set (I'll write a hashset wrapper). And actually the Hero stores all the itemMenus in my List struct, which is a arraylist, but really should be a set, as I don't care for random access I merely need the ability to traverse it, know how many there are, and figure out which icons to populate in the activator menu.

edit:
In case this does get approved here is a quick together implementation of HashSet written in roughly 5 minutes::
JASS:
//! zinc
library HashSet requires Dictionary
{
    //! textmacro HashSetTemplate takes T
    public struct Set_$T$
    {
        Dict_$T$ m_dict;
        
        method operator Count()->integer
        {
            return m_dict.Count;
        }
        
        method Add($T$ object)->boolean
        {
            return m_dict.Add(object,-1);
        }
        
        method Remove($T$ object)->boolean
        {
            return m_dict.Remove(object) == -1;
        }
        
        method Contains($T$ object)->boolean
        {
            return m_dict.Contains(object);
        }
        
        method Clear()
        {
            m_dict.Clear();
        }
        
        private method onDestroy()
        {
            m_dict.destroy();
        }
        
        method GetEnumerator()->DictEnumerator_$T$
        {
            return m_dict.GetEnumerator();
        }

        static method create()->thistype
        {
            thistype s = thistype.allocate();
            s.m_dict = m_dict.create();
            return s;
        }
    }
        //! endtextmacro        
        //! runtextmacro HashSetTemplate("integer")
        //! runtextmacro HashSetTemplate("handle")
        //! runtextmacro HashSetTemplate("string")
}
//! endzinc
Obviously, if I really wanted to submit it as a resource I would probably write a hashset enumerator which dictionary enumerator would inherit from. And I would also have to document.

Are you serious you want documentation on creates and destroys? I thought the create without parameters was pretty much as easy as it got. Since you can't make a node as its private, you can't actually make an enumerator either, despite being public. So the only instantiateable thing in the whole Library is dict_integer,dict_handle, or dict_string.
 

Jesus4Lyf

Good Idea™
Reaction score
397
>So the only instantiateable thing in the whole Library is dict_integer,dict_handle, or dict_string.
How do I know that? :p

Actually, I was wondering when I was reading it if those were static structs or something. So the use would be dict_integer[myKey]==myValue, as opposed to myIntegerDictionary[myKey]==myValue... wasn't sure.

>WHen I create a Hero struct, I create like 7 triggers for that unit. It'd be nice to say if I deallocate the hero, I can just say hey disable this set of triggers set (I'll write a hashset wrapper). And actually the Hero stores all the itemMenus in my List struct, which is a arraylist, but really should be a set, as I don't care for random access I merely need the ability to traverse it, know how many there are, and figure out which icons to populate in the activator menu.

So you don't need a hashtable, you need a singularly linked list and a "count" integer.

The only situation in which this the dictionary seems necessary is one in which you need random access and need to traverse. Which is uncommon. If you need just random access, use a hashtable. If you need just to traverse, use a singularly linked list or a doubly linked circular list. I still can't think of a situation in which you need to do both...

Edit: How about you attach a struct to a bunch of timers/triggers, and when you destroy the struct you need to destroy all the timers/triggers and flush their attached data, but you may destroy any number of them before destroying the struct already...
Ironic. I need exactly that for SpellStruct. :p
But then...
this actually creates quite an awkward interface.
...
in your collection you may iterate over all items attached, but not over all dictionaries an item has been attached to.
See, I can't find a trigger's attached data using this, then, because I don't know which dictionary holds its attachment...

I don't know how to solve that in a meaningful way. I could attach the dictionary to each trigger, but... it is quickly losing its maintainability if I do that.
 

Renendaru

(Evol)ution is nothing without love.
Reaction score
309
This seems to be very similar to a Linked List, in use at least.
 

weaaddar

New Member
Reaction score
6
Somewhat. Think of it as Table and Linked List combined.
If you have arbitrary large amount of objects then::
Dictionary is truly needed when you want to have associativity (i.e. Dict[SomeUnit] = 45), random access ( you need to ask does this collection contain x?), and the ability to enumerate the elements in the collection (while(I still have elements){ do something}).

HashSet (posted in this thread), basically lets you get the random acess and enumeration part if you don't need associativity.

If you only need associative and don't need enumeration use Table.

If you only need the ability to enumerate use a linked List.

While dictionary is relatively cheap, a good linked list and most of table will inline to native code. In other words, don't use dictionary as a linked list of pairs.

edit: On second thought its not so terrible to use as a linked list, but a simpler linked list is probably better as you don't have to worry about a hashtable calls.

I'm also going to add a private insert method to make operator []=, and add a little more efficient, and switch all methods to camelcased lower case letters (yuck).
 

Jesus4Lyf

Good Idea™
Reaction score
397
>Dictionary is truly needed when you want to have associativity (i.e. Dict[SomeUnit] = 45), random access ( you need to ask does this collection contain x?), and the ability to enumerate the elements in the collection (while(I still have elements){ do something}).

Eliminating the bolded bit would still mean you truly need a dictionary. :p
Same for the first, they're kinda similar.
 

weaaddar

New Member
Reaction score
6
Obviously I meant for you to use a typecast library...
But more seriously I think this is probably a good thing that its a macro and in theory you can instance any dictionary you'd want :)
 
Reaction score
333
I think enumerators should be done differently:

  • The return value from "MoveNext" should be removed in favor of a "HasNext" method.
  • For the sake of semantic cleanliness, the internal destroy should be removed in favor of users manually calling destroy.
  • Users should be able to create enumerators by calling the create method with the dictionary as an argument (HasNext would return false in the case of an enumerator over an invalid or empty dictionary).
 

Jesus4Lyf

Good Idea™
Reaction score
397
JASS:
public struct DictEnumerator_$T$
    {
        private Node_$T$ m_node;
        method operator Key()->$T$
        {
            return m_node.Key;
        }
        method operator Value()->integer
        {
            return m_node.Value;
        }
        method MoveNext()->boolean
        {
            thistype retval;
            m_node = m_node.Next;
            retval = m_node;
            if(m_node == 0) this.destroy();
            return retval > 0;
        }
        static method create(Node_$T$ node)->thistype
        {
            thistype e;
            if(node == 0) return 0;
            e = thistype.allocate();
            e.m_node = node;
            return e;
        }
    }

-->
JASS:
    public struct DictEnumerator_$T$
    {
        method operator Key()->$T$
        {
            return Node_$T$(this).Key;
        }
        method operator Value()->integer
        {
            return Node_$T$(this).Value;
        }
        method operator MoveNext=(integer i)->thistype
        {
            return thistype(Node_$T$(this).Next);
        }
        static method create(Node_$T$ node)->thistype
        {
            return thistype(node);
        }
    }

It would be perfect if you didn't have to use the syntax enum.MoveNext=1 to increment.
 

weaaddar

New Member
Reaction score
6
The enumerator returns false if it fails to enumerate any further. MoveNext vs HasNext is semantic difference. I'm from the school of .net, so enumerators test and move in the same step. As for why it destroys itself, it makes the loop a little easier. As after all once your done it destroys itself, and if you want to manually destroy it, you are more then welcome. But the important thing is the enumerator is readOnly so it doesn't effect the outside world.

And Jesus4lyf, I'm not sure why MoveNext is a property. MoveNext= 7 provides the samesult as MoveNext=4 as you never use i. I really rather have a private node variable. m_node is an instance of an object not a type. So I'm not sure what m_node(this) means. Do you mean Node_$T$(this)? That might be okay but hokey. That would make the enumerator better suited as one of those wacko array structs that you don't create like an ARGB.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top