FreeBASIC マニュアルのトップに戻る

FreeBASIC 連想配列(GLib Hash Table)

目次→フォーラム→FreeBASIC→補足Fast text search (hash table / GLib)←オリジナル・サイト

連想配列(GLib Hash Table) 左にメニュー・フレームが表示されていない場合は、ここをクリックして下さい

←リンク元に戻る プログラム開発関連に戻る

 このページは、FreeBASIC の examples フォルダにあるプログラムを微修正して紹介します。
\FreeBASIC\examples\misc\glib\g_HashTable.bas
 glib を使うため、dll を準備するのに手間どいましたが、フォーラムで TJF さんなどのアドバイスをいただき、コンパイルして実行できるようになりました。
 私が知る限り、最速の連想配列です。

 ただし、ポインタを使うのでそれなりの注意が必要です。
 速度を要求しないなら「連想配列(containers の hashtable)」の方がコーディングが容易で、私の推奨です。

外部参考サイト:
 GLib リファレンスマニュアル
https://documents.mikeforce.net/glib-2.18.x-refs/glib/html/
 ハッシュ・テーブル
https://documents.mikeforce.net/glib-2.18.x-refs/glib/html/glib-Hash-Tables.html
 GLib の驚異
https://www.ibm.com/developerworks/jp/linux/library/l-glib/
 ハッシュ・テーブル、反復処理、GScanner、トークン
https://www.ibm.com/developerworks/jp/linux/library/l-glib2.html
 GLib のハッシュテーブルを使う
https://linuxshugendo.wordpress.com/2012/03/15/glib-%E3%81%AE%E3%83%8F%E3%83%83%E3%82%B7%E3%83%A5%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%82%92%E4%BD%BF%E3%81%86/

注意1:FreeBASIC の Win32 か Win64 のどちらを使うかによって、対応した dll を入手する必要があります。
注意2:私は、最初 FreeBASIC の lib フォルダに dll を登録しました。通常はこれで使えるはずですが、何故かソースのフォルダにも dll をコピーしないとコンパイルできませんでした。
注意3:Win32 のみで使える Ultimate FB HashMap もあります。
注意4:FreeBASIC 1.05 だと使える連想配列に、Dictionary Class があります。

DLL の入手先:
 gtk3 32-bit dlls.7z
https://drive.google.com/file/d/1Cybpf-23jLamuQK10m4nplbMEzL7xTXz/view?usp=sharing
 gtk3 64-bit dlls.7z
https://drive.google.com/file/d/10lSg6rrYFmVIf1miomY-6nvxUNn0f1a6/view?usp=sharing
 gtk2-runtime-2.24.10-2012-10-10-ash.exe (gtk2-runtime-2.24.8-2011-12-03-ash.exe)
http://sourceforge.jp/projects/sfnet_gtk-win/


このページ内の目次
1.examples\misc\glib\g_HashTable.bas
2.キー生成での干渉の有無チェック
3.キー生成と連想配列生成結果の照合チェック
4.品目マスタと品目オーダを照合

基本関数
★新しいハッシュテーブルを作成★返り値:新しい GHashTable
Var table = g_hash_table_new(@g_str_hash, @g_str_equal)
第一引数はキーをハッシュする関数、第二引数はキーを比較する関数
' 演算子 @ (のアドレス)
guint g_str_hash (gconstpointer v) 文字列をハッシュ値に変換します。
他に、g_direct_hash, g_int_hash, g_int64_hash, g_double_hash があります。
gboolean g_str_equal (gconstpointer v1, gconstpointer v2) 2つの文字列をバイト毎に比較して、等しい場合は TRUE を返します。
比較関数には他に、g_direct_equal, g_int_equal, g_int64_equal, g_double_equal があります。


★キーと値を挿入★
g_hash_table_insert ()
g_hash_table_insert (GHashTable *hash_table, gpointer key, gpointer value)
新しいキーとその値を GHashTable に挿入します。
既に新しいキーが GHashTable の中に存在している場合は、そのキーの値が新しい値で置き換えられます。

★既存のキーを置き換える
' 注:g_hash_table_insert は古い値を破壊する
' g_hash_table_replace は古い値とキーを破壊する


★キーの存在、キーに関連づけられた値を確認
g_hash_table_lookup ()
gpointer
g_hash_table_lookup (GHashTable *hash_table, gconstpointer key)
GHashTable の中にあるキーを検索します。

g_hash_table_lookup_extended ()
gboolean
g_hash_table_lookup_extended (GHashTable *hash_table, gconstpointer lookup_key, gpointer *orig_key, gpointer *value)
GHashTable の中にあるキーを検索して、そのキーとそれに関連づけられた値、そしてキーが見つかったことを示す gboolean 型の TRUE を返します。これはメモリ上に確保したキーを解放する必要がある場合に便利な関数で、例えば g_hash_table_remove() 関数を呼び出す前に利用できます。

g_hash_table_remove ()
gboolean
g_hash_table_remove (GHashTable *hash_table, gconstpointer key)
GHashTable からキーとその値を削除します。


Print all keys and values / Alle Keys und Values ausgeben
すべてのキーと値を表示する
g_hash_table_foreach ()
void
g_hash_table_foreach (GHashTable *hash_table, GHFunc func:キーと値のペアを引数にして呼び出す関数, gpointer user_data:func に引き渡すユーザ・データ)

GHashTable に格納されているキーと値のペアに対して指定した関数を順番に呼び出します。この関数にはキーとその値のペア、そして user_data がそれぞれ引き渡されます。

参考:GLIB のハッシュテーブル(再挑戦)!
https://linuxshugendo.wordpress.com/2014/05/09/glib-%E3%81%AE%E3%83%8F%E3%83%83%E3%82%B7%E3%83%A5%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%EF%BC%88%E5%86%8D%E6%8C%91%E6%88%A6%EF%BC%89%EF%BC%81/


FreeBASIC の examples\misc\glib\g_HashTable.bas

 FreeBASIC の予約語をハッシュ・テーブルに登録して表示します。

HashTable_glib JP2.bas
'Fast text search (hash table / GLib)
'by TJF ≫ Sep 26, 2011 12:16 
'https://www.freebasic.net/forum/viewtopic.php?f=7&t=18558&p=163893&hilit=glib+hash#p163893


' This is file HashTable_glib.bas, an example for GLib Hash tables
' (C) 2011 by Thomas[ dot ]Freiherr[ at ]gmx{ dot }net
' License GPLv 3
'
' See for details
' http://developer.gnome.org/glib/2.28/glib-Hash-Tables.html

#INCLUDE ONCE "glib.bi"

' Define some keys (FB keywords) /  Definiere Keys (hier FB Befehle)
' いくつかのキー(FB キーワード)を定義する。演算子 @ (のアドレス)
Static As ZString Const Ptr FBKEY(...) = { _
@"ABS",@"ACCESS",@"ACOS",@"ALIAS",@"ALLOCATE",@"ALPHA",@"AND", _
@"ANDALSO",@"ANY",@"APPEND",@"ASSERT",@"ASSERTWARN",@"ASC", _
@"ASIN",@"ASM",@"ATAN2",@"ATN", _
@"BASE",@"BEEP",@"BIN",@"BINARY",@"BIT",@"BITRESET",@"BITSET", _
@"BLOAD",@"BSAVE", _
@"CALL",@"CALLOCATE",@"CASE",@"CAST",@"CBYTE",@"CDBL",@"CDECL", _
@"CHAIN",@"CHDIR",@"CHR",@"CINT",@"CIRCLE",@"CLASS",@"CLEAR", _
@"CLNG",@"CLNGINT",@"CLOSE",@"CLS",@"COLOR",@"COM",@"CONS", _
@"COMMAND",@"COMMON",@"CONDBROADCAST",@"CONDCREATE",@"CONDDESTROY", _
@"CONDSIGNAL",@"CONDWAIT",@"CONST",@"CONSTRUCTOR",@"CONTINUE", _
@"COS",@"CPTR",@"CSHORT",@"CSIGN",@"CSNG",@"CSRLIN",@"CUBYTE", _
@"CUINT",@"CULNG",@"CULNGINT",@"CUNSG",@"CURDIR",@"CUSHORT", _
@"CUSTOM",@"CVD",@"CVI",@"CVL",@"CVLONGINT",@"CVS",@"CVSHORT", _
@"DATA",@"DATE",@"DATEADD",@"DATEDIFF",@"DATEPART",@"DATESERIAL", _
@"DATEVALUE",@"DAY",@"DEALLOCATE",@"DECLARE",@"DEFBYTE",@"DEFDBL", _
@"DEFINT",@"DEFLNG",@"DEFLNGINT",@"DEFSHORT",@"DEFSNG",@"DEFSTR", _
@"DEFUBYTE",@"DEFUINT",@"DEFULNGINT",@"DEFUSHORT",@"DELETE", _
@"DESTRUCTOR",@"DIM",@"DIR",@"DO",@"DRAW",@"DYNAMIC", _
@"DYLIBFREE",@"DYLIBLOAD",@"DYLIBSYMBOL", _
@"ELSE",@"ELSEIF",@"ENCODING",@"END",@"ENUM",@"ENVIRON",@"ESCAPE", _
@"EOF",@"EQV",@"ERASE",@"ERFN",@"ERL",@"ERMN",@"ERR",@"ERROR", _
@"EXEC",@"EXEPATH",@"EXIT",@"EXP",@"EXPLICIT",@"EXPORT",@"EXTERN", _
@"FALSE",@"FBOOLEAN",@"FIELD",@"FILEATTR",@"FILECOPY",@"FILEDATETIME", _
@"FILEEXISTS",@"FILELEN",@"FIX",@"FLIP",@"FOR",@"FORMAT",@"FRAC", _
@"FRE",@"FREEFILE",@"FUNCTION", _
@"GET",@"GETJOYSTICK",@"GETKEY",@"GETMOUSE",@"GOSUB",@"GOTO", _
@"HEX",@"HIBYTE",@"HIWORD",@"HOUR", _
@"IF",@"IIF",@"IMAGECONVERTROW",@"IMAGECREATE",@"IMAGEDESTROY",@"IMP", _
@"IMPORT",@"INKEY",@"INP",@"INPUT",@"INPUT$",@"INSTR",@"INSTRREV", _
@"INT",@"IS",@"ISDATE", _
@"KILL", _
@"LBOUND",@"LCASE",@"LEFT",@"LEN",@"LET",@"LIB",@"LPT",@"LINE", _
@"LOBYTE",@"LOC",@"LOCAL",@"LOCATE",@"LOCK",@"LOF",@"LOG", _
@"LOOP",@"LOWORD",@"LPOS",@"LPRINT",@"LSET",@"LTRIM", _
@"MID",@"MINUTE",@"MKD",@"MKDIR",@"MKI",@"MKL",@"MKLONGINT",@"MKS", _
@"MKSHORT",@"MOD",@"MONTH",@"MONTHNAME",@"MULTIKEY",@"MUTEXCREATE", _
@"MUTEXDESTROY",@"MUTEXLOCK",@"MUTEXUNLOCK", _
@"NAME",@"NAMESPACE",@"NOKEYWORD",@"NEXT",@"NEW",@"NOT",@"NOW", _
@"OCT",@"OFFSETOF",@"ON",@"ONCE",@"OPEN",@"OPTION",@"OPERATOR", _
@"OR",@"ORELSE",@"OUT",@"OUTPUT",@"OVERLOAD", _
@"PAINT",@"PALETTE",@"PASCAL",@"PCOPY",@"PEEK",@"PIPE",@"PMAP", _
@"POINT",@"POINTER",@"POKE",@"POS",@"PRESERVE",@"PRESET",@"PRINT", _
@"PRIVATE",@"PROCPTR",@"PROPERTY",@"PROTECTED",@"PSET",@"PTR", _
@"PUBLIC",@"PUT", _
@"RANDOM",@"RANDOMIZE",@"READ",@"REALLOCATE",@"REDIM",@"REM", _
@"RESET",@"RESTORE",@"RESUME",@"RETURN",@"RGB",@"RGBA",@"RIGHT", _
@"RMDIR",@"RND",@"RSET",@"RTRIM",@"RUN", _
@"SADD",@"SCOPE",@"SCRN",@"SCREEN",@"SCREENCOPY",@"SCREENCONTROL", _
@"SCREENEVENT",@"SCREENINFO",@"SCREENGLPROC",@"SCREENLIST", _
@"SCREENLOCK",@"SCREENPTR",@"SCREENRES",@"SCREENSET",@"SCREENSYNC", _
@"SCREENUNLOCK",@"SECOND",@"SEEK",@"SELECT",@"SETDATE",@"SETENVIRON", _
@"SETMOUSE",@"SETTIME",@"SGN",@"SHARED",@"SHELL",@"SIN", _
@"SIZEOF",@"SLEEP",@"SPACE",@"SPC",@"SQR",@"STATIC", _
@"STDCALL",@"STEP",@"STOP",@"STR",@"STRING",@"STRPTR",@"SUB", _
@"SWAP",@"SYSTEM",@"SHR",@"SHL", _
@"TAB",@"TAN",@"THEN",@"THIS",@"THREADCREATE",@"THREADWAIT", _
@"TIME",@"TIMESERIAL",@"TIMEVALUE",@"TIMER",@"TO",@"TRANS", _
@"TRIM",@"TRUE",@"TYPE", _
@"UBOUND",@"UCASE", _
@"UNION",@"UNLOCK",@"UNSIGNED",@"UNTIL",@"USING", _
@"VA_ARG",@"VA_FIRST",@"VA_NEXT",@"VAL",@"VALLNG",@"VALINT", _
@"VALUINT",@"VALULNG",@"VAR",@"VARPTR",@"VIEW", _
@"WAIT",@"WBIN",@"WCHR",@"WEEKDAY",@"WEEKDAYNAME",@"WEND", _
@"WHILE",@"WHEX",@"WIDTH",@"WINDOW",@"WINDOWTITLE",@"WINPUT", _
@"WITH",@"WOCT",@"WRITE",@"WSPACE",@"WSTR",@"WSTRING", _
@"XOR",@"YEAR", _
@"AS",@"BYREF",@"BYVAL", _
@"BYTE",@"UBYTE",@"SHORT",@"USHORT",@"LONG",@"ULONG", _
@"INTEGER",@"UINTEGER",@"LONGINT",@"ULONGINT", _
@"SINGLE",@"DOUBLE",@"ZSTRING"}


' Search for a key, output error message if not exist (value = 0)
' Sucht Key _K_ und meldet Fehler wenn nicht vorhanden (Value = 0)
' ★キーを検索し、存在しない場合はエラーメッセージを出力(値= 0)★
' ★演算子 * (の値)
' g_hash_table_lookup (GHashTable *hash_table, gconstpointer key)
#DEFINE check(_T_, _K_) _
   If g_hash_table_lookup(_T_, _K_) = 0 Then _
      Print "エラー、キーワードが見つかりません: "; *_K_

' Callback for output: value first, then key
' Callback zur Ausgabe: erst Value, dann Key
' 出力のコールバック:キーと値
Sub HashOut Cdecl( _
   ByVal key As gpointer, _
   ByVal value As gpointer, _
   ByVal user_data As gpointer)
   ' key を Ptr に型変換して、値とともに表示
   Print *Cast(ZString Ptr, key),GPOINTER_TO_INT(value)
End Sub


' ***** main / Hauptprogramm *****
Dim i As Integer

' Create new hash table / Neue HashTable erstellen
' ★新しいハッシュテーブルを作成★
'                     演算子 @ (のアドレス)
' g_str_hash (gconstpointer v) 文字列をハッシュ値に変換します。
' g_str_equal (gconstpointer v1, gconstpointer v2) 2つの文字列をバイト毎に比較して、等しい場合は TRUE を返します。

Var table = g_hash_table_new(@g_str_hash, @g_str_equal)

' Insert keys and values / Fuellen mit den Keys und Values
' ★キーと値を挿入★
Print "キーの件数:"; UBound(FBKEY); " 何かキー入力で、テーブルに登録したキーと、キーのアドレス、値を表示します。"
Sleep
For i As Integer = 0 To UBound(FBKEY)
   g_hash_table_insert(table, FBKEY(i), GINT_TO_POINTER(i + 1000))
   Print *FBKEY(i), FBKEY(i), i + 1000
Next i

Print 
Print "最後のキーの値:"; g_hash_table_lookup(table, FBKEY(UBound(FBKEY))); 'キーの存在を確認
Print " 何かキー入力で、登録した全てのキーと値を表示します。"
Sleep

' Replace an existing key / Ersetzen eines vorhandenen Keys (6 x)
' 既存のキーを置き換える
' 注:g_hash_table_insert は古い値を破壊する
'     g_hash_table_replace は古い値とキーを破壊する
For i = 0 To 5
   'g_hash_table_insert (GHashTable *hash_table, gpointer key, gpointer value)
   g_hash_table_insert(table, @"UBYTE", GINT_TO_POINTER(i - 999999))
Next i

' Check if all keys are available / Pruefen ob alle Keys vorhanden
' すべてのキーが使えるかどうか確認
For i As Integer = 0 To UBound(FBKEY)
   check(table, FBKEY(i))
Next i

' Print all keys and values / Alle Keys und Values ausgeben
' すべてのキーと値を表示する
' g_hash_table_foreach (GHashTable *hash_table, GHFunc func:キーと値のペアを引数にして呼び出す関数, gpointer user_data:func に引き渡すユーザ・データ)
' (*GHFunc) (gpointer key, gpointer value, gpointer user_data)
g_hash_table_foreach(table, @HashOut, 0)

' Search for a non existing key / Suche einen nicht vorhandenen Key
Print
Print "既存のキー以外を検索した結果の表示例です。何かキー入力で終了します。"
Print
'     演算子 @ (のアドレス)
check(table, @"FreeBasic-Portal")

' Destroy table, release memory / Tabelle aufloesen, Speicher freigeben
' テーブルを破棄、メモリを解放
g_hash_table_unref(table)

Sleep
ページの頭に戻る

キー生成での干渉の有無チェック

 Hash Table で生成するキーの衝突 (collision) を検証するプログラムです。
 英字4文字の組合せ 456,976件(26^4)のテーブルを作成します。

UsageTestHashTable_glib2 JP.bas
'Fast text search (hash table / GLib)
'by TJF ≫ Sep 26, 2011 12:16 
'https://www.freebasic.net/forum/viewtopic.php?f=7&t=18558&p=163893&hilit=glib+hash#p163893

' See for details
' http://developer.gnome.org/glib/2.28/glib-Hash-Tables.html

#Include Once "glib.bi"

Dim Shared KeyArray(500000) As String     
Dim Shared As Integer Counter, i, j, k, l
Dim As Single t1,t2
Dim collisions As Integer
Dim accumulation As Integer


' ***** main / Hauptprogramm *****

' Create new hash table / Neue HashTable erstellen
' ★新しいハッシュテーブルを作成★
' g_hash_table_new (GHashFunc hash_func, GEqualFunc key_equal_func)
'                            演算子 @ (のアドレス)
' g_str_hash (gconstpointer v) 文字列をハッシュ値に変換します。
' g_str_equal (gconstpointer v1, gconstpointer v2) 2つの文字列をバイト毎に比較して、等しい場合は TRUE を返します。

Var HashTable = g_hash_table_new(@g_str_hash, @g_str_equal)

t1=Timer
Counter = 0
collisions = 0
accumulation = 0

' Insert keys and values / Fuellen mit den Keys und Values
' ★キーと値を挿入★

'g_hash_table_insert(HashTable, @!"AAC\0", GINT_TO_POINTER(987654))
'g_hash_table_insert(HashTable, @!"ZAC\0", GINT_TO_POINTER(876543))

For i = Asc("A") To Asc("Z")
   Print Chr(i) + Space(1);
   For j = Asc("A") To Asc("Z")
      For k = Asc("A") To Asc("Z")
         For l = Asc("A") To Asc("Z")
            Counter = Counter + 1
            'ReDim Preserve KeyArray(Counter + 1)
            
            'KeyArray(Counter) =  Chr(i) + Chr(j) + Chr(k) + Chr(l)
            KeyArray(Counter) = Chr(i, j, k, l)
            
            'StrPtr(KeyArray(Counter)) returns a ZSTRING PTR to the content of the STRING.
            If g_hash_table_lookup(HashTable, StrPtr(KeyArray(Counter))) = 0 Then
               accumulation = accumulation + 1
               'g_hash_table_insert (GHashTable *hash_table, gpointer key, gpointer value)
               'GLib には、int から gpointer へ正確にキャストする GINT_TO_POINTER というマクロと、
               'gpointer から int へ正確にキャストする GPOINTER_TO_INT というマクロが定義されています。
               g_hash_table_insert(HashTable, StrPtr(KeyArray(Counter)), GINT_TO_POINTER(Counter))
            Else
               'キーの重複が発生したら表示する
               'Display when duplicate keys occur
               collisions += 1
               Print
               Print g_hash_table_lookup(HashTable, StrPtr(KeyArray(Counter))) , Counter, KeyArray(Counter)
               Sleep
            End If
         Next l
      Next k
   Next j
Next i

t2=Timer

Print
Print
Print "最終登録のキー:";KeyArray(accumulation), "キーに与えた値:" ;g_hash_table_lookup(HashTable, StrPtr(KeyArray(accumulation)))
    
Print

Print "終了しました。 KeyArray = "; KeyArray(Counter); "      処理件数 = 26^4(456,976) = "; Counter
'Print "Finished. Last KeyArray = "; KeyArray(Counter); "      Counter = 26^4(456,976) = "; Counter
Print "衝突件数 = ";collisions," 登録件数 = ";accumulation
'Print "collisions = ";collisions," accumulation = ";accumulation
Print
Print "所要秒数 = ";t2 - t1
'Print "Required seconds = ";t2 - t1

' Destroy table, release memory / Tabelle aufloesen, Speicher freigeben
' テーブルを破棄、メモリを解放
g_hash_table_unref(HashTable)

Sleep
ページの頭に戻る

キー生成と連想配列生成結果の照合チェック

 11,881,376 件もの文字列データを連想配列に登録して、登録結果を照合チェックするプログラム例です。
 私の PC で、連想配列の生成に 4秒、照合チェックに 5秒と超高速で処理できました!!

UsageTestHashTable_glib2 JP - 2-2.bas
'Fast text search (hash table / GLib)
'by TJF ≫ Sep 26, 2011 12:16 
'https://www.freebasic.net/forum/viewtopic.php?f=7&t=18558&p=163893&hilit=glib+hash#p163893

' See for details
' http://developer.gnome.org/glib/2.28/glib-Hash-Tables.html

#Include Once "glib.bi"

Dim Shared KeyArray(12000000) As String     
Dim Shared As Integer Counter, i, j, k, l, m
Dim As Single t1,t2,t3
Dim collisions As Integer
Dim accumulation As Integer
Dim KeyString As String


' ***** main / Hauptprogramm *****

' Create new hash table / Neue HashTable erstellen
' ★新しいハッシュテーブルを作成★
' g_hash_table_new (GHashFunc hash_func, GEqualFunc key_equal_func)
'                            演算子 @ (のアドレス)
' g_str_hash (gconstpointer v) 文字列をハッシュ値に変換します。
' g_str_equal (gconstpointer v1, gconstpointer v2) 2つの文字列をバイト毎に比較して、等しい場合は TRUE を返します。

Var HashTable = g_hash_table_new(@g_str_hash, @g_str_equal)

t1=Timer
Counter = 0
collisions = 0
accumulation = 0

' Insert keys and values / Fuellen mit den Keys und Values
' ★キーと値を挿入★

'g_hash_table_insert(ArrayItemID, @!"AAC\0", GINT_TO_POINTER(987654))
'g_hash_table_insert(ArrayItemID, @!"ZAC\0", GINT_TO_POINTER(876543))

For i = Asc("A") To Asc("Z")
   Print Chr(i) + Space(1);
   For j = Asc("A") To Asc("Z")
      For k = Asc("A") To Asc("Z")
         For l = Asc("A") To Asc("Z")
            For m = Asc("A") To Asc("Z")
               Counter = Counter + 1
               
               'KeyString = Chr(i, j, k, l)
               KeyString = Chr(i, j, k, l, m)
               KeyArray(Counter) = KeyString

               If g_hash_table_lookup(HashTable, SAdd(KeyArray(Counter))) = 0 Then
                  accumulation = accumulation + 1
                  'g_hash_table_insert (GHashTable *hash_table, gpointer key, gpointer value)
                  'GLib には、int から gpointer へ正確にキャストする GINT_TO_POINTER というマクロと、
                  'gpointer から int へ正確にキャストする GPOINTER_TO_INT というマクロが定義されています。
                  g_hash_table_insert(HashTable, SAdd(KeyArray(Counter)), GINT_TO_POINTER(Counter))  '★3★★★★★★★★★★★
                  '上で HashTable を作るときに配列を使う点がポイント。何故か文字列データを直接使うと連想配列ができない
                  'Print KeyString,g_hash_table_lookup(HashTable, SAdd(KeyString))
                  'Sleep

                  'Print KeyString, g_hash_table_lookup(HashTable, SAdd(KeyString))
                  'Sleep
               Else
                  'キーの重複が発生したら表示する
                  'Display when duplicate keys occur
                  collisions += 1
                  Print
                  Print g_hash_table_lookup(HashTable, SAdd(KeyArray(Counter))) , Counter, KeyArray(Counter)
                  Sleep
               End If
            Next m
         Next l
      Next k
   Next j
Next i

t2=Timer

Print
Print
Print "最終登録のキー:";KeyString, "キーに与えた値:" ;g_hash_table_lookup(HashTable, SAdd(KeyString))
Print

Print "終了しました。 KeyString = "; KeyString; "      処理件数 = 26^5(11,881,376) = "; Counter
'Print "Finished. Last ItemID = "; ItemID(Counter); "      Counter = 26^4(456,976) = "; Counter
Print "衝突件数 = ";collisions," 登録件数 = ";accumulation
'Print "collisions = ";collisions," accumulation = ";accumulation
Print
Print "登録所要秒数 = ";t2 - t1
'Print "Required seconds = ";t2 - t1
Print


'****************************************************************
'Hash によるデータの照合チェック
'****************************************************************

For i = Asc("A") To Asc("Z")
   Print Chr(i)+Space(1);
   For j = Asc("A") To Asc("Z")
      For k = Asc("A") To Asc("Z")
         For l = Asc("A") To Asc("Z")
            For m = Asc("A") To Asc("Z")
               KeyString = Chr(m, l, i, j, k)
               'KeyString = Chr(i, j, k, l)
               'Print KeyString, g_hash_table_lookup(HashTable, SAdd(KeyString)) '上で HashTable を作るときに配列を使う点がポイント
               'Sleep
               Counter = g_hash_table_lookup(HashTable, SAdd(KeyString))
               If KeyString <> KeyArray(Counter) Then    ' Hash を使ってデータを照合
                  'データの不整合が発生したら表示する
                  Print
                  Print "Inconsistent data", KeyString, g_hash_table_lookup(HashTable, SAdd(KeyString))
                  Sleep
               End If
            Next m
         Next l
      Next k
   Next j
   Print "Consistent data", KeyString, g_hash_table_lookup(HashTable, SAdd(KeyString))
Next i

Print
t3=Timer
Print "照合所要秒数 = ";t3 - t2
Print
Print "合計所要秒数 = ";t3 - t1

' Destroy table, release memory / Tabelle aufloesen, Speicher freigeben
' テーブルを破棄、メモリを解放
g_hash_table_unref(HashTable)

Sleep
ページの頭に戻る

品目マスタと品目オーダを照合

 品目マスタ(15,000件)と品目オーダ(50,000件)を照合して、品目別の受注数量・金額・重量を集計する事例です。
 私のパソコン環境で、1秒以下で処理が完了しました。
 (このプログラムは、Excelマクロで作成して利用していたものを FreeBASIC に移植したものです。)

サンプルデータを作成するための地域ファイル → region.txt
サンプルデータ(品目マスタと品目オーダ)を作成するプログラム → PrepareTestData00Jp.bas

OrderSortingHashTable_glib01.bas
'品目マスタと品目オーダを照合して品目別受注量を集計

'Fast text search (hash table / GLib)
'by TJF ≫ Sep 26, 2011 12:16 
'https://www.freebasic.net/forum/viewtopic.php?f=7&t=18558&p=163893&hilit=glib+hash#p163893

' See for details
' http://developer.gnome.org/glib/2.28/glib-Hash-Tables.html

#Include Once "glib.bi"                                  '★1★★★★★★★★★★★

' Create new hash table / Neue HashTable erstellen
' ★新しいハッシュテーブルを作成★
' g_hash_table_new (GHashFunc hash_func, GEqualFunc key_equal_func)
'                            演算子 @ (のアドレス)
' g_str_hash (gconstpointer v) 文字列をハッシュ値に変換します。
' g_str_equal (gconstpointer v1, gconstpointer v2) 2つの文字列をバイト毎に比較して、等しい場合は TRUE を返します。

Var MasterItemID = g_hash_table_new(@g_str_hash, @g_str_equal)  '★2★★★★★★★★★★★
Var OrderItemID = g_hash_table_new(@g_str_hash, @g_str_equal)   '★2★★★★★★★★★★★

Dim STARTT As Long
Dim ENDTIME As Long
Dim Minut As Integer
Dim Shared ItemID(20000) As String
Dim Shared OrderLine(20000) As String
Dim Shared OrderItem As String
Dim Shared Region As String
Dim Shared Price As String
Dim Shared Weight As String
Dim Shared Counter As Integer
Dim Shared collisions As Integer
Dim i As Integer
Dim QuantityString As String
Dim Amount As String
Dim Dimension As Integer
Dim cellString As String
   
Dim file_name As String 
Dim file_num As Integer
Dim CharacterString As String
Dim Regions As Integer
Dim ItemMasterNo As Integer
Dim IntegratedNo As Integer
Dim Orders As Integer

Dim Shared ItemMasterArray(20000,3) As String 'Region, price, weight
Dim Shared IntegratedOrderArray(20000,5) As String 'Region, ItemID, Quantity, Amount, Weight
Dim Shared SortArray(20000) As String

'****************************************************************
'****************************************************************

'★★★多次元配列を複数列を使ってソート★★★
'How to sort datasheet (x-dim array) by a few columns (at once in 1 loop) ?
'by badidea ≫ Dec 15, 2019 21:54 
'https://www.freebasic.net/forum/viewtopic.php?f=3&t=27993&start=30#p266914

'by fxm ≫ Jun 25, 2020 6:49 
'https://www.freebasic.net/forum/viewtopic.php?f=3&p=273475&sid=547977eb5d3cc9cf3e6ee7323616cff3#p273475

#Include "crt/stdlib.bi"
#Include "string.bi"

'------------------------------- class: row_type -------------------------------

Type row_type
   Dim As String col(Any)
   Declare Operator Cast () As String
End Type

Operator row_type.cast () As String
   Dim As String tempStr
   For i As Integer = 1 To UBound(col)
      If i = 1 Then tempStr &= col(i) Else tempStr &= !"\t" & col(i) 
   Next
   Return tempStr
End Operator

'------------------------------ class: sort_type -------------------------------

Type sort_type
   Dim As Short column
   Dim As Short direction
   Declare Constructor()
   Declare Constructor(column As Short, direction As Short)
End Type

'a stupid constructor
Constructor sort_type()
   this.column = 0
   this.direction = 0
End Constructor

'another stupid constructor
Constructor sort_type(column As Short, direction As Short)
   this.column = column
   this.direction = direction
End Constructor

'------------------------------ class: data_type -------------------------------

Type data_type
   Static As sort_type sortOrder(1 To 3)
   Dim As Integer numRows, numCols
   Dim As row_type row(Any)
   Declare Constructor(numRows As Integer, numPivotMS As Integer)
   Declare Destructor()
   Declare Sub copyTo(dst As data_type)
   Declare Sub sort(sort1st As sort_type, sort2nd As sort_type, sort3rd As sort_type)
   Declare Static Function qSortCallback Cdecl(pRow1 As row_type Ptr, pRow2 As row_type Ptr) As Long
End Type

Dim As sort_type data_type.sortOrder(1 To 3)

Constructor data_type(numRows As Integer, numCols As Integer)
   ReDim row(numRows)
   this.numCols = numCols
   For iRow As Integer = 1 To numRows
      ReDim (row(iRow).col)(numCols) 'weird syntax, compiler wants the extra ( )
   Next
End Constructor

Destructor data_type()
   For iRow As Integer = 1 To numRows
      Erase row(iRow).col
   Next
   Erase row
End Destructor

Sub data_type.sort(sort1st As sort_type, sort2nd As sort_type, sort3rd As sort_type)
   'disable invalid sort filters
   sortOrder(1) = IIf(sort1st.column < 1 Or sort1st.column >= numCols, sort_type(0,0), sort1st)
   sortOrder(2) = IIf(sort2nd.column < 1 Or sort2nd.column >= numCols, sort_type(0,0), sort2nd)
   sortOrder(3) = IIf(sort3rd.column < 1 Or sort3rd.column >= numCols, sort_type(0,0), sort3rd)
   qsort(@row(1), UBound(row), SizeOf(row_type), CPtr(Any Ptr, @qSortCallback))
End Sub

Function data_type.qSortCallback Cdecl(pRow1 As row_type Ptr, pRow2 As row_type Ptr) As Long
   For i As Integer = 1 To 3
      With sortOrder(i)
         Select Case .direction
         Case +1
            If pRow1->col(.column) < pRow2->col(.column) Then Return -1
            If pRow1->col(.column) > pRow2->col(.column) Then Return +1
         Case -1
            If pRow1->col(.column) > pRow2->col(.column) Then Return -1
            If pRow1->col(.column) < pRow2->col(.column) Then Return +1
         Case Else
            'skip, including direction = 0
         End Select
      End With
   Next
   Return 0
End Function


'****************************************************************
'****************************************************************
   'Print "Read ""ItemMaster.csv"" and store each line in MasterItemID, ItemID()."
   Print """ItemMaster.csv"" を読み込んで MasterItemID, ItemID() に登録"
'****************************************************************
   Print
   
   file_name = "ItemMaster.csv"
   file_num = FreeFile( )                 '' 有効なファイル番号を検索します

   '' ファイルを開きます。そして、ファイル番号をそれに結び付けます。エラーが有れば、抜けます。
   If( Open( file_name For Input As #file_num ) ) Then
      Print "ERROR: 開こうとしたファイル名 " ; file_name
      Sleep
      End -1
   End If
   
   Counter = 0
   collisions = 0

   Do Until Eof( file_num )               '' ファイルの端に達するまで、繰り返します。
      Counter = Counter + 1   
      ItemID(Counter) ="": Region ="": Price ="" : Weight =""

      Line Input #file_num, CharacterString           '' テキストの行を読みます。
      
      ItemID(Counter) = Left(CharacterString,3)
      Region = Mid(CharacterString,6,InStrRev(CharacterString,"""")-6)
      Price  = Mid(CharacterString,InStrRev(CharacterString,"""")+2,InStrRev(CharacterString,",")-InStrRev(CharacterString,"""")-2)
      Weight = Right(CharacterString,Len(CharacterString)-InStrRev(CharacterString,","))

      If g_hash_table_lookup(MasterItemID, StrPtr(ItemID(Counter))) = 0 Then                   '★4★★★★★★★★★★★

         'g_hash_table_insert (GHashTable *hash_table, gpointer key, gpointer value)
         'GLib には、int から gpointer へ正確にキャストする GINT_TO_POINTER というマクロと、
         'gpointer から int へ正確にキャストする GPOINTER_TO_INT というマクロが定義されています。
         g_hash_table_insert(MasterItemID, StrPtr(ItemID(Counter)), GINT_TO_POINTER(Counter))  '★3★★★★★★★★★★★
         ItemMasterArray(Counter,1) = Region
         ItemMasterArray(Counter,2) = Price
         ItemMasterArray(Counter,3) = Weight   
      Else
         'キーの重複が発生したら表示する
         'Display when duplicate keys occur
         collisions += 1
         Print
         Print g_hash_table_lookup(MasterItemID, StrPtr(ItemID(Counter))) , Counter, ItemID(Counter)
         Sleep
      End If
      
   Loop
   'Print "Numbers of Item Master = ";Counter
   Print "品目マスタの件数 = ";Counter
   Print 
   'Print "Contents of the last Item Master : ";CharacterString  ' 画面に最終行を出力します。
   Print "品目マスタの最後のデータの内容 : ";CharacterString  ' 画面に最終行を出力します。
   Print ItemID(Counter) , Region , Price ,  Weight 
   Print
   
   Close #file_num                        '' ファイル番号を通したファイルを閉じます。

'****************************************************************
'****************************************************************
   'Print "Read ""OrderList.csv"" and compare it with MasterItemID to aggregate price and weight by item and region."
   Print """OrderList.csv"" を読み込み、MasterItemID と照合して、品目毎に数量・金額・重量を IntegratedOrderArray に集計します。"
'****************************************************************
   Print
   
   Open "OrderItemError.csv"  For Output As #1

   file_name = "OrderList.csv"
   file_num = FreeFile( )                 '' 有効なファイル番号を検索します

   '' ファイルを開きます。そして、ファイル番号をそれに結び付けます。エラーが有れば、抜けます。
   If( Open( file_name For Input As #file_num ) ) Then
      Print "ERROR: 開こうとしたファイル名 " ; file_name
      Sleep
      End -1
   End If
   
   Counter = 0
   Orders  = 0
   
   STARTT=Val(Left(Time,2))*3600+Val(Mid(Time,4,2))*60+Val(Right(Time,2))

   Do Until Eof( file_num )               '' ファイルの端に達するまで、繰り返します。
      CharacterString = "" : OrderItem = "" : QuantityString = ""

      Line Input #file_num, CharacterString           '' テキストの行を読みます。
      Orders  = Orders + 1
      OrderItem = Left(CharacterString,3)
      QuantityString = Right(CharacterString,Len(CharacterString)-InStrRev(CharacterString,","))
      
      If g_hash_table_lookup(MasterItemID, StrPtr(OrderItem)) = 0 Then                   '★4★★★★★★★★★★★
         'Print OrderItem, g_hash_table_lookup(MasterItemID, StrPtr(OrderItem))
         'Sleep
      
         'エラー出力
         Print #1, CharacterString         
      Else
         
         ItemMasterNo = g_hash_table_lookup(MasterItemID, StrPtr(OrderItem))
         
         'ItemMasterArray(20000,3)      'Region, price, weight
         'IntegratedOrderArray(20000,5) 'Region, ItemID, Quantity, Amount, Weight
         
         Region = ItemMasterArray(ItemMasterNo,1)
         Amount = Str(Val(QuantityString)*Val(ItemMasterArray(ItemMasterNo,2)))
         Weight = Str(Val(QuantityString)*Val(ItemMasterArray(ItemMasterNo,3)))

         
         If g_hash_table_lookup(OrderItemID, StrPtr(OrderItem)) = 0 Then                   '★4★★★★★★★★★★★
            Counter = Counter + 1
            OrderLine(Counter) = OrderItem
            g_hash_table_insert(OrderItemID, StrPtr(OrderLine(Counter)), GINT_TO_POINTER(Counter)) '★3★★★★★★★★★★★

            IntegratedOrderArray(Counter,1) = Region
            IntegratedOrderArray(Counter,2) = OrderItem
            IntegratedOrderArray(Counter,3) = QuantityString
            IntegratedOrderArray(Counter,4) = Amount
            IntegratedOrderArray(Counter,5) = Weight
            'Print Counter,Region,ItemID,QuantityString,Amount,Weight
            'sleep
   
         Else
            IntegratedNo = g_hash_table_lookup(OrderItemID, StrPtr(OrderItem))
            
            IntegratedOrderArray(IntegratedNo,3) = Str(Val(IntegratedOrderArray(IntegratedNo,3))+Val(QuantityString))
            IntegratedOrderArray(IntegratedNo,4) = Str(Val(IntegratedOrderArray(IntegratedNo,4))+Val(Amount))
            IntegratedOrderArray(IntegratedNo,5) = Str(Val(IntegratedOrderArray(IntegratedNo,5))+Val(Weight))
            'Print IntegratedNo,ItemID,QuantityString,Amount,Weight
            'sleep
   
         End If
      End If
      
   Loop
   'Print "Number of orders in the list : ";Orders
   Print "リスト中のオーダ件数 : ";Orders
   Print
   ENDTIME = Val(Left(Time,2))*3600+Val(Mid(Time,4,2))*60+Val(Right(Time,2))
   Minut=(ENDTIME-STARTT)\60
   'Print Using "processing time: ## minutes ## seconds"; Minut; (ENDTIME-STARTT)-Minut*60
   Print Using "処理時間: ## 分 ## 秒"; Minut; (ENDTIME-STARTT)-Minut*60
   Print
   
   Close #1
   Close #file_num                        '' ファイル番号を通したファイルを閉じます。

'****************************************************************
'****************************************************************
' Destroy table, release memory / Tabelle aufloesen, Speicher freigeben
' テーブルを破棄、メモリを解放
g_hash_table_unref(MasterItemID)                                    '★5★★★★★★★★★★★
g_hash_table_unref(OrderItemID)                                     '★5★★★★★★★★★★★

'****************************************************************
'****************************************************************
   'Print "Sort the totaled results by Region and ItemID."
   Print "集計結果を、地域順・品目コード順に並び替えます。"
'****************************************************************
   Print


Dim As Integer numRows = Counter, numCols = 5

Var myData = data_type(numRows, numCols)

For iRow As Integer = 1 To Counter
   With myData.row(iRow)
      For iCol As Integer = 1 To UBound(.col)
         .col(iCol) = IntegratedOrderArray(iRow,iCol)
      Next
   End With
Next iRow

myData.sort(sort_type(1, +1), sort_type(2, +1), sort_type(0, 0))

For iRow As Integer = 1 To Counter
   With myData.row(iRow)
      For iCol As Integer = 1 To UBound(.col)
         IntegratedOrderArray(iRow,iCol) = .col(iCol)
      Next
   End With
Next iRow


'****************************************************************
'****************************************************************
   'Print "Out put the sorted results to ""OrderSorting.csv"""
   Print "集計結果を ""OrderSorting.csv"" として出力します。"
'****************************************************************
   Print

   'IntegratedOrderArray(20000,5) 'Region, ItemID, Quantity, Amount, Weight
   Open "OrderSorting.csv"  For Output As #1
      For i = 1 To Counter
         CharacterString = ""
         For Dimension =1 To 5
            cellString = IntegratedOrderArray(i,Dimension)
            If Dimension = 1 Then
               cellString = """" & cellString & """"
            EndIf
            If Dimension > 1 Then
               cellString = "," & cellString
            EndIf
            CharacterString = CharacterString & cellString
         Next Dimension

         Print #1, CharacterString

      Next i
   Close #1
   

'****************************************************************
'****************************************************************
   'Print "Output of the sorted order aggregate has been completed."
   Print "集計結果のリスト出力が完了しました。"
   Print "*******************************************************"
   'Print "Please enter any key to exit."
   Print "何かキー入力すると終了します。"
Sleep
ページの頭に戻る
補足 に戻る
←リンク元に戻る プログラム開発関連に戻る
ページ歴史:2020-07-12
日本語翻訳:WATANABE Makoto、原文著作者:Thomas.Freiherr

ホームページのトップに戻る

表示-非営利-継承