状態遷移
状態遷移を使って簡単な(適当な)言語を進めるプログラム書きました。
# encoding: cp932 STATE_TRANSITION_TABLE = { "Q1" => ["Q1","Q2","Q3"], "Q2" => ["Q2","A1","Q4"], "Q3" => ["Q3","Q4","Q5"], "Q4" => ["Q4","A2","Q6"], "Q5" => ["Q5","Q6","A4"], "Q6" => ["Q6","A3","A5"], } MESSAGE = { "Q1" => "静(1)と動(2)ならどちらを選ぶ", "Q2" => "コーヒー党(1)?それとも紅茶当(2)?", "Q3" => "記号(1)と言語(2)どちらを選ぶ?", "Q4" => "カレーは大好き?もちろん(1) それほど(2)", "Q5" => "規律(1)と自由(2)どちらが重要?", "Q6" => "古いものに勝ちを見出すタイプ?はい(1) いいえ(2)", "A1" => "次はjavaを学習しましょう", "A2" => "Haskellがあなたを待っています", "A3" => "次はLispへ進みましょう", "A4" => "やっぱりRubyですね", "A5" => "Python" } state = "Q1" while state[0] == "Q" puts(MESSAGE[state]) state = STATE_TRANSITION_TABLE[state][gets().to_i] || STATE_TRANSITION_TABLE[state][0] end puts(MESSAGE[state])
ここで指示通りに実行していくと、問題なく状態遷移してくれます。指示通りなら。例えば、実行結果を見ると
静(1)と動(2)ならどちらを選ぶ 6 静(1)と動(2)ならどちらを選ぶ -1 記号(1)と言語(2)どちらを選ぶ?
状態Q1から始まります。1つめの入力は6です。でも、6なんて指示してないので状態は遷移せず同じ質問が繰り返されています。次は-1と入力。すると質問が「記号(1)と言語(2)どちらを選ぶ?」となりました。これは状態Q3です。STATE_TRANSITION_TABLEを見ると、 "Q1" => ["Q1","Q2","Q3"] です。-1を入力したことで、本来2の入力があった時の遷移状態Q3へ移動してしまってます。これは問題。
なので、-1など負の値を受け付けないように修正しました。puts(MESSAGE[state])の下に以下を追加しました。
puts(MESSAGE[state]) i = gets().to_i() if i<0 || i>= STATE_TRANSITION_TABLE[state].size i=0 end
状態遷移の利点は、後に状態の追加や変更に対して配列の部分を修正するだけでいいこと。
hashの格納順番
ハッシュ表を作る際、配列を使って実装した。
HASH_SIZE = 11 TABLE = Array.new(HASH_SIZE) def set(key,val) #ハッシュをして、ハッシュ表にデータを格納 end set("hello","world") set("今日は","赤ちゃん") set("竹やぶ","焼けた") set("テレビ","ラジオ") set("ruby","red") set("sapphire","blue") set("121","11") set("富士","オーム") set("人","見頃")
これを実行すると
[["hello", "world"], ["ruby", "red"], ["121", "11"], ["人", "見頃"], ["今日は", "赤ちゃん"], ["富士", "オーム"], ["竹やぶ", "焼けた"], ["テレビ", "ラジオ"], ["sapphire", "blue"]]
設定順通りにはデータが格納されていない。
今まで使っていたhashだと
table = {} table["hello"] ="world" table["今日は"] ="赤ちゃん" table["竹やぶ"] ="焼けた" table["テレビ"] ="ラジオ" table["ruby"] ="red" table["sapphire"] ="blue" table["121"] ="11" table["富士"] ="オーム" table["人"] ="見頃"
実行すると
{"hello"=>"world", "今日は"=>"赤ちゃん", "竹やぶ"=>"焼けた", "テレビ"=>"ラジオ", "ruby"=>"red", "sapphire"=>"blue", "121"=>"11", "富士"=>"オーム", "人"=>"見頃"}
となり、設定順に格納されている。キーを基にハッシュ関数に書けると設定順にはならないので、別の配列などで順番を保持するものを作っておくこと。
ensure使い慣れん
例外処理でbeginとrescueを使っていたのですが、ensureを使いなれてないことがわかりました。ensureは例外の有無にかかわらずbegin式の最後に記述する処理を実行するもの。
今回は暗号処理をしたものには必ずresetメソッドを呼び出すために使いました。
def encrypt(file, pass) enc = OpenSSL::Cipher::AES256.new("CBC") enc.encrypt() enc.pkcs5_keyivgen(pass) begin File.open(file, "rb") do |fin| File.open("#{file}.sec", "wb") do |fout| while buff = fin.read(8000) fout.write(enc.update(buff)) end fout.write(enc.final()) end end ensure enc.reset() end end
File.openでエラーが出た場合は、本文のほうで例外処理をしてあります。
begin encrypt(arg, pass) puts("#{arg}を暗号化したファイル#{arg}.secを作成しました。") rescue puts("#{arg}の暗号化に失敗しました。") end
$stdin.gets()
gets()
コマンドラインでファイルを引数とすると、gets()では指定したファイルの内容を一行読み込む。これを避けるために$stdin.gets()を使用する
暗号化と複合化
ライブラリopensslを使ってファイルを暗号化・複合化するプログラムを書きました。
暗号化
enc = OpenSSL::Cipher::AES256.new("CBC") enc.encrypt() enc.pkcs5_keyivgen(pass) File.open(file, "rb") do |fin| File.open("#{file}.sec", "wb") do |fout| while buff = fin.read(8000) fout.write(enc.update(buff)) end fout.write(enc.final()) end end enc.reset()
- 暗号オブジェクトを生成
- encrypt()で暗号化の準備
- pkc5_keyivgen(pass)でパスワードからivとキーを生成
- read(8000)で8000文字のbuffブロックを作る
- update(buff)で暗号化
- ブロックに残っているデータを暗号化
- reset()でivとキーなど複合の為に必要な情報を消去
以上の順で暗号化を行う。
複合化
enc = OpenSSL::Cipher::AES256.new("CBC") enc.decrypt() enc.pkcs5_keyivgen(pass) File.open(file, "rb") do |fin| File.open("#{file}.plain", "wb") do |fout| while buff = fin.read(8000) fout.write(enc.update(buff)) end fout.write(enc.final()) end end enc.reset()
暗号化と違う点は
- decrypt()で複合化の準備
一点のみなので、とても楽だった。