Lua を使ってみよう

prev index next

Luaとのめぐり会い

書店の情報処理コーナ、平積みになっている本の中に「入門 Luaプログラミング入門 上野豊 著」というのが目に付きました。また新しい言語が出できたのかとちょっとぺらぺらと捲って見て...、とりあえず、購入はしたけれどそのままになっていました。しばらくしてからふと思い出して Lua でもやってみようかと始めてみたところ、なんとこれはおもしろい、しばらくは Lua にハマッてしまいました。とりあえずですが理解できたことを紹介します。

使用しているソフト

Luaとは

Lua はブラジル、リオデジャネイロ、カトリカ大学(PUC-Rio) の情報工学科コンピュータグラフィックステクノロジーグループ TeCGraf で設計開発されたスクリプト言語です。C言語のプログラムに組み込まれることを目的に設計されたということですが、ここではC言語の拡張機能的な使い方に注目しています。ちなみに Lua という名前は、ポルトガル語で という意味だそうです.
      特徴としては、
    
    ・覚えやすい
    ・コンパクトで処理がはやい
    ・C言語と接続しやすい

   などが挙げられていますが、使用しての実感としては「フットワーク」がよいといったところです.

インストール

ここでは Cygwin にインストールします. インストール方法は Linux に準じて行います.
  ソースプログラムの入手先:

    http://www.lua.org/        Lua 5.1.3 が最新です (2008年3月)

     http://hammm.dw.land.to/lua/   Lua 5.1.3 の Shit-JIS 対応版です

    ここでは Shift-JIS 版を使用します。ダウンロードして解凍します.インストール方法は同じです.

  lua-5.1.3.tar.gz (lua-5.1.3-mbenc.zip) を解凍したディレクトリに移動して

         make linux

        make test

        make install

  以上で /usr/local/bin、 /usr/local/lib、 /usr/local/include にインストールされます.

  動作の確認は、

      $ lua
        Lua 5.1.3  Copyright (C) 1994-2008 Lua.org, PUC-Rio
        >

Lua プログラミング

実際に Lua で作成したプログラムです.三角形の3辺を入力してヘロンの公式を使って面積を求めます.

  triangle.lua

 -- Helonの公式を使用して三角形の面積を求める 

 --   面積を求める関数の定義
 function Heron(a, b, c)
    local area , s
    if a+b > c and b+c > a and c+a > b then
       s = (a + b + c ) / 2
       area = math.sqrt(s*(s-a)*(s-b)*(s-c))
    else
       area = -1  
    end
    return area
 end

 -- データの入力
 io.write("三角形の3辺 a b c -> ")
 line = io.read()

 -- [[文字列として入力した数値を分解してテーブルに格納する.  空白を区切りとして split する]]--
 --   分解された数値は文字のため数値に変換しておく
 t={}
 string.gsub(line,"(%S+)", function (v) table.insert(t,tonumber(v)) end)

 -- 関数の呼び出し
 ss = Heron(t[1], t[2], t[3])

 if ss > 0 then
     print("面積 = ".. ss)
   else
     print("data error")
 end

  実行結果.
 $  lua triangle.lua
 三角形の3辺 a b c -> 3 4 5
 面積 = 6
Lua プログラミングについては「入門 Luaプログラミング入門 上野豊 著」をはじめとして、インターネット上からも入手可能なので、言語の詳細な説明はそれらを参照していただくこととして、ここでは、ある程度のプログラム言語の知識があることを想定して Lua の特徴的なこと思われることを取り上げててみたいと思います.
  Lua には対話型とファイルから実行する方法があります.

   ・対話型実行

       $ lua
       Lua 5.1.3  Copyright (C) 1994-2008 Lua.org, PUC-Rio

    "hello Lua World!" を表示する

       > print("Hello Lua World!")
       Hello Lua World!

    ・ 5 + 3 の演算結果を表示する方法3通り

       > print 5 + 3
       8

       > return 5 + 3
    8

       > = 5 + 3
       8

    ・ファイルからの実行

      作業用のディレクトリを設けて テキストエディタで次の1行を入力します

   test.lua

  print("Hello Lua World!")
カレントディレクトリに保存して実行します.
 $ lua list.lua

  Hello Lua World!
ファイルの拡張子はなんでも構わないようですが lua としておきます.

Lua を使用してみる

 Lua の変数は何にでも化ける ?

> =type(a) -- 最初の状態 nil > a=123 -- 数値 > =type(a) number > a="abce" -- 文字列 > =type(a) string > a=2>3 -- 論理値 > =type(a) boolean > a=print    -- 関数 > =type(a) function

 複数の代入ができる

    > a, b, c = 1, 2, 3 > = a, b, c 1 2 3 > a, b = b, a -- 値の入れ替え (swap) > = a, b 2 1

 数値の 0 は true

    > a = 0 > if a then print("true") else print("false") end true > a = nil > if a then print("true") else print("false") end false

 こんな代入文も

> a = a or 111 -- aは未設定 > = a 111 > a = 222 > a = a or 111 -- aは既値 > = a 222

 文字列の連結

> = "abc".."123".."xyz" abc123xyz > a = 123 > = "abc" .. a .. "xyz" abc123xyz

 配列はテーブルで表す

> t={11, 22, 33} -- 要素は数値 > =t[1], t[2], t[3] 11 22 33 > t={"aa", "bb", "cc"} -- 要素は文字(列) > =t[1], t[2], t[3] aa bb cc > t={"aa",22}; t[4]=false -- 混合.t[3]は要素が欠けている > =t[1], t[2], t[3], t[4] aa 22 nil false  > t={apple=220, banana=180, grape=250} -- keyを持った要素 > = t["apple"], t.banana -- 2つの参照方法 220 180

 テーブルの要素に関数を使用する

    > a = print > t = { a, function() print("abc") end, function(v) print(v) end } > = t[1](123) 123 > = t[2]() abc > = t[3]("xyz") xyz

 テーブルの操作

> t={111, 222, 333} > table.remove(t, 3) -- 2番目を削除 > table.insert(t, 2, 444) -- 2番目に挿入 > for i=1,#t dp print(i,t[i]) end 1 111 2 444 3 222 > table.sort(t) -- 昇順にソートする > for i=1,#t do print(i,t[i]) end 1 111 2 222 3 444

 テーブルの全要素を出力 - 3通り

    > t={apple=220, banana=180, grape=250}  -- 元のテーブル > for i,v in pairs(t) do print(i,v) end apple 220 grape 250    banana 180 > i,v=next(t) > while (i) do print(i,v) i,v = next(t,i) end apple 220 grape 250 banana 180 -- レファレンス マニュアルには記載されていないが、 > table.foreach (t,print) apple 220 grape 250 banana 180     t={"aa",22}; t[4]=false のように key がない場合は > for i = 1,#t do print(i,t[i]) end -- #t : テーブル t の要素数     1 aa     2 22 3 nil 4 false

 文字列の検索・置換

> s="Hello Lua World" -- 元の文字列 > p,q = string.find(s,"Lua") -- "Lua" を検索 > =p, q -- 検索結果 7 9 -- ヒットした文字位置 > = string.sub(s, p, q) -- 部分の切り取り Lua > p,q,r=string.find(s,"(L%w+)") -- capture (検索結果の取り込み) > = p, q, r 7 9 Lua > = string.gsub(s,"Lua","Happy") -- "Lua" を "Happy" に置換 Hello Happy World 1 -- 置換後の文字列と置換回数 > = string.gsub(s,"(%w+)", print) -- captureした値を関数 print に渡す Hello Lua World Hello Lua World 3 -- 元の文字列とヒットした数 > = string.gsub(s,"(%w+)", "value=%1") -- 取り込んだ値を %1 として value=Hello value=Lua value=World 3 "L"で始まる語を検索して印字する. 置き換えに関数を定義できる > string.gsub(s,"(L%w+)", function (v) print ("found : " ..v) end ) found : Lua

 文字列の結合・分解

  ・文字列の結合にはtabel.concatが標準で用意されています > s = {"aa","bb","cc"} > = table.concat(s, ",") aa,bb,cc   ・文字列の分解には(例えば)下記のような関数splitを利用します     > function split(str, d) >> local s = str >> local t = {} >> local p = "%s*(.-)%s*"..d.."%s*" >> local f = function(v) >> table.insert(t, v) >> end >> if s ~= nill then >> string.gsub(s, p, f) >> f(string.gsub(s, p, "")) >> end >> return t >> end > > t = split("aa,bb,cc", ",") > for i,v in pairs(t) do print(i,v) end 1 aa 2 bb 3 cc

 文字列の操作のプログラム例

 HTML形式の文字列の "<BODY> 〜 </BODY>" 部分の "<TAG>" を除いて表示する.  disp-text.lua
-- 関数の定義>
function body_text(str)
   local s, p = 1, q, tag_val         -- p = 1 検索開始位置

   -- "<BODY>" のパターンを検索し"<BODY>〜" の文字列を作成する
   while  true  do
      _, q, tag_val = string.find(str,"<(.-)>", p)   -- "_" はダミー変数

      if string.lower(tag_val) == "body" then
         s = string.sub(str, q+1)     -- "<BODY>〜" の部分の文字列を作成する
         break
      end
      p = q + 1                       -- 次の"<>"の検索開始位置
   end

   -- "<>" の部分を "" に置き換えた(すなわち削除した)文字列を戻り値とする
   return (string.gsub(s, "%b<>", ""))     -- return ()で戻り値は1個だけ
end

-- テストデータと実行
s = [[<HTML><TITLE>文字列検索・置換</TITLE>
<BODY><P>月日は百代の過客にして</P><BR>
<P>行き交う人もまた</P><BR>
<P>旅人なり</P>
</BODY></HTML>]]

print(body_text(s))                   -- 関数呼び出し、印字
 実行結果
 $ lua script-4.lua
 月日は百代の過客にして
 行き交う人もまた
 旅人なり

 関数の可変引数あつかい

> function f(...) -- 可変引数の関数定義 >> local a = {...} >> local sum = 0 >> for i = 1,#a do -- #a : aの要素の数 >> sum = sum + a[i] >> end >> return sum >> end > > print(f(1,2,3,4,5)) -- 関数呼び出し 15

 可変引数の個数を知る

可変引数の個数を参照できる(selectは複数の返却値を持つ関数) > function f(a,b,...) print(a,b) for i=1,select("#",...),2 do -- select("#",...) 可変部分の個数 x,y = select(i,...) -- 可変部分のi,i+1番目 print (x,y) end end > f(11,22,33,44,55,66) 11 22 -- a,b の出力 33 44 -- 可変1,2番目の出力 55 66 -- 可変3,4番目の出力

 関数の再帰呼び出し - nの階乗を求める

> function fact(n) -- 関数の定義 >> if n == 0 then return 1 >> else return n * fact(n-1) end >> end > = fact(5) -- 関数の呼び出し 5!     120

 入出力 - 標準入出力

・画面から1行読み込んで表示する > while true do s = io.read() -- 1行入力 if s == nil then break end -- EOD(Ctrl+D)で終り io.write(s, "\n") -- 1行出力.(改行を選択できる) end aaaa -- 入力文字列 aaaa -- 出力文字列 bbbbbbb bbbbbbb ccccc ccccc > -- Ctrl+D 入力で終了

 ファイルの入出力

  ・テキストファイル a.txt を読み込んで b.txt に書き出す(file copy)    io.input ("a.txt") io.output("b.txt") for line in io.lines() do io.write(line .. "\n") -- 改行コードを付加する end ・ファイルハンドラを使用した例 f, msg = io.open("a.txt","r") -- f: ファイルハンドラ g = io.open("b.txt","w") -- g: ファイルハンドラ if f then local s = f:read("*a") -- "*a" : ファイル全体を読む g:write(s.."\n") else print(msg) -- エラーメッセージ end f:close() g:close()

 コマンドラインのパラメタ

  cmdline.lua
 -- コマンドラインからのパラメタは テーブル arg に格納される
    for i = 0, #arg do                          -- #arg : パラメタ数
        print (i, arg[i])
    end
  実行結果    $ lua cmdline.lua aa 123 b.txt c.lua    0 cmdline.lua    1 aa    2 123     3 b.txt     4 c.lua

Lua と C 言語の連携

C言語をメインに Lua を使用する最大の魅力は C言語との連携だと思います.前述のように Lua での標準入出力おとびコマンドラインからのパラメタの参照が分かれば一応の接続はできるわけですが Lua では C言語とのインターフェースが比較的楽にできるのでここでは基本的なパターンを例示します.
  ・ C言語から Lua の関数を利用する
  ・ Lua から C言語の関数を利用する
  ・ C言語関数を共有ライブラリ(DLL)として利用する

 C言語から Lua の関数を利用する

 最初に挙げた三角形の面積を求める例題から面積計算の関数部分を Heron としこれを C言語から実行します.   script-1.lua
 --   面積を求める関数の定義
 function Heron(a, b, c)
    local area , s
    if a+b > c and b+c > a and c+a > b then
       s = (a + b + c ) / 2
       area = math.sqrt(s*(s-a)*(s-b)*(s-c))
    else
       area = -1  
    end
    return area
 end
 Helon を呼ぶ C言語のプログラム   list-1.c
 #include <lua.h>
 #include <lauxlib.h>
 #include <lualib.h>
 #include <stdio.h>
 int main()
 {
        lua_State *L;
        double a, b, c, s;

        L = lua_open();
        luaL_openlibs(L); 

      /* Lua fileを読み込む */
        if(luaL_dofile(L, "script-1.lua")){
           printf("load error\n");
           return -1;
        }

      /* データ入力 */
        printf("enter a,b,c -> ");
        scanf("%lf %lf %lf", &a, &b, &c);

      /* 関数名 "Heron" をスタックに積む */
	lua_getglobal(L, "Heron");

      /* 関数呼び出しのパラメタセット */
        lua_pushnumber(L, a);
        lua_pushnumber(L, b);
        lua_pushnumber(L, c);

      /* 関数呼び出し */
        if( lua_pcall(L, 3, 1, 0) ) {             // 呼び出しパラメタ 3、返却値 1
            printf("%s\n", lua_tostring(L, -1) );
            lua_pop(L,1);
        }

      /* 返却値取得 */
        s = lua_tonumber(L, 1);
        
      /* 結果の出力 */
        if (s >= 0){
             printf("area=%f\n", s);
        }
        else {
             printf("input data error\n");
        }

        lua_close(L);
 }
 コンパイルと実行
 $ gcc -o list-1 list-1.c -llua

 $ ./list-1
 enter a,b,c -> 3 4 5
 area=6.000000

 Lua からC言語の関数を利用する

 C言語で定義された関数 foo を Lua から利用する.C言語から Lua を起動して実行する.   list-2.c
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

/* Lua から呼ばれる関数の定義 */
int foo(lua_State *L) {
         int n = lua_gettop(L);           /* 引数の数 */
         lua_Number sum = 0;
         int i;
         for (i = 1; i <= n; i++) {
           if (!lua_isnumber(L, i)) {
             lua_pushstring(L, "関数 `average' の引数が正しくありません");
             lua_error(L);
           }
           sum += lua_tonumber(L, i);
         }
         lua_pushnumber(L, sum);          /* 最初の戻り値 */
         lua_pushnumber(L, sum/n);        /* 2番目の戻り値 */
         return 2;                        /* 戻り値の数 */
}

int main()
{
     lua_State* L;

     /* initialize Lua */
        L = lua_open();

     /* load Lua base libraries */
	luaL_openlibs(L);

     /* foo を登録	*/
        lua_register(L, "foo", foo); 

     /* Lua スクリプトを読み込んで実行 */
        if(luaL_dofile(L, "script-2.lua")){
           printf("load error\n");
           return -1;
        }

        lua_close(L);
}
  script-2.lua
-- script-2.lua

-- C言語の関数 foo を利用する
sum, ave = foo(1, 2, 3, 4, 5)

-- 関数の実行結果 sum、ave を印字する
print("sum=" .. sum .. " : ave= " .. ave)
  コンパイルと実行
 $ gcc -o list-2 list-2.c -llua

 $ ./list-2
 sum=15 : ave= 3

 Lua からC言語で作成したDLLを使用する

 前述の関数 foo の部分をダイナミックリンクライブラリ (DLL) にして Lua から使用する.起動は Lua から.   list-3.c
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

int foo(lua_State *L) {
         int n = lua_gettop(L);           /* 引数の数 */
         lua_Number sum = 0;
         int i;
         for (i = 1; i <= n; i++) {
           if (!lua_isnumber(L, i)) {
             lua_pushstring(L, "関数 `average' の引数が正しくありません");
             lua_error(L);
           }
           sum += lua_tonumber(L, i);
         }
         lua_pushnumber(L, sum/n);        /* 最初の戻り値 */
         lua_pushnumber(L, sum);          /* 2番目の戻り値 */
         return 2;                        /* 戻り値の数 */
}
  script-3.lua
-- script-3.lua

f = package.loadlib("list-3.dll", "foo")

ave, sum = f(1,2, 3, 4, 5)
print("ave="..ave, "sum="..sum)
  コンパイルと実行
 $ gcc -shared -o list-3.dll list-3.c -llua

 $ lua script-3.lua
 ave=3   sum=15

データベース Sqlite3 の利用

  Luaでデータベースを利用する方法としては、WindowsではLuaSqlがありますが、Linux(Cygwin)では Lua-Sqlite3 が使用できます。
  詳細はlua-sqlite3のDocumentにあります。

 インストール

  ・Sqlite3

   Sqlite3の最新版は http://www.sqlite.org/download.html から入手できます。2008.7.10 時点での最新版は Version 3.5.9 が
   安定版と銘打っていますのでこれを任意のディレクトリにダウンロードします。
    sqlite-3.5.9.tar.gz       (ソースプログラムを使用)

     ダウンロードしたディレクトリで以下の作業を行います

   $ tar xzvf sqlite-3.5.9.tar.gz     (解凍)

     $ cd sqlite-3.5.9

     $ ./configure --disable-tcl

     $ make

     $ make install

     インストール先は /usr/local/bin、 /usr/local/lib、 /usr/local/include です。

   $ sqlite3 --version
    3.5.9

     と表示されたら sqlite3 は動作しています.
  ・Lua-Sqlite3

   Lua-sqlite3は LuaForgeの http://luaforge.net/projects/lua-sqlite3 から入手できます。現時点では version 0.4.1で開発段階は
   alpha となっています。最新の release は May 11, 2006 となっています。これで完成なのか中断されたのかは分かりませんが
   とりあえず使用することにします。
  lua-sqlite3-0.4.1.tar.bz2       (ソースプログラムを使用)

     ダウンロードしたディレクトリで以下の作業を行います

   $ bzip2 -dc lua-sqlite3-0.4.1.tar.bz2 | tar xvf -     (解凍)

     $ cd lua-sqlite3-0.4.1

     $ ./configure

     $ make

     $ make install

    インストール先は /usr/local/lib/lua です。
  ・Cygwin でのインストール

   Cygwin 用としては提供されているわけではないのですが linux に準じてインストールします。 sqlite3 では make の段階で大量
   のwarning が出ましたが一応正常に動作するようです。 lua-sqlite3 も インストールできました。問題があるかも知れませんが
   とりあえず使用してみました。    実行してみてエラーがでるので次の修正をしています.

   /usr/local/lib/lua の ファイル libluasqlite3.so に対して

   $ chmod 766 libluasqlite3.so       (インストール時は744)

プログラムでの使用例

  テストのデータベース sample.dbは

gcodegnameprice
1001apple 2180
1002banana1155
1003peach2324
1004plum3185
1005orange5220
1006grape3358
1007pear2253
1008mango4423


  データベースの作成手順ははOpen COBOLでSQLite3を使おう 1に記載してありますのでここでは省略します。

  test.lua
  require ("sqlite3")

  db = sqlite3.open("sample.db")

  for row in db:rows("SELECT * FROM fruits") do
    print(row.fcode, row.fname, row.price)
  end

  db.close(db)
  カレントディレクトリに test.lua と sample.db を置いて実行します。require ("sqlite3") はこのまま実行するとsqlite3.luaがカレント
  ディレクトリにあるもとして実行しますのでエラーになります。 sqlite3.luaの格納ディレクトリは/usr/local/lib/luaですのでこの位置を
  示すために
  $ export LUA_PATH="/usr/local/lib/lua/?.lua"
  を実行します。ちなみにexportで複数のディレクトリを指定する必要がある場合は";"で区切って書きます。

  ※ プログラムの中で package.path = "/usr/local/lib/lua/?.lua" と指定することでもできます。

 プログラムの実行と結果
$ lua test.lua
1001    apple   2180
1002    banana  1155
1003    peach   2324
1004    plum    3185
1005    orange  5220
1006    grape   3358
1007    pear    2253
1008    mango   4423

参照資料・その他

 ・ 入門 Luaプログラミング   上野 豊 著     ソフトバンククリエイティブ  (2008.1)
  ・ Lua 5.1 リファレンスマニュアル                                 (2006.10)
  ・ Lua 5.1 Reference Manual(英文)                                          (2008.1)
 ・ Programming in Lua  by Roberto Ierusalimschy(英文)
 ・ lua-users wiki Lua Tutorial(英文)

  ・ Lua は MIT ライセンスです
 ・ Lua-sqlite3 は MIT/X Consortium ライセンスです
 ・ そのほかこのホームページに記載した内容については引用、転載は自由です。利用者の責任でご利用願いします。

prev index next


2008年 3月29日 初版 アップロード 2008年 7月 6日 データベースの利用 追加

Copyright (C) sanpontze. All Rights Reserved