試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

GLibのハッシュテーブルをVala言語で用いる(後半)

GLibのハッシュテーブルをVala言語で用いる(前半)」の続き。見出し「データの出し入れ」の中における続きとなる。

データの取り出し

ハッシュテーブルオブジェクトからキーを指定してデータを取り出すにはハッシュテーブルオブジェクトのメンバ関数lookup()にキーのデータを入れる。
全てのデータを取り出したい場合は

  • HashTableIter構造体を用いた方法
  • ハッシュテーブルオブジェクトのfor_each()を用いた方法
  • ハッシュテーブルオブジェクトのget_keys()でキーの一覧を得る方法

の3つの方法がある。
最初の方法はテーブル内のキーと値の型を指定したものを用意した上でnext()をtrueを返す間繰り返し呼び出し続けてキーと値を取り出していく。下はキーにstring,値にintの型を用いるハッシュテーブルに対するHashTableIterの例。

[キーの型] key;
[値の型] val;
GLib.HashTableIter<string,int> iter = GLib.HashTableIter<string,int> ([ハッシュテーブルオブジェクト]);
while (iter.next (out key, out val))
{
  ...
}

2番目の方法は2つの引数(キーと値)を受け取る関数を用意するもので引数は両方void *型なので、それぞれを用いる際には型変換する必要がある。

[ハッシュテーブルオブジェクト].for_each ((key, val) =>
{
  // key,valは型変換して用いる
});

3番目の方法はキーの一覧を得た上で、それぞれのキーに対する値をlookup()で取り出す。get_keys()の結果をforeach構文で処理すると書きやすい。型変換も不要。

foreach ([キーの型(「var」でも可)] key in [ハッシュテーブルオブジェクト].get_keys ())
{
  // 「[ハッシュテーブルオブジェクト].lookup (key)」でそれぞれのキーに対応する値を得る
}

例1

内容は簡単なデータの出し入れで、全てのデータの取り出しもしている。
[任意]ファイル名: hashtabletest.vala

/*
 * ハッシュテーブルのテスト
 *
 * valac --pkg posix hashtabletest.vala -o hashtabletest
 */

using Posix;

namespace HashTableTest
{
  int
  main (string[] args)
  {
    string key;
    int val;
    // HashTableは「キー」とそれに対応した「値」をペアにして格納できるオブジェクト
    // キーと値の型はそれぞれ決めることができ
    // オブジェクトの型の中に「<[キーの型],[値の型]>」として記述する
    // 今回はキーを文字列,値を整数とした
    // コンストラクタ引数はキーの型に応じたハッシュ生成関数と
    // 比較関数(内容が一致したときにtrue,一致しないときにfalseを返すように記述されたもの)を指定
    GLib.HashTable<string,int> fruits = new GLib.HashTable<string,int> (GLib.str_hash, GLib.str_equal);
    /*
    // 比較の関数を自前で記述する場合は一致したときにtrue,一致しない場合にfalseを返すようにする
    GLib.HashTable<string,int> fruits = new GLib.HashTable<string,int> (GLib.str_hash, (a, b) =>
    {
      // 引数はvoid *型なので文字列としての比較のために型変換をする
      return ((string) a == (string) b);
    });
    */

    // HashTableIterはHashTableの中の項目を指し示して項目を移動できる構造体
    GLib.HashTableIter<string,int> iter;

    // insert()やreplace()はキーと値のペアを追加する
    // キーが衝突した場合は新しいもので置き換えられる
    fruits.insert ("apple", 100);
    fruits.insert ("orange", 60);
    fruits.insert ("cherry", 40000);
    fruits.replace ("cherry", 40);  // 40000というデータはここでなくなる
//    fruits.insert ("cherry", 40);  // この例ではreplace()と挙動は同じ

    // キー指定による個別の取り出し
    print ("lookup: cherry=%d\n", fruits.lookup ("cherry"));

    // HashTableIterによる取り出し
    iter = GLib.HashTableIter<string,int> (fruits);
    while (iter.next (out key, out val))
    {
      print ("iter: %s=%d\n", key, val);
    }

    // for_each()による取り出し
    // 引数はvoid *型なので型変換する
    fruits.for_each ((key, val) =>
    {
      print ("for_each: %s=%d\n", (string) key, (int) val);
    });

    // キーの一覧を取得してそのそれぞれの値を取り出す方法
    // 最も書きやすい
    foreach (string key in fruits.get_keys ())
    {
      print ("get_keys/foreach: %s=%d\n", key, fruits.lookup (key));
    }

    return EXIT_SUCCESS;
  }
}

下は実行例。

lookup: cherry=40
iter: apple=100
iter: cherry=40
iter: orange=60
for_each: apple=100
for_each: cherry=40
for_each: orange=60
get_keys/foreach: orange=60
get_keys/foreach: cherry=40
get_keys/foreach: apple=100

例2

例1をもとに、キーや値の破棄をする際に呼ばれる関数を用意したもの。
[任意]ファイル名: hashtabletest2.vala

/*
 * ハッシュテーブルのテスト2
 * キーと値の破棄を行う関数を用意してそれを用いる形の例
 *
 * valac --pkg posix hashtabletest2.vala -o hashtabletest2
 */

using Posix;

namespace HashTableTest
{
  int
  main (string[] args)
  {
    string key;
    int val;
    GLib.HashTable<string,int> fruits = new GLib.HashTable<string,int>.full (GLib.str_hash, GLib.str_equal, (data) =>
    {
      debug ("key_destroy_func: %s", (string) data);
    }, (data) =>
    {
      debug ("value_destroy_func: %d", (int) data);
    });

    GLib.HashTableIter<string,int> iter;

    fruits.insert ("apple", 100);
    fruits.insert ("orange", 60);
    fruits.insert ("cherry", 40000);
    fruits.replace ("cherry", 40);
//    fruits.insert ("cherry", 40);  // この例ではreplace()と挙動は同じ

    print ("lookup: cherry=%d\n", fruits.lookup ("cherry"));

    iter = GLib.HashTableIter<string,int> (fruits);
    while (iter.next (out key, out val))
    {
      print ("iter: %s=%d\n", key, val);
    }

    fruits.for_each ((key, val) =>
    {
      print ("for_each: %s=%d\n", (string) key, (int) val);
    });

    foreach (string key in fruits.get_keys ())
    {
      print ("get_keys/foreach: %s=%d\n", key, fruits.lookup (key));
    }

    print ("-- end of main() --\n");

    return EXIT_SUCCESS;
  }
}

下は実行例。最初の「DEBUG」の出力は項目「cherry」が更新されたことによる解放処理のもので、「-- end of main() --」の後ろの解放処理はハッシュテーブルのオブジェクト変数の寿命によるもの。

** (process:[プロセスID]): DEBUG: hashtabletest2.vala:19: key_destroy_func: cherry
** (process:[プロセスID]): DEBUG: hashtabletest2.vala:22: value_destroy_func: 40000
lookup: cherry=40
iter: apple=100
iter: cherry=40
iter: orange=60
for_each: apple=100
for_each: cherry=40
for_each: orange=60
get_keys/foreach: orange=60
get_keys/foreach: cherry=40
get_keys/foreach: apple=100
-- end of main() --
** (process:[プロセスID]): DEBUG: hashtabletest2.vala:19: key_destroy_func: apple
** (process:[プロセスID]): DEBUG: hashtabletest2.vala:22: value_destroy_func: 100
** (process:[プロセスID]): DEBUG: hashtabletest2.vala:19: key_destroy_func: cherry
** (process:[プロセスID]): DEBUG: hashtabletest2.vala:22: value_destroy_func: 40
** (process:[プロセスID]): DEBUG: hashtabletest2.vala:19: key_destroy_func: orange
** (process:[プロセスID]): DEBUG: hashtabletest2.vala:22: value_destroy_func: 60

関連記事:

参考URL: