Minecraftとタートルと僕

PCゲームMinecraftのMOD「ComputerCraft」の情報を集めたニッチなブログです。

こちらのページは更新が滞っており、情報が古くなりつつあります。新しいCC情報サイトをはじめましたので、もしよければご参照ください。今後ともよろしくお願い申し上げます。

「百億のマインクラフトと千億のタートル」(https://hevo2.hatenablog.com/)

sleep実装からイベントを学ぶ(1)―はじめに

はじめに

「whileループにはsleep()を入れること」

CCプログラマなら誰でも知っているこのルール。

そもそもsleep()って何をやっているんだろう。そのような疑問を持った僕が調べた結果、その副産物によって、これからの記事を書く予定です。

これからの記事を読むことで、

  • CCのイベントとは何か
  • イベントはどう取り扱えば良いのか
  • イベントを利用してインタラクティブなプログラムを組むにはどうすればよいのか

このようなことを学べるはずです。

たとえば、モニターをクリックすることでプログラムを再起動したり、タートル君の画面上で上下キーを押して選択肢を選んだりできるようになると、面白いプログラムが組めそうですよね。

また、このテーマのネタ元はほとんどがCC公式(英語)であることも明記しておきます。

sleep()はどのように定義されているのか

まずは、sleep()がどこで定義されているのか実際に見てみましょう。
ゲーム起動中に書き換えるとゲームが止まる可能性がある危険なファイルなので、
ゲームとはまた別に「ComputerCraft1.53.zip」ファイルを展開してから中を覗いてください。

「ComputerCraft1.53/lua/bios.lua」というファイルです。
このファイルには、今回のsleepに関する以外にも、興味深い内容が多く含まれているので、実際に中を読むことをお勧めします。

以下に、sleepに関連する部分2か所をファイルから抜粋しました。

function sleep( _nTime )
    local timer = os.startTimer( _nTime )
	repeat
		local sEvent, param = os.pullEvent( "timer" )
	until param == timer
end
function os.sleep( _nTime )
	sleep( _nTime )
end

sleep()関数がまず定義されていて、os.sleep()はその別名関数となっています。これ見るまでは逆だと思ってました。

閑話休題。

とてもシンプルな関数定義ですね。
しかしこの内容を理解するために、以下のことを前提知識として知っておく必要があります。

  1. repeat ~ until って?
  2. タイマー(timer)って?
  3. そもそもイベント(Event)って何?
  4. os.pullEvent()ってようするに何しているの?

まずは、一番説明が簡単な「repeat」から説明しましょう。

「repeat ~ until 条件式」ループ

まずは「repeat ~ until 条件式」ループについて簡単に説明しましょう。
一言で言うと、「while 条件式 do ~ end」ループの亜種です。

以下、「while」と「repeat」の違いについてまとめてみました*1

制御構造 条件式が偽 条件式が偽ではない ループ内の実行
while ループをやめる ループを続ける 条件式を調べてから実行
repeat ループを続ける ループをやめる まず1回実行してから条件式を調べる

つまりrepeatは、基本的にループを続ける方針なのだけど「条件式が偽でない」ことがわかったらループをやめるというループ文です。

「偽かどうか」で分岐するの?「真(true)か偽(false)か」じゃなくて?

この疑問をもっと正確に言い換えるならば、「Luaの条件式はboolean型以外も受け付けるの?」でしょうか。
これはプログラミング初心者だけでなく他のプログラム言語(特に型の区別が厳格な言語)に慣れ親しんだ人も誤解しやすいところなのですが、Luaの制御構造は、「真か偽か」ではなく「偽か、そうでないか」で分岐します。
正確にはそのように定められているわけではないのですが、実際にはそう覚えた方が理解しやすいです。
まずはLua5.1のリファレンスマニュアルから、定義部分を抜粋しましょう。

制御構造の条件式は任意の値をとれる。 false と nil は共に偽である。 nil と false 以外のすべての値は真になる (特に、数値の0や空文字列は真であることに注意)。

つまり、

偽として扱うもの false, nil
真として扱うもの 偽として扱う上記以外すべて(数値0や空白スペースも真)

だから次のようなプログラムは正常に動きます。

function hevoCount()
  --アイテムの数を数えてその「個数」を返すという仮想的な関数
  --ただしカウントに失敗したらエラーとして「false」を返す
end

if hevoCount() then
 print('good')
else
 print('error')
end

これに関連して余談(読み飛ばし可)

制御構造の条件式書式スタイルについて、個人的な好みをまとめてみました。
「ふーん、そうなんだ。変なところにこだわりがあるんだね」
程度に理解してもらえればOKです。

if文で条件式がtrueのときの書き方例

某匿名掲示板で次のようなのを見ました(若干アレンジしてます)。

x = true 
if x == true then print('ok') end

真なら通すのだから僕なら「if x then print('ok') end」でしょうか。
もしかしたらわかりやすさのため、わざとかもしれませんが。
色々なスタイルの人がいますね。面白い。

if文で条件式がfalseのときの書き方例

  • if not x then print('ok') end
    • 省スペースは正義派
  • if x == false then print('ok') end
    • false目立つように強調したいよ派
  • unless x then print('ok') end
    • なんでunlessがないのよ派 →もちろんエラーです
  • if x ~= true then print('ok') end
    • どれだけひねくれているのよ派 →場合によってはバグの可能性
  • if not (x == true) then print('ok') end
    • ネタとしてわざとやっているでしょ派 →こちらもバグの可能性

まとめ

一度脱線するとそのまま大幅に寄り道してしまうけれど、
個人のブログだし、フリーダムな舵取りしても問題ないよね:P

次回は、イベントについて説明しましょう。

*1:ミスを発見したので2013/12/24に修正しました