はじめに
前回は、無線でメッセージ(文字列)を送信/受信するプログラムを紹介しました。
今回はこのプログラムを修正して、タートルを遠隔操作するプログラムに作り変えてみましょう。
以下の画像のように、Wireless Modemをつけたコンピュータとワイヤレスタートル(Wireles Turtle)を用意してください。 今回は、コンピュータ(画像中、金色のAdvanced Computer)が命令メッセージを送信するコントローラ側、タートルが命令メッセージを受信する側となります。
メッセージ送信(コントローラ側)プログラム
メッセージ送信プログラムは、前回の送信プログラムをそのまま使います。
メッセージ受信(タートル側)プログラム、バージョン2
前回の受信プログラム(バージョン1)に、L9-11のような3行を加えました。
実はこのプログラム、L9のloadstring(message)()
を付け加えるだけでもかまいません(言い換えると、L9とL10-11は同じことをやっており、L9のコメントアウト記号をはずして、L10-11をコメントアウトしてもかまいません)。
L9の記法はわかりづらいと思ったので、L10-11のようにより分かりやすい形に分解してみました。
まずここで使っているloadstring()関数について説明しましょう(正確な情報はLua5.1リファレンスマニュアルをどうぞ)。
- loadstring(文字列)
- 引数として与えた文字列を関数として解釈して無名関数を作成(コンパイル)します。そしてその無名関数を返値とします。
- 例えば受信したメッセージ(message)が、"turtle.up()"という文字列だとすると、この文字列を解釈して、
turtle.up()
を実行してくれる新しい無名関数を作成します。
- また、無名関数の末尾に
()
を加えることで、その無名関数を実行します。
この一連の流れをよりわかりやすいように分解したのがL10-11の記述になります。
- L10:
local func = loadstring(message)
loadstring(message)
によって作成された無名関数を、変数func
に代入
- L11:
func()
- 無名関数の末尾に
()
を加えることで、引数なしでその無名関数を実行。
- 無名関数の末尾に
「無名関数って何?」「なんでこんなことができるの?」と詳しい仕組みに興味を持った方は、以下の記事で詳しく解説しているのでご覧ください。一言でいうと「Luaの便利な仕様」ですw
プログラムを実行してみよう
- 受信側(タートル側)プログラムを実行。 → タートルはそのままメッセージ待ち状態へ。
- 送信側(コントローラ側)プログラムで、以下のようにプログラムを実行。
msg_sender turtle.turnRight() 30
・・・ID30のタートルにメッセージ"turtle.turnRight()"を送信。
文字列"turtle.turnRight()"を受信したタートルは、それを関数turtle.turnRight()
と解釈して、右を向くはずです。
以下は一例ですが、タートルを操作する関数は他にもたくさんあります(Turtle (API) - ComputerCraft Wiki)。 いろいろな命令を入力・送信して遊んでみましょう。
turtle.forward()
、turtle.back()
・・・前/後へ移動(燃料が必要)turtle.up()
、turtle.down()
・・・上/下へ移動(燃料が必要)turtle.turnRight()
、turtle.turnLeft()
・・・90度右を向く/90度左を向く(燃料の必要なし)
このプログラムの問題点
さて、遊んでいるうちに、面倒なことが起きませんでした?
- "turtle.turnRight()" と打ち込むつもりが間違えて、"turtle.turnRihgt()" とつづりを間違えて命令しちゃった!
- 受信側のタートルプログラムは、エラーが生じたのでプログラム強制終了wwww
うあぁぁぁ。また受信側のプログラムを実行しなおしかよ。めんd-。
そうなんです。loadstring(文字列)関数を実行するときに、誤った文字列を与えてしまうと、「nilを呼びだそうとしてる」(だから、そんな関数作れないよ!)とエラーが発生して強制的にプログラムを停止させてしまうのです。
再挑戦したければ、またタートル側で待ちうけプログラムを起動しなおさないといけません。つまり文字入力を失敗できないのです。これはつらい。
困りました。エラーが生じても、それをなだめて、プログラムをそのまま実行しつづけることはできないのでしょうか。
メッセージ受信(タートル側)プログラム、バージョン3
結論から言うと、工夫すればできます。以下は、この問題を修正したプログラムです。
L10で新しく出てきた関数、pcall()
を紹介します(正確な情報はLua5.1リファレンスマニュアル)。
pcall(f, arg1, arg2, ・・・)
- pcall()は一言でいうと、「関数を実行してエラーが発生したらそれを捕まえて、falseとエラーメッセージを返す」関数です。
- 引数1(無名関数): 実行したい関数を与えます
- 引数2以降、その無名関数に与える引数をいくつでも
- 返値1(boolean): ステータスコード。関数実行成功ならtrue、エラーが生じたらfalse。
- 返値2以降: 関数実行成功なら関数実行による返値を、エラー発生時にはエラーメッセージを返す。
- 実行例:
pcall(func, arg1, arg2)
は、func(arg1, arg2)
を実行してエラー発生を捕らえて処理を行います。
L12-16は、pcall()から返ってきたステータスコード(変数status)によって、表示メッセージを変えている部分になります。
今度こそ、プログラムで遊んでみよう
上記画像はタートル側の画面ですが、途中、スペルミスをしても「(error)」と指摘されるだけで、そのまま続けてプログラムを実行していますね。
まとめ
さあこれで、いくらでも間違えることができるようになりましたね。便利でしょう?
・・・え? わざわざ、関数名を入力するのがめんどくさい?
ですよねー。
次回はこの対策を考えましょう。