ハッシュ反復子オブジェクトとcall compcostルーチンで、マジカルチェンジのデータを順番通りに連結する

マジカルチェンジって知っていますか?

「チェ~ンジチェンジ、マジカルチェンジ、○○○という字を一文字変えて、×○○」って言って、繋げていくゲームです。

僕は今ちょうど30歳で、マジカル頭脳パワーっていうテレビ番組で、このゲームがやられていたころには、結構大きくなっていた(中学生ぐらいか?)ので、実際人とやったことないのですが、見た記憶はあります。

以前、マジカルバナナのデータを加工する記事
「ハッシュオブジェクトでマジカルバナナのデータを順番通りに連結する」
http://sas-tumesas.blogspot.jp/2014/09/blog-post_16.html

をやったので、同じノリで、マジカルチェンジもやってみようとしたら、これが思った以上に
難易度が激辛で困りました。

一応書くには書きましたが、間違いがあるかもしれず、また、より効率的な方法があると思うので
皆さんも挑戦してみて、教えてください。

データは

data Q1;
X='きいろ';output;
X='こいる';output;
X='こいん';output;
X='まいこ';output;
X='まいる';output;
X='かいろ';output;
X='かいこ';output;
run;











で、前回同様、先頭の1ワード目は固定ですが、以降は順番がぐちゃぐちゃで、ゲームが成立していないので、上記のデータセットから





のような結果を作るのが今回のお仕事です。


僕のコードは以下の感じです

data A1;
length Y RESULT $100;
 if _n_=1 then do;
  call missing(Y);
  call compcost('REPLACE=',99);
  declare hash hq(dataset: 'Q1(rename=(X=Y))');
  declare hiter hi('hq');
   hq.definekey('Y');
   hq.definedata('Y');
   hq.definedone();
 end;
set Q1(obs=1);
 rc=hi.first();
 RESULT=X;
  do until(hq.num_items=1);
   COST=compged(X,Y);
   if COST=99 then do;
    RESULT=catx('→',RESULT,Y);
rc=hq.remove(key:X);
    X=Y;
   end;
   rc=hi.next();
 end;
keep RESULT;
run;

はい、まず以前、ハッシュ反復子オブジェクトはいずれ、基礎から説明しますといってましたが、今回それを使ってます。
説明すると長くなるので、今回は概要だけ掴んでいただけたらと思います。

そもそも、今回の問題がバナナより難しいのは、キーマッチングじゃないからなんですね!1文字違いかどうかを判定するまで、くっつけれないんですね!で結局、1個1個、判定していくしかないんです。
だからしてfindメソッドを始めとした、ハッシュオブジェクトから、キー情報を元にデータをルックアップする仕組みが使えないんです。

だから、一端、ハッシュオブジェクトを作った後に、
declare hiter hi('hq');
として、そのハッシュオブジェクト「hq」をハッシュ反復子オブジェクト「hi」に進化させてます。

ハッシュ反復子オブジェクトは、キーの縛りから開放されます。findメソッド等、キーを手がかりにするメソッドが使えなくなる代わりに、firstメソッド(ハッシュ反復子オブジェクト内の最初のデータをとってくる)やnextメソッド(次のデータをとってくる)のように、単純に格納順でデータを見れるようになるんですね。


で、今回のもう一つの目玉が、1文字だけ変わっているという状態をどのように判定するかですね。
これは例えば1文字目が違って後は一緒、2文字目が違って後は一緒・・というように素直に条件分岐してもいいです。
ただ、例えば、今回は3文字のマジカルチェンジですが、文字数が増えると少し面倒になります。

そこで以前、文字列の類似度をスコア化する話題をした際に
「データステップ100満開 文字列がどれくらい類似しているかを定量化する」

でcompget関数を説明しました。

1文字消して一致したら何ポイント、1文字変えたら何ポイントといったように、一致するまでにかかるコストをスコア化する関数です。

このスコアルールを、自分で変更できるのが call compcostです。

これを使って、1文字違いのケースを、他のコストスコアをどう組み合わせても出現しないような数字にしてやります(他のケースのコストを全部0にしてもいいけど、列挙するのが面倒なので)

 call compcost('REPLACE=',99);は、1文字の置換のコストを99に設定しています。

他にどんな操作があるかはリファレンスを見てください。


以上が今回のコードの種明かしです。

もっと良いコードをどなたかお願いします。

transposeして、横にしてからarrayで、ループで、スコア比較して横同士入れ替えていくような方がいいのかなぁ。それはそれで面倒そうだけど。


で、どうでもいい話なんですが、僕、ハッシュオブジェクトからハッシュ反復子オブジェクトを作る
declare hiter書くとき、瞬間的に凄いテンションがあがるんですよね。

ハッシュオブジェクトが超サイヤ人2だとしたら、反復子オブジェクトは超サイヤ人3、ハッシュオブジェクトが始解なら反復子は卍解みたいな。
いや、別にハッシュ反復子オブジェクトになったから強いとかじゃないですし、反復子定義後も、ハッシュオブジェクトが消えるわけではなく、そっちに対するメソッドも有効なので、性質の違いがあるだけで、ちょっと例えおかしいんですが。

ただ、いっつも凄い気持ちいいので、やっぱり、男って、子供から大人まで、変形とか変身とか、進化とか、そういうのに弱いのかなぁって思いました。

Ζガンダムが一番好きですし、将棋ウォーズで、片美濃囲い→美濃囲い→高美濃囲い→銀冠のエフェクトの声聞くと、昂ぶりますし。

これ以上書くと、病院勧められそうなんでこのへんで終わります。

0 件のコメント:

コメントを投稿