[ 掲示板に戻る ]

記事No.1489に関するスレッドです

連想配列を使った重複を削除する仕組みについて / タスク
お世話になっております。
下記コードで重複が削除できるようですが

hash[fget(fp,i)]=1 でダミーの数字を指定する理由
fput(fp,hash[i, HASH_KEY]) で重複が削除される理由
この2点がよく分かりません。
ヘルプを見る限りHASH_KEYはキーを返すだけのはずですが
なぜ勝手に削除できるのでしょうか?

ご教授よろしくお願いいたします。

//  重複行の削除.uws
HASHTBL hash = HASH_CASECARE

fp=fopen("test.txt",F_READ)
for i=1 to fget(fp,-1)
  hash[fget(fp,i)]=1 // 1 はダミー
next
fclose(fp)

fp=fopen("new.txt",F_write)
for i=0 to length(hash)-1
  fput(fp,hash[i, HASH_KEY])
next
fclose(fp)

引用元:http://www.nagomi-jp.net/~liners/p0b.htm

No.1489 2015/07/23(Thu) 10:12:30

Re: 連想配列を使った重複を削除する仕組みについて / stuncloud
text.txtの内容が以下だったとして

foo
bar
foo
baz

先ずはじめのforループ内で以下の動作が行われます

hash["foo"] = 1 // foo というキーで値に1を代入
hash["bar"] = 1 // bar というキーで値に1を代入
hash["foo"] = 1 // foo というキーは既に存在するので1を再代入 ※
hash["baz"] = 1 // baz というキーで値に1を代入

ポイントは ※ の部分です
連想配列はキーが存在しなければキーと値のペアが新しく作られ、キーが既に存在していれば値を新たに代入します
ここで、hashにはfoo、bar、bazのキーを持つ3つの要素が追加されたことになります
これが重複を排除するための仕組みです

> hash[fget(fp,i)]=1 でダミーの数字を指定する理由

上記を踏まえるとわかると思いますが、テキストから読みだした各行を連想配列のキーに入れるのが目的なので値はなんでも良いのです
hash[fget(fp,i)] = hash[fget(fp,i)] + 1
例えばこんな風にしておけば、元のテキスト内でその内容の行が幾つあったかをカウントするといった工夫も可能です

> fput(fp,hash[i, HASH_KEY]) で重複が削除される理由

重複の削除は最初のループで終わっているのでここはその結果を書き出しているだけです
読みだした行は連想配列のキーに入れていたので、そのままキーを書き出しています

No.1490 2015/07/23(Thu) 11:44:57

Re: 連想配列を使った重複を削除する仕組みについて / koujichiu
じぶんの勉強も兼ねまして、
UWSCのプログラムについているヘルプを使わせてもらって
そのまま実験してみました ^^;

その実験結果から解ったというか想像した感じですが、

●ダミーの数字を指定する理由:
配列を初期化するのに要素を入れないとなので、適当に入れてある

(for のループで回してるので要素に 0から要素数-1 の数字をいれてもよいが
面倒なので同じ値を要素に入れている)

●重複が削除される理由:
削除しているというよりも、
連想配列のキーとして使われている、16進数値の文字列が重複しているものを削除し、
これを逆ハッシュして文字列に変換後にソートして並べ直して表示している
ような感じかもです

●構想?としては、

1: 連想配列としてのHASHTBL型のキーの値に、各行の文字列を使っている

2: HASHTBLの宣言時に、HASHTBL型のプロパティとして HASH_SORTがデフォルトでtrue
になっているのかもで、 すぐ並べ替えられるようになっている
(HASH_SORTで、キーに使ってあるハッシュ値の重複部分がわかりつつソートもできる)

3: これは想像ですが、forのループでまわしている時に使っているループ変数iは
連想配列hashのキー文字列ではなく、hashの配列としてのキー番号にひもづけされている
のかもで、全部の要素を同じ値にしてもループのなかで個別に全部ヒットする

のかもしれません ^^;

///////////////////////////////////////////////////////////////////////////////

// 連想配列の操作
//PUBLIC HASHTBL B     // パブリック、大・小文字区別しない、キーは書込み順
HASHTBL A = HASH_CASECARE or HASH_SORT // 大・小文字区別、キーはソート

A["あ"] = 1
A["う"] = 3
A["お"] = 5
A["え"] = 4
A["い"] = 2
A["お"] = 9

// "う"の値は何?
print "'う' の値は " + A["う"]

// "え"があれば削除
ifb A["え", HASH_EXISTS]
  flg = A["え", HASH_REMOVE]    // 必要無くとも変数で受ける
endif

print
print "// ソート順で表示 (文字型での昇順ソート)"
for n = 0 to Length(A)-1
  print A[n, HASH_KEY] + " = " + A[n, HASH_VAL]
next

A["あ"] = 1
A["う"] = 1
A["お"] = 1
A["い"] = 1
A["お"] = 1

print
print "// ソート順で表示 (ただし、値を全部1にしたとき)"
for n = 0 to Length(A)-1
  print A[n, HASH_KEY] + " = " + A[n, HASH_VAL]
next

print
print "// ソート順で表示 (ただし、要素の値を一度だけ指定)"
print A[1, HASH_KEY] + " = " + A[1, HASH_VAL]

A["あ"] = ""
A["う"] = ""
A["お"] = ""
A["い"] = ""
A["お"] = ""

print
print "// ソート順で表示 (ただし、値が全部無いとき)"
for n = 0 to Length(A)-1
  print A[n, HASH_KEY] + " = " + A[n, HASH_VAL]
next

////////////////////////////////////////////////////

//  重複行の削除.uws
HASHTBL hash = HASH_CASECARE // ●HASH_SORT はデフォでTrue ?

fp=fopen("test.txt",F_READ)
for i=1 to fget(fp,-1)
  //hash[fget(fp,i)]=1 // 1 はダミー
  hash[fget(fp,i)]="" // ●"" はダミー
next
fclose(fp)

fp=fopen("new.txt",F_write)
for i=0 to length(hash)-1
  fput(fp,hash[i, HASH_KEY])
next
fclose(fp)

No.1491 2015/07/23(Thu) 11:59:52

Re: 連想配列を使った重複を削除する仕組みについて / koujichiu
> stuncloud さん

>キーが既に存在していれば値を新たに代入します

再登録をすることで重複を防ぐ。。。なるほどです

× 16進数値の文字列が重複しているものを削除し、 ×
× (HASH_SORTで、キーに使ってあるハッシュ値の重複部分がわかりつつソートもできる) ×

////////////////////////////////////////////////////////

あともうひとつ。。。

HASHTBLのプロパティでHASH_SORTがデフォルトでTrueになっていたら、
テキストの重複行削除以外に
テキストを並べ替えてしまいますね。。。 m(_ _)m

No.1492 2015/07/23(Thu) 12:20:41

Re: 連想配列を使った重複を削除する仕組みについて / タスク
詳しいご解説ありがとうございます!
値が上書きされていたのですね。
盲点でした。
上書きするためにダミーを使うのですね。

お手数おかけしました。

詳しいご説明感謝いたします。
ありがとうございます。

>hash[fget(fp,i)] = hash[fget(fp,i)] + 1

応用を教えていただきありがとうございます。
HASH_valで確認したところ確かにカウントされていました。

今までこういったカウントは
ifb文使ってカウントしてたりしてましたが
これでコードが省略出来て大変助かります!


hash["foo"] = hash["foo"]+1
hash["bar"] = hash["bar"]+1
hash["foo"] = hash["foo"]+1
hash["baz"] = hash["baz"]+1

こういう状態になってるということですよね。
正直、初めは意味が分からなかったのですが
何度も見返してようやく意味が分かりました。
積極的に使って慣れていこうと思います。

fget(fp,-1)でループかけたり
慣れてる方は凄いですね。。
stuncloudさんありがとうございました!

No.1493 2015/07/23(Thu) 12:27:44

Re: 連想配列を使った重複を削除する仕組みについて / stuncloud
> こういう状態になってるということですよね。
そうです
> fget(fp,-1)でループかけたり
この「-1」は「F_LINECOUNT」定数のことですね、ファイルの行数が得られるので今回のように一行ずつforで処理したい場合にぴったりです

No.1494 2015/07/23(Thu) 14:02:08

Re: 連想配列を使った重複を削除する仕組みについて / タスク
koujichiuさん

ヘルプからそこまで推測できるのが凄いです・・・

私は推測すら出来ませんでした。。
これからも掲示版を覗いて勉強させていただきます。

ご回答ありがとうございました。

No.1495 2015/07/23(Thu) 17:49:12

Re: 連想配列を使った重複を削除する仕組みについて / stuncloud
せっかくなのでもうひとつ
書き出し部分のforループの代わりにfor inを使うとよりすっきり書けます

for key in hash
 fput(fp, key)
next

連想配列のfor inの場合inの前の変数にはキーが入ります
今回のケースでは不要でしたが、値が欲しい場合は hash[key] とします

No.1496 2015/07/23(Thu) 23:47:56

Re: 連想配列を使った重複を削除する仕組みについて / タスク
stuncloudさん
確かによりすっきりですね!
ありがとうございますm(__)m

No.1497 2015/07/25(Sat) 16:37:52