前回の復習
前回までで、logging APIによるタートル移動履歴機能を完成させました。
logging.forward()
などを使って移動したらログファイルに移動したことを書き込んでいきます。
今回は、そのログファイルの内容を使って、スタート地点に戻る機能を実現します。
基本的な考え方
考え方はとてもシンプルです。
たとえばタートルが移動を完了した後、ログファイルには以下のようなログが残っているとします。
turtle.forward() turtle.forward() turtle.turnRight() turtle.forward()
(1)このファイルの中身を読んで、(2)反対の意味の関数に置換して、(3)逆順に入れ替えればよいのです。
つまりはこんな感じに。
turtle.back() turtle.turnLeft() turtle.back() turtle.back()
いまはFS APIというテーマでやっているので、置換&逆順にしたものをファイルに書き出すことにしましょう。
最終的に、この変換後ファイルをプログラムとして実行することで元の場所に戻ってきてくれるはずです。
ログファイルを変換するプログラム
アルゴリズムはいくつか考えられるのですが*1、 わかりやすさ重視でこのようなプログラムを書いてみました。
-- config LOG_FILE = "mylog" -- log file REV_FILE = "myrev" -- reverted log file TRANS_TBL = {} TRANS_TBL["turtle.forward()"] = "turtle.back()" TRANS_TBL["turtle.back()"] = "turtle.forward()" TRANS_TBL["turtle.up()"] = "turtle.down()" TRANS_TBL["turtle.down()"] = "turtle.up()" TRANS_TBL["turtle.turnRight()"] = "turtle.turnLeft()" TRANS_TBL["turtle.turnLeft()"] = "turtle.turnRight()" -- functions function backupFile(filename, tail_str) local tail_str = tail_str or "-bak" if fs.exists(filename) then fs.delete(filename..tail_str) fs.move(filename, filename..tail_str) return true else return false, "File doesn't exist" end end function readTrans(filename, trans_tbl) local fh = fs.open(filename, 'r') local tmp_tbl = {} repeat local line = fh.readLine() local tLine = TRANS_TBL[line] if tLine then table.insert(tmp_tbl, tLine) end until not line fh.close() return tmp_tbl end function reverseWrite(my_array, filename) local fh = fs.open(filename, 'a') for i=#my_array,1,-1 do fh.writeLine(my_array[i]) end fh.close() end -- main backupFile(REV_FILE) local translated_tbl = readTrans(LOG_FILE, TRANS_TBL) reverseWrite(translated_tbl, REV_FILE)
いつものように、設定項目(config)部分、メイン(main)部分、メイン部分で使っている関数(functions)部分の順に簡単に説明しましょう。
解説(Config)
このプログラムの利用者が自分の好きに書き換えられるパラメータです。
LOG_FILE = "mylog"
- タートルが移動したときにログを出力するファイルの名前です。
- このファイルをプログラムとして実行すると、同じ地点に移動することができます。
REV_FILE = "myrev"
- 逆の意味の関数に変換して、それを逆順にした関数リスト。その結果を書き出すファイルです。
- このファイルをプログラムとして実行すると、元の場所に戻ってきます。
TRANS_TBL = {} (以下略)
- 逆の意味の関数に変換するための変換テーブルです。
- テーブルのキーとして"turtle.forward()"という文字列を指定して参照すると、その逆の意味の関数文字列が返ってきます。
- つまり、
TRANS_TBL["turtle.forward()"]
とすると、その結果は"turtle.back()"
- なお、
TRANS_TBL["-- 登録されていない文字列"]
のように、定義していないキーで参照すると結果はnilとなります。
解説(main)
簡単に3ステップ。
- 最終結果を保存するファイルと同じ名前のファイルがすでにあるならばバックアップをとる。
backupFile(REV_FILE)
- LOG_FILEを読み込んで逆の意味の関数に変換したテーブルをローカル変数translated_tblに代入
local translated_tbl = readTrans(LOG_FILE, TRANS_TBL)
- translated_tblの中身を逆順に並べ替えてREV_FILEに書き込み。
reverseWrite(translated_tbl, REV_FILE)
解説(Functions)
function backupFile(filename, tail_str)
- これまでも何度も行っている処理です。ファイルが存在したら念のためバックアップ。
- そのバックアップ先のファイル名もあったら、そちらは消します。
function readTrans(filename, trans_tbl)
- 指定されたファイルを読んで(read)、変換テーブルを使って変換(translate)
- 返値としてテーブル(配列)を返します。
function reverseWrite(my_array, filename)
- 与えられたテーブル(配列)を、逆順に並べ替えてファイルに書き込み。
- なお、配列を逆順に(つまり後ろから)読み出すために、for文の特殊構文を使っています。
- 詳細は以下のとおり
-- 普通の使い方 -- for文でi=1から4まで順に増やしつつ繰り返す for i=1,4 do print(i) end -- 4のあとにさらに数値を付け加えることで増加量を指定可能。 -- この場合、print(1)、print(3)が実行される。 for i=1, 4, 2 do print(i) end -- 増加量にマイナスの値も指定可能なので次のようなことも。 -- for文でi=4から1まで順に値を減らしつつ繰り返す for i=4, 1, -1 do print(i) end -- 配列を逆順に(後ろから)読み出す。#arrayで配列の要素数。 array = {'a', 'b', 'c', 'd'} for i=#array, 1, -1 do array[i] end
なお、配列を逆順に読み出すには、次のようなやり方もあります*2。 お好きなほうをどうぞ。
-- 配列を逆順に(後ろから)読み出す array = {'a', 'b', 'c', 'd'} for i=1, #array do array[#array-i+1] end
今回のまとめ
変換プログラムができたので、これをこれまでのlogging APIに組み込みました。
その結果はこちら。
- logging API ver.0.4 (http://pastebin.com/xLwUS6Y5)
logging API: log turtle's move functions and come back to start position by using logfile. https://t.co/WVE1ngiqJu
— hevohevo (@hevohevo) June 19, 2014
次回はこのAPIの使い方からはじめましょう。