トップ » 技術記事 » 高速スクリプト言語「Lua」を始めよう!(4) - 高速スクリプト言語「Lua」を始めよう!(4)

高速スクリプト言語「Lua」を始めよう!(4) - 高速スクリプト言語「Lua」を始めよう!(4)

タグ: Lua

本稿では動作速度が高速で、非常に移植性が高い組み込み向けのプログラミング言語「Lua」の使い方について紹介します。Lua は、JavaScript や Pascal に似ていることから、とても手に馴染みやすいのが特徴です。自作アプリケーションにちょっとしたスクリプト言語を組み込みたい場合に重宝します。そこで、Lua のインストールから、簡単な使い方を紹介し、簡単なアプリケーションに組み込んで使うまでの過程を解説します。

本稿の目標

本稿では、Lua がどんなプログラミング言語なのかを紹介し、実際に簡単なアプリケーションに組み込んで使ってみるところまでを紹介します。

第一回目は、Lua とは何か、そして、どんな風にプログラムを書くのかを紹介しました。第二回目は、Lua の便利なテーブル型や文字列の操作について紹介しました。第三回目は、Lua の関数やメタテーブルについて紹介しました。

今回は、オブジェクト指向について紹介します。

プロトタイプベースのオブジェクト指向

Lua には、明確なクラスの定義が存在しません。JavaScript に似たプロトタイプベースのオブジェクト指向を利用します。それも、テーブル型とメタテーブルを利用します。

以下は、テーブルコンストラクタを利用して、Dog オブジェクトを作成します。そして、Dog オブジェクトの showProfile メソッドを呼び出します。


-- オブジェクトを作成
Dog = {
  name = "Jiro",
  age  = 3,
  showProfile = function(self)
    prof = string.format("name=%s,age=%d", self.name, self.age)
    print(prof)
  end
}
-- showProfile メソッドを呼ぶ
Dog:showProfile()

Lua では、テーブル型の値に対して、「table["key"]」と書くところを「table.key」と書くことができるので、オブジェクト指向風に表現することができます。

そして、「table:key()」のように書くと「table.key(table)」と同じ意味になるのです。これは、かなり面白い仕組みですで、第一引数で、自身(self)をとるような関数を定義しておけば、あたかもインスタンスのメソッドを呼び出すようにして関数を実行することができます。

しかし、Lua には、new 演算子が用意されていません。そのため、new と同じような動作を行うメソッドを用意する必要があります。

以下のプログラムでは、new メソッドの中で、テーブル型の変数 obj を生成し、そこに、連想配列のキー name と age と showProfile を設定し戻り値として戻しています。

-- Dog クラスを定義
Dog = {}
Dog.new = function(name, age)
  local obj = {}
  obj.name = name
  obj.age  = age
  obj.showProfile = function(self)
    s = string.format("[name=%s,age=%d]", self.name, self.age)
    print(s)
  end
  return obj
end
-- インスタンスを生成
jiro = Dog.new("jiro", 3)
sabu = Dog.new("sabu", 2)
-- メソッドを呼ぶ
jiro:showProfile()
sabu:showProfile()

実行結果:

[name=jiro,age=3]
[name=sabu,age=2]

オブジェクト指向の継承を利用する

そして、Luaには、オブジェクト指向の継承を実現するための仕組みが用意されています。なお、ここでは、オブジェクト指向の用語に合わせるように、便宜的にテーブルのことを、オブジェクトと呼び、テーブルの連想配列の要素をプロパティと呼びます。

継承を実現するために、メタテーブルには __index というキーワードが用意されています。メタテーブルの __index にスーパークラス(継承元のクラス)を指定することで、継承を実現できるようになるのです。

この仕組みは、__index にスーパークラスのオブジェクトを設定すると、そのオブジェクト自身のキーを探した後、スーパークラスのオブジェクトのプロパティを探索するようになるのです。

以下は、クラスの継承を実現したものです。Animal クラスを定義し、Animal を継承した、Dog クラスを作った例です。

-- 基本となる Animal クラスを定義
Animal = {}
Animal.name = "unknown"
Animal.showProfile = function(self)
  print("*** profile")
  print("name="..self.name)
end
-- Animal を継承して Dog クラスを作る
Dog = {}
Dog.new = function (name)
  local obj = {}
  obj.name = name
  setmetatable(obj,{__index=Animal})
  return obj
end
-- インスタンスを生成する
jiro = Dog.new("jiro")
jiro:showProfile()

実行結果

*** profile
name=jiro

ただし、すべてのクラスに、new メソッドを定義する場合、サブクラスで、スーパークラスの new メソッドを呼ぶことで、スーパークラスのプロパティをすべてもったオブジェクトが作成されることになるので、メタテーブルの __index を操作する必要はないのかもしれません。

以下、メタテーブルを利用しないで、継承を再現する例です。

-- 基本となる Animal クラスを定義
Animal = {}
Animal.new = function (name)
  local obj = {}
  obj.name = name
  obj.enumProperty = function (self)
    print("**"..self.name)
    for key,val in pairs(self) do
      print("-"..key, val)
    end
  end
  return obj
end
-- Animal を継承して Dog クラスを作る
Dog = {}
Dog.new = function (name)
  local obj = Animal.new(name)
  obj.bark = function (self)
    print(self.name..":bowwow!")
  end
  return obj
end
-- Dog を継承して BullDog クラスを作る
BullDog = {}
BullDog.new = function (name)
  local obj = Dog.new(name)
  obj.walk = function(self)
    print(self.name..":walking!")
  end
  return obj
end
-- インスタンスを生成する
jiro = Dog.new("jiro")
jiro:bark()
jiro:enumProperty()
sabu = BullDog.new("sabu")
sabu:walk()
sabu:enumProperty()

実行したところ

jiro:bowwow!
**jiro
-enumProperty   function: 0x10046e28
-name   jiro
-bark   function: 0x10047320
sabu:walking!
**sabu
-bark   function: 0x10035d68
-enumProperty   function: 0x10035d50
-name   sabu
-walk   function: 0x10035e08

このように、Lua のオブジェクト指向は、その実現方法が様々です。Lua のユーザーズ Wiki を確認すると、オブジェクト指向の実現方法がいくつか列挙されています。好きな方法を利用してオブジェクト指向を実践すると良いでしょう。

http://lua-users.org/wiki/ObjectOrientedProgramming

関数についての補足

さて、Lua では、テーブルをオブジェクト指向風に見せる工夫がいろいろあることは紹介しましたが、関数の定義の際にも、「:」を使うことができます。以下の、3つの関数の定義はどれも同じ意味になります。

-- obj["a"] に関数を設定する
obj.a = function(self) print(self) end
-- 上と同じ意味
function obj.a(self) print(self) end
-- 上と同じ意味
function obj:a() print(self) end

関数を定義する際にも、self を省略できるのは便利ですね。

モジュールについて

少し規模の大きなプログラムを作る場合、プログラムのモジュール化は必須となります。Lua でもモジュール化の機能が提供されています。

普通に外部のLuaプログラムファイルを読み込む場合には、require が利用できます。以下の、test.lua と、main.lua を、それぞれファイルに保存して、main.lua を実行します。

-- test.lua
function hoge()
  print("hoge")
end
-- main.lua
require("test")
hoge()

実行したところ

$ lua main.lua
hoge

ライブラリを完全にモジュール化するには、module 宣言を利用します。モジュール化することで名前の衝突を防ぐことができます。ライブラリを利用するには、「ライブラリ名.変数名」のようにして利用することができます。

-- test.lua
module("test", package.seeall)

function hoge()
  print("hoge")
end
-- main.lua
require("test")
test.hoge()

まとめ

以上、Lua のオブジェクト指向について紹介しました。ここまで紹介したように、Luaではプロトタイプベースのオブジェト指向が可能でした。基本的な文法紹介は今回で終わりです。次回からは、Luaを自作アプリケーションに組み込んで使う方法を紹介します。

Series Navigation«高速スクリプト言語「Lua」を始めよう!(3)高速スクリプト言語「Lua」を始めよう!(5)»高速スクリプト言語「Lua」を始めよう!(6)»

執筆者紹介

クジラ飛行机

クジラ飛行机

くじらはんど(http://kujirahand/)にて、日本語プログラミング言語「なでしこ」(IPA未踏ユース採択)、テキスト音楽「サクラ」(OSPオンラインソフト大賞入賞)など多くのオンラインソフトを開発。著書に「Flexプロフェッショナルガイド」「なでしこ公式バイブル」、「一週間でマスターするActionScript3.0」など。

TrackBack URL :