From WikiChip
Editing mirc/commands/hmake

Warning: You are not logged in. Your IP address will be publicly visible if you make any edits. If you log in or create an account, your edits will be attributed to your username, along with other benefits.

The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then save the changes below to finish undoing the edit.

This page supports semantic in-text annotations (e.g. "[[Is specified as::World Heritage Site]]") to build structured and queryable content provided by Semantic MediaWiki. For a comprehensive description on how to use annotations or the #ask parser function, please have a look at the getting started, in-text annotation, or inline queries help pages.

Latest revision Your text
Line 4: Line 4:
 
A hash table is stored entirely in memory and thus it is the programmer's responsibility to save the data to a file if necessary via the {{mIRC|/hsave}} command (and load it back later via the {{mIRC|/hload}} command).
 
A hash table is stored entirely in memory and thus it is the programmer's responsibility to save the data to a file if necessary via the {{mIRC|/hsave}} command (and load it back later via the {{mIRC|/hload}} command).
  
It's generally best to have the number of buckets be a prime number, and starting with v7.53 mIRC started forcing the buckets to be either 1 or a prime number. Prior to that, mIRC only incremented the buckets to avoid an even number. The default parameter for number of buckets is 100, with allowed values from 1 to 10000. The old behavior was to increment those to the odd 101 or 10001. Current behavior increments 2+ if it's needed to make the buckets be a prime number, so the default 100 increases to the prime 101, and 10000 max increases to the prime 10007.
+
It's generally best to have the number of buckets be a prime number, but mIRC gives the user a large variety of choices. Instead of forcing the number of buckets to be prime, mIRC only forces the number to be odd. If [num_buckets] is an even number, mIRC will add one to the number. The default parameter for number of buckets is 100, with allowed values from 1 to 10000. Since even numbers are incremented to make them odd, the actual range is 1 to 10001 - with the default 100 incremented to 101, which is a prime number.
  
A hash table name was formerly limited to 256 significant characters - any additional characters were simply ignored. Starting with v7.53 the limit is now 612 characters. ({{mIRC|$maxlens}}'s 512+100) In mIRC, a hash table is a much faster alternative to ini and normal text files. (Rephrase or Citation needed. hashtable
+
A hash table name is limited to 256 significant characters - any additional characters are simply ignored. In mIRC, a hash table is a much faster alternative to ini and normal text files.
  
'''Note:''' For most practical purposes, if wanting to lessen the chance of more than 1 item per bucket, it's best to keep the ratio of <item count>:<maximum capacity> at 78% to maintain a good time-space tradeoff (i.e. 0.78 load factor). That means if you are planning on storing 78 items in the hash table, you should create a hash table with the size of 100 buckets. (A table of 1000 buckets is good to store up to about 780 items to maintain a maximum performance). The general equation to calculate optimal number of buckets is:
+
'''Note:''' For most practical purposes, it's best to keep the ratio of <item count>:<maximum capacity> at 78% to maintain a good time-space tradeoff (i.e. 0.78 load factor). That means if you are planning on storing 78 items in the hash table, you should create a hash table with the size of 100 buckets. (A table of 1000 buckets is good to store up to about 780 items to maintain a maximum performance). The general equation to calculate optimal number of buckets is:
  
 
* [#_of_buckets] = [#_of_keys_that_will_be_used] / 0.78
 
* [#_of_buckets] = [#_of_keys_that_will_be_used] / 0.78
 
== Linked List ==
 
== Linked List ==
Because the hash table uses chaining to resolve collisions and due to the fact mIRC does not rehash or grows the array, using a hash table created with 1 bucket can be used to create a linked list. New items are added to the start of the chain (i.e. $hget(<table_name>, 1) will always be last item added). Additionally, changing the value or removing an item does not alter the overall order of the list.
+
Because the hash table uses chaining to resolve collisions and due to the fact mIRC does not rehash or grows the array, using a hash table with 1 bucket, the hmake command can be used to create a linked list. New items are added to the start of the chain (i.e. $hget(<table_name>, 1) will always be last item added). Additionally, changing the value or removing an item does not alter the overall order of the list.
 
== Synopsis ==
 
== Synopsis ==
 
  /hmake [-smNhL] <table_name> [num_buckets]
 
  /hmake [-smNhL] <table_name> [num_buckets]
Line 28: Line 28:
 
== Parameters ==
 
== Parameters ==
 
* '''<table_name>''' - The name of the table you wish to make.
 
* '''<table_name>''' - The name of the table you wish to make.
* '''[num_buckets]''' - The number of buckets to use as the table's capacity (If no number is specified, the default is 100 (effectively 101 buckets))
+
* '''[num_buckets]''' - The number of buckets to use as the table's capacity (If no number is specified, the default is 100)
 
== Example ==
 
== Example ==
 
A basic usage for a hash table.
 
A basic usage for a hash table.
Line 94: Line 94:
 
   }
 
   }
 
}</syntaxhighlight>
 
}</syntaxhighlight>
 
* Demonstrates how table items are accessed by $hget(table,N) in reverse order of creation if table created with 1 bucket. Changing the hmake command to use a larger number of buckets causes the items to be associated with N in a non-sequential pattern:
 
 
<source lang="mIRC">
 
<source lang="mIRC">
//hfree -sw test | hmake -s test 1 | var %i 1 | while (%i isnum 1-10) { hadd test item $+ $base(%i,10,10,3) data | inc %i } | var %n 1 | while ($hget(test,%n).item) { echo -a $ord(%n) itemname is $v1 | inc %n }
+
//hfree -sw test | hmake -s test 1 | var %i 1 | while (%i isnum 1-50) { hadd test item $+ $base(%i,10,10,3) data | inc %i } | var %n 1 | while ($hget(test,%n).item) { echo -a $ord(%n) itemname is $v1 | inc %n }
</source>
+
* Demonstrates how table items are accessed by $hget(table,N) in reverse order of creation if table created with 1 bucket. Changing the hmake command to use a larger number of buckets causes the items to be associated with N in a non-sequential pattern.
  
* Through v7.52, demonstrates that the number of buckets is always an odd number. An even number of buckets and even+1 arrange the items in the same sequence. (This was true through v7.52 where $hget(tablename).size reported the buckets parameter used to create the table instead of the actual number of buckets. Beginning v7.53 the number of buckets is reported correctly, and now uses the prime number >= size parameter while allowing buckets=1. The max N for the buckets parameter is 10000, which uses the next available prime, 10007.
 
<source lang="mIRC">
 
//hfree -sw test | hmake -s test 2 | var %i 1 , %a | while (%i isnum 1-999) { hadd test item $+ $base(%i,10,10,3) data | inc %i } | var %n 1 | while ($hget(test,%n).item) { var %a $sha1(%a $v1) | inc %n } | echo -a hash of item sequence %a
 
</source>
 
 
* Demonstrates that it can be 10x faster to create a hashtable containing 9999 items than to create 9999 local %variables:
 
<source lang="mIRC">
 
 
//hfree -sw test | hmake -s test | var %i 9999 , %ticks $ticks | while (%i) { var %test $+ %i data %i | dec %i } | echo 4 -a done $calc($ticks - %ticks) ticks
 
//hfree -sw test | hmake -s test | var %i 9999 , %ticks $ticks | while (%i) { var %test $+ %i data %i | dec %i } | echo 4 -a done $calc($ticks - %ticks) ticks
 
//hfree -sw test | hmake -s test | var %i 9999 , %ticks $ticks | while (%i) { hadd test    %i data %i | dec %i } | echo 4 -a done $calc($ticks - %ticks) ticks
 
//hfree -sw test | hmake -s test | var %i 9999 , %ticks $ticks | while (%i) { hadd test    %i data %i | dec %i } | echo 4 -a done $calc($ticks - %ticks) ticks
</source>
+
* Demonstrates that it can be 10x faster to create a hashtable containing 9999 items than to create 9999 local %variables.
*It can be simpler to access dynamically named items from hash tables because hash tables don't require using $eval or [ braces ] to access the value in a hashtable item.
 
<source lang="mIRC">
 
//var -s %nick foobar , %flood. $+ %nick $ticks , %test %flood. [ $+ [ %nick ] ]
 
vs
 
//var %nick foobar | hadd -sm flood %nick $ticks | echo -a %nick is $hget(flood,%nick)
 
</source>
 
To use hash tables instead of variables, there are a few extra differences to be aware of.
 
* You can receive $null from a $hget(no-such-table,item) or $hget(existing-table,no-such-item) without an error
 
* But you cannot create an item without first making sure that the table exists. If it's possible for the table to not exist, you can use /hadd's -m or -mN switch to create the table if needed.
 
* You cannot use the /hmake command to create a table without making certain the table does not already exist.
 
<source lang="mIRC">//if (!$hget(tablename)) hmake tablename</source>
 
* You cannot delete a tablename with /hfree unless you make certain the table already exists, or use the -w switch without a wildcard
 
<source lang="mIRC">//if ($hget(tablename)) hfree tablename
 
or
 
/hfree -sw tablename</source>
 
* The equivalent to /unset is /hdel, which can accept wildcards
 
* To retrieve data from a hashtable item:
 
<source lang="mIRC">//echo -a %variablename is the same as $hget(tablename,itemname)</source>
 
* To check for the existence of a variable, scripts currently check if $var(%NonWildcardVariablName,1) is $null. To check if the item name exists, check if $hfind(tablename,itemname) is $null.
 
* Note that $hfind is able to do things $var() cannot do, such as finding items based on the wildcard or regex pattern of the itemnames or the data inside them.
 
* hashtables do not have the same global vs local scope as %variables do. While it's possible to use %var inside an alias to mask the value of a same-name global variable, you cannot do this with hashtables. While a local variable can be seen only inside the alias or :EVENT: where it was created, hashtables and their items are created only with global scope.
 
* mIRC handles saving global %variables to disk for you, but it doesn't do that with hashtables. If you need to save updated hashtable data for the next mIRC restart, you must use /hsave to save it to disk, then must use /hload to retrieve it after restart.
 
* As with %variables, hashtable items can be created with similar temporary status, but does not offer the same syntax, and not all features.
 
* Temporary item existing for 5 seconds:
 
<source lang="mIRC">
 
//hadd -mu5 test item | echo -a $hget(test,item).unset | timer 5 1 echo -a $!hget(test,item).unset
 
</source>
 
* Note that items created or updated using the -uN property are by default not saved to disk without using /hsave's -u switch.
 
* Using -k to ignore the N in -uN only if the .unset is already non-zero
 
<source lang="mIRC">
 
//hadd -mku5 test item | echo -a $hget(test,item).unset | timerx1 5 1 echo -a $!hget(test,item).unset | timerx2 1 2 hadd -ku10 test item
 
</source>
 
* -z decrements item each second. This example deletes item whenever the first of; either the item value reaching zero or the .unset time reaches zero:
 
<source lang="mIRC">
 
//hadd -mku5z test item $rand(3,9) | timerx1 5 1 echo -a unset $!hget(test,item).unset value $!hget(test,item)</source>
 
* There is no -e flag to 'delete on exit', but you can do the equivalent by setting a very long -uN time.
 
<source lang="mIRC">
 
//set -e %varname 1 | hadd -mu $+ $calc(2^31-1) table item | echo -a $var(%varname,1).secs vs $hget(table,item).unset
 
</source>
 
* hinc and hdec have the same -c switch as /inc|/dec to inc or dec the value each second
 
<source lang="mIRC">
 
//hfree -w test | hinc -mcu5 test item-hinc 5 | hdec -cu5 test item-hdec-c 5 | timerx1 5 1 echo -a item-hinc $!hget(test,item-hinc) item-hdec $!hget(test,item-hdec-c)
 
</source>
 
  
If needing items to always be in a predictable sequence, you can't count on using /hsave and /hload to preserve that order. '''These series of commands should be pasted separately in sequence, as a demonstration of each bullet point.''' When using buckets=1...
+
//hfree -sw test | hmake -s test 2 | var %i 1 , %a | while (%i isnum 1-999) { hadd test item $+ $base(%i,10,10,3) data | inc %i } | var %n 1 | while ($hget(test,%n).item) { var %a $sha1(%a $v1) | inc %n } | echo -a hash of item sequence %a
* Items listed in reverse order of creation. 1st created item is $hget(table,N) where N is the number of items in the table. i.e. $hget(table, $hget(table,0).item )
+
* Demonstrates that the number of buckets is always an odd number. An even number of buckets and even+1 arrange the items in the same sequence.
<source lang="mIRC">
 
//hfree -sw test | hmake -s test 1 | var %i 1 | while (%i <= 10) { hadd test item $+ %i data | inc %i } | var %N 1 | while ($hget(test,%N).item) { echo 4 -a $ord(%N) item is $hget(test,%N).item | inc %N }
 
</source>
 
* When /hsave writes items to disk, they're written to disk in sequential N order, where the last created item in $hget(table,1) position is written first.
 
<source lang="mIRC">//hsave -s test test.dat | run notepad test.dat</source>
 
* When /hload adds items from disk, it adds them in sequential order with the 1st item on disk added first, and the last item on disk added last. If the item name already exists in the table, it retains its current Nth position in the table, but all new additions are again added into reverse order of creation, where the final added item is in the $hget(table,1) position.
 
<source lang="mIRC">
 
//hfree -w test2 | hadd -sm1 test2 item5 | hload -sm1 test2 test.dat | var %i 1 | while ($hget(test2,%i).item) { echo -a the $ord(%i) item is $hget(test2,%i).item | inc %i }
 
</source>
 
* If an item is deleted then added, it is moved to the $hget(table,1) position
 
<source lang="mIRC">
 
//hdel -s test2 item7 | hadd -s test2 item7 | var %i 1 | while ($hget(test2,%i).item) { echo -a the $ord(%i) item is $hget(test2,%i).item | inc %i }
 
</source>
 
* To retain the original sequence where the original order of creation is preserved except in cases where items are deleted then added, giving them a newer creation order, you cannot simply /hsave the table ON EXIT then /hload ON START. During ON START you would either need to /hload + /hsave + /hfree + /hload which does extra disk writing:
 
<source lang="mIRC">
 
//hfree -w test2 | hload -sm1 test2 test.dat | hsave test2 test2.dat | hfree -w test2 | hload -sm1 test2 test2.dat | var %i 1 | while ($hget(test2,%i).item) { echo -a the $ord(%i) item is $hget(test2,%i).item | inc %i }
 
</source>
 
* or /hload the table into a dummy table, from which you re-add them from 'dummy' to the 'real' table in the N=1-to-total sequence, giving the 'real' table the same 'reverse order of creation' order:
 
<source lang="mIRC">
 
//hfree -w dummy | hfree -w test2 | hload -sm1 dummy test.dat | hmake -s test2 1 | var %i 1 | while ($hget(dummy,%i).item) { hadd -s test2 $v1 $hget(test2,$v1) | inc %i } | var %i 1 | hadd test2 newestitem | while ($hget(test2,%i).item) { echo -a the $ord(%i) item is $hget(test2,%i).item | inc %i }
 
  
*Note: If your table is in binary format, you need to preserve binary data by replacing
+
* If needing items to always be in a predictable sequence, you can't count on using /hsave and /hload to preserve that order. By using 1 bucket, this example creates Item $+ N as the Nth item:
hadd -s test2 $v1 $hget(test2,$v1)
+
//hfree -sw test | hmake -s test 1 | var %i 20 | while (%i) { hadd test item $+ %i data | dec %i } | var %N 1 | while ($hget(test,%N).item) { echo 4 -a $ord(%N) item is $hget(test,%N).item | inc %N }
with
+
* This command saves the 20 items to disk, with the 1st pair of rows being the item named Item1 and the last pair of rows being the item named Item20:
noop $hget(dummy,$v1,&temp) | hadd -b test2 $v1 &temp
+
//hsave -s test test.dat | run notepad test.dat
 +
* When you /hload this diskfile into memory, the items are loaded into the Nth item in reverse order, where the 1st item now contains the item named Item20 instead of Item1:
 +
//hfree -w test2 | hload -sm1 test2 test.dat | echo -a the 1st item is $hget(test2,1).item
 +
* If you save this new hashtable to disk again, you'll find the line-pairs are saved to disk in the opposite order than in test.dat, which will cause loading them into a hash table to finally be the original seqence of the 1st table:
 +
//hsave -s test2 test2.dat | run notepad test2.dat
 
</source>
 
</source>
 
== Compatibility ==
 
== Compatibility ==

Please note that all contributions to WikiChip may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see WikiChip:Copyrights for details). Do not submit copyrighted work without permission!

Cancel | Editing help (opens in new window)