Cでプログラムを作成するときに見逃したことの1つは、辞書データ構造です。 Cで実装する最も便利な方法は何ですか?パフォーマンスを探しているのではなく、ゼロからコーディングするのが簡単です。私はそれが汎用であることも望んでいません-string-> intのようなものが行います。ただし、任意の数のアイテムを保存できるようにしたいのです。
これは、演習を目的としています。使用できるサードパーティのライブラリがあることを知っています。しかし、しばらくの間、それらが存在しないことを考慮してください。このような状況では、上記の要件を満たす辞書を実装する最も簡単な方法は何ですか。
Cプログラミング言語 のセクション6.6は、単純な辞書(ハッシュテーブル)データ構造を示しています。便利な辞書の実装がこれより単純になるとは思わない。便宜上、ここでコードを再現します。
struct nlist { /* table entry: */
struct nlist *next; /* next entry in chain */
char *name; /* defined name */
char *defn; /* replacement text */
};
#define HASHSIZE 101
static struct nlist *hashtab[HASHSIZE]; /* pointer table */
/* hash: form hash value for string s */
unsigned hash(char *s)
{
unsigned hashval;
for (hashval = 0; *s != '\0'; s++)
hashval = *s + 31 * hashval;
return hashval % HASHSIZE;
}
/* lookup: look for s in hashtab */
struct nlist *lookup(char *s)
{
struct nlist *np;
for (np = hashtab[hash(s)]; np != NULL; np = np->next)
if (strcmp(s, np->name) == 0)
return np; /* found */
return NULL; /* not found */
}
char *strdup(char *);
/* install: put (name, defn) in hashtab */
struct nlist *install(char *name, char *defn)
{
struct nlist *np;
unsigned hashval;
if ((np = lookup(name)) == NULL) { /* not found */
np = (struct nlist *) malloc(sizeof(*np));
if (np == NULL || (np->name = strdup(name)) == NULL)
return NULL;
hashval = hash(name);
np->next = hashtab[hashval];
hashtab[hashval] = np;
} else /* already there */
free((void *) np->defn); /*free previous defn */
if ((np->defn = strdup(defn)) == NULL)
return NULL;
return np;
}
char *strdup(char *s) /* make a duplicate of s */
{
char *p;
p = (char *) malloc(strlen(s)+1); /* +1 for ’\0’ */
if (p != NULL)
strcpy(p, s);
return p;
}
2つの文字列のハッシュが衝突すると、O(n)
ルックアップ時間が発生する可能性があることに注意してください。 HASHSIZE
の値を増やすことにより、衝突の可能性を減らすことができます。データ構造の詳細については、本を参照してください。
quickestの方法は、 thash のような既存の実装を使用することです。
また、実際に自分でコーディングしたい場合は、uthash
のアルゴリズムを調べて再利用できます。それはBSDライセンスですので、著作権表示を伝えるための要件を除いて、あなたはそれで何ができるかについてかなり無制限です。
実装を簡単にするために、単純に配列全体を検索するのは簡単です。いくつかのエラーチェックは別として、これは完全な実装です(テストされていません)。
typedef struct dict_entry_s {
const char *key;
int value;
} dict_entry_s;
typedef struct dict_s {
int len;
int cap;
dict_entry_s *entry;
} dict_s, *dict_t;
int dict_find_index(dict_t dict, const char *key) {
for (int i = 0; i < dict->len; i++) {
if (!strcmp(dict->entry[i], key)) {
return i;
}
}
return -1;
}
int dict_find(dict_t dict, const char *key, int def) {
int idx = dict_find_index(dict, key);
return idx == -1 ? def : dict->entry[idx].value;
}
void dict_add(dict_t dict, const char *key, int value) {
int idx = dict_find_index(dict, key);
if (idx != -1) {
dict->entry[idx].value = value;
return;
}
if (dict->len == dict->cap) {
dict->cap *= 2;
dict->entry = realloc(dict->entry, dict->cap * sizeof(dict_entry_s));
}
dict->entry[dict->len].key = strdup(key);
dict->entry[dict->len].value = value;
dict->len++;
}
dict_t dict_new(void) {
dict_s proto = {0, 10, malloc(10 * sizeof(dict_entry_s))};
dict_t d = malloc(sizeof(dict_s));
*d = proto;
return d;
}
void dict_free(dict_t dict) {
for (int i = 0; i < dict->len; i++) {
free(dict->entry[i].key);
}
free(dict->entry);
free(dict);
}
単純なハッシュ関数と構造のいくつかのリンクリストを作成し、ハッシュに応じて、値を挿入するリンクリストを割り当てます。同様にそれを取得するためにハッシュを使用します。
私はしばらく前に簡単な実装を行いました:
... #define K 16 //連鎖係数 struct dict { char * name;/*キーの名前*/ int val;/*値*/ struct dict * next;/*リンクフィールド*/ }; typedef struct dict dict; dict * table [K]; int initialized = 0; void putval(char *、int); void init_dict() { 初期化= 1; int i; for(i = 0; iname =(char *)malloc(strlen(key_name)+1); ptr-> val = sval; strcpy(ptr-> name 、key_name); ptr-> next =(struct dict *)table [hsh]; table [hsh] = ptr; } int getval(char * key_name) { int hsh = hash(key_name); dict * ptr; for(ptr = table [hsh]; ptr!=(dict *)0; ptr =(dict *)ptr-> next) if(strcmp(ptr-> name、key_name)== 0) return ptr-> val; return -1; }
ここに簡単な実装があり、文字列から 'Matrix'(struct)を取得するために使用しました。より大きな配列を作成し、実行時にその値を変更することもできます。
typedef struct { int** lines; int isDefined; }mat;
mat matA, matB, matC, matD, matE, matF;
/* an auxilary struct to be used in a dictionary */
typedef struct { char* str; mat *matrix; }stringToMat;
/* creating a 'dictionary' for a mat name to its mat. lower case only! */
stringToMat matCases [] =
{
{ "mat_a", &matA },
{ "mat_b", &matB },
{ "mat_c", &matC },
{ "mat_d", &matD },
{ "mat_e", &matE },
{ "mat_f", &matF },
};
mat* getMat(char * str)
{
stringToMat* pCase;
mat * selected = NULL;
if (str != NULL)
{
/* runing on the dictionary to get the mat selected */
for(pCase = matCases; pCase != matCases + sizeof(matCases) / sizeof(matCases[0]); pCase++ )
{
if(!strcmp( pCase->str, str))
selected = (pCase->matrix);
}
if (selected == NULL)
printf("%s is not a valid matrix name\n", str);
}
else
printf("expected matrix name, got NULL\n");
return selected;
}
hsearch/hcreate Windowsで利用できないがPOSIXで義務付けられているため、Linux/GNUシステムで利用できるライブラリのセットに誰も言及していないことに驚いています。
リンクには、その使用法を非常によく説明するシンプルで完全な基本例があります。
スレッドセーフなバリアントも備えており、使いやすく、非常に高性能です。
ハッシュテーブルは、単純な「辞書」の伝統的な実装です。速度やサイズを気にしない場合は、 google for it のみを使用してください。多くの自由に利用可能な実装があります。
私が最初に見たものです -一見すると、それは私には大丈夫に見えます。 (非常に基本的です。無制限の量のデータを保持したい場合は、テーブルメモリが大きくなると「再割り当て」するためのロジックを追加する必要があります。)
幸運を!
GLibおよびgnulib
これらは、より具体的な要件がない場合の最善策です。なぜなら、それらは広く利用可能で、ポータブルで、おそらく効率的だからです。
GLib: https://developer.gnome.org/glib/ GNOMEプロジェクト。文書化されたいくつかのコンテナ: https://developer.gnome.org/glib/stable/glib-data-types.html 「Hash Tables」と「Balanced Binary Trees」を含む。ライセンス:LGPL
gnulib: https://www.gnu.org/software/gnulib/ by GNUプロジェクト。ソースをコピーしてコードに貼り付けることを意図しています。文書化されたいくつかのコンテナ: https://www.gnu.org/software/gnulib/MODULES.html#ansic_ext_container 「rbtree-list」、「linkedhash-list」、「rbtreehash-list」を含む。 GPLライセンス。
最も簡単な方法は、バイナリツリーを使用することです。最悪の場合もO(logn)のみです。
ハッシュが鍵です。これにはルックアップテーブルとハッシュキーを使用すると思います。多くのハッシュ関数をオンラインで見つけることができます。