== この文書はLuaバージョン4の内容です ==

Lua言語の手引き

― プログラミング言語Lua(4.0)についての基礎知識

TeCGraf, PUC-Rio

訳2001年10月24日

1. はじめに

この文章は、フリーソフトウエアLuaのホームページから配布されているポルトガル語で書かれたLuaの入門書の和訳です。Waldemar Celes Son、 Luiz Enrique de Figueiredo、 Robert Ierusalimschy によって書かれた論文"Programming in Lua - Practical Theory (version 2.1) " および "The Language of Extension Lua "の内容を含んでおり、Lua version 3.1 への対応は Anna Magdalena Hester による文 書をもとに Robert de Beauclair Seixas によって行われました。日本語への翻訳は、原著者の許可を得て、大阪大学大学院の池田 徹志さんと、産業技術総合研究所 阿部 幸絵さんの協力のもとに、産業技術総合研究所 上野 豊 によって行われました。

     ftp://ftp.tecgraf.puc-rio.br/pub/lua/nocoes-3.1.pdf 
     http://www.tecgraf.puc-rio.br/lua/ftp/nocoes-3.1.pdf

Luaはフリーソフトウエアとして配布されているプログラミング言語ですが、 TeCGrafが著作権を保持しています。

     ftp://ftp.tecgraf.puc-rio.br/pub/lua/lua-3.1.tar.gz 
     http://www.tecgraf.puc-rio.br/lua/

Copyright (c) 1994-1998 TeCGraf, PUC-Rio. Written by Waldemar Celes Filho, Roberto Ierusalimschy, Luiz Henrique de Figueiredo. All rights reserved.

2. 概要

Lua言語は、C 言語などで書かれたアプリケーションプログラムに組み込んで、初期設定や機能拡張を実現するために開発されたプログラミング言語です。アプリケーションプログラムに、Lua言語の処理系を組み込むことで、パラメータ設定やカスタマイズした処理をLua言語で記述することができます。また、単独で動作するLua言語インタプリタを使って、目的の処理を行うプログラ ムを作成することもできます。 Lua言語は、いわゆる関数型のプログラム言語で、処理の流れは、Pascal言語に似た簡素な文法になっています(if-elseif-else-end, while-do-end, repeat-until)。そして、データ記述方法が豊富で、便利な機能があります。 (1)文字列データの 処理を容易にするパターンマッチング (2)テーブル型デー タによる構造化したデータの記述 (3)テーブル内容の動的な変更が可能 (4)自動メモリ割り当てとガベージコレクション メ モリの再配置や初期化などは言語処理系の持つガーベジコレクション機能によっ て、メモリや変数の定義などに煩わされることなく、多様に構造化したデータを扱うプログラミングができます。テーブル型データは、データを抽象化するのに便利です。Lua言語のプログラミングでは標準関 数やその他のライブラリ関数が利用できますが、独自の関数を追加することもできます。 これまでにLua言語を使ったシステムもいくつか開発されています。例えばCGILua は、高度なWebサイトを構築できるシステムですが、Lua言語によるスクリプトを使って、動的なHTML文書を生成できるようになっています。

     http://www.lua.org

3. プログラムと実行環境

Lua言語のプログラムは、命令文と関数の定義で構成されます。それらは一つの実行環境の中に作成されます。命令文はテキスト中のどのカラムにあってもよく、フリーな書式で、スペースや改行で区切ったり、セミコロン ( ;) で明示的に区切ったりすることもできます。また、2つの連続したハイフン ( -) から行末までがコメントとなります。 Lua言語のインタプリタは、一つの実行環境に複数のプログラムを読み込むことができ、それぞれのプログラムは、読み込まれた順に翻訳され、それぞれの命令文が実行されます。そして、関数定義の中にある命令文は翻訳されたあと、関数のコードとして登録されます。また、大域変数や関数の登録されている実行環境は、インタプリタを動作させたときに作られ、利用を終了するまで有効で、それらの関数やデータは、Lua言語のプログラムやC 言語とのインターフェイ スのライブラリで、操作できます。 Lua言語の処理系では、実行単位のプログラムをチャンクと呼びますが、それを仮想マシンの実行コードに翻訳し、その後仮想マシンによってコードを実行します。登録されている関数のコードは、関数が呼び出された時に高速に実行されます。これは、コンパイラとインタプリタを融合した方式で、プログラムを逐次翻訳することで実行速度を低下させることなく、イン タプリタの使いやすい環境を保っています。

4. 変数と型

Luaのプログラムでは、大域変数は宣言なしで使うことができます。(注:後で説明する局所変数を使うこともできます)例えば、次の文で、新たに変数 a を使い、2.3という数値型のデータを参照するようにします。

     a =  2.3

そして次の例では変数aで文字列データを参照するように変更します。

     a = "Language Lua"

Lua言語では、変数には型がありませんが、変数で参照するデータの種類には型があり、 nil, 数値, 文字列, 関数, C の関数, ユーザデータ、 テーブルの7種類あります。

nil

nil型は 未定義データを表します。

     a = b

この場合は、変数 b の参照するデータは nil となるので、変数 a には nil が代入されます。次の例のように、 nil という予約語で表現することができます。

     a = nil

変数 a には nil という値が代入されますが、 a に値が入っ ていない状態と同じです。変数に値が入っていないかどうかは次のようにテストすることができます。したがって、すべての未定義の変数は nil と いう値を持つと考えられます。

     a == nil
数値

Lua で扱う数値データは実数と整数の区別はありません。次の例では、 a、 b、 c、d の各変数に数値を代入しています。

     a =4
     b =4.0 
    c =0.4e1 
     d =40e-1
文字列

Lua の文字列は一重引用符(' ')または二重引用符(" ")で囲まれる文字の系列 です。文字列中では、以下のエスケープシーケンスを用いることができます。

     \n     改行 
    \t     水平タブ 
    \r     復帰 
    \v     垂直タブ 
     \xxx   文字コードが 10 進数 xxx  の文字 
    \a     ベル 
    \b     一文字後退 
    \f     フォームフィード 
     \\     バックスラッシュ 
    \"     二重引用符  (") 
    \'     一重引用符  (') 
    \\     バックスラッシュ(\)

二重引用符で囲まれた文字列中では、一重引用符をエスケープ無 しでそのまま用いることができます。しかし、ある文字列を囲むのに用いられている引用符合をその文字列中で用いるときには、エスケープする必要があり ます。次の二つは文字列として有効であり、同一の文字列です。

     s = "Olho  d'agua" 
     s = 'Olho  d\'agua'

二重の角括弧も文字列を囲むのに用いられます。二重引用符や一重引用符と異なり、この中ではエスケープシーケンスが解釈されません。また内部に入れ子の文字列を含むこともできます。

例:

     s = [[This is a text
     that crosses
     a string more than and contains one 
     string nested:  [[nested]] in the end!]]
関数

Lua の関数はファーストクラスの型です。つまり、関数は変数に代入したり、他の関数へ引数として渡したり、戻り値として返したりすることができます。関数の定義は大域変数に値を代入することと同じで、関数の持つプログラムが値になります(6参照)。代入された大域変数は関数型の値を持ちます。関数定義は次のようになりますが、ここで、変数 func1に関数型の値が代入されることになります。

     function func1  (...) 
       ... 
     end
 
これは次のような関数呼び出しを行うことで実行できます。
 
     func1(...)
ユーザデータ

ユーザデータ型を用いることで、 C 言語の変数を Lua の変数に代入できます。 ユーザデータ型は C 言語の void * 型に相当し、C 言語の変数を Lua の変数で扱うことができます。代入と等価性の判定以外の演算以外は行うことができませんが、 Lua言語 と C を結合するプログ ラムを書くのに便利です。しかし、Lua 言語のみでプログラムを書く時には用いるこ とはできませんし、Lua言語 中でこの型の値を直接生成することもできません。

テーブル

テーブル型は Lua における主要な型です。テーブル型は連想配列を実現して おり、nil 以外の他の任意の型のインデクスを用いることができます。 テーブル型を用いることで、ベクトル、リスト、レコード等の型を同一の構造で作成できます。テーブル生成の構文を説明する前に1つ例を示します。以下は空のテーブルを明示的に作成して a に代入します。

     a = {  }

このテーブルに後からフィールドを追加することも可能です。 テーブルに関しては7節で詳述します。

5. 代入と演算

前述のように Lua の変数は型を持たず、変数はいろいろな型の値を保持します。Lua の変数は動的に型づけされ、値が変数に代入される時に特定の1つの型を持ちます。同じ変数に別の値が代入されれば、変数は2 度目に代入された値を持ちます。したがって2度目の代入の前には、その変数はある1つの型を持っていますが、代入の後には別の型の値を持っ ていることがあります。変数が持つ値に演算を行うとき、その値が行う演算に有効なものであるかどうかは実行時にチェックされます。例えば関数を値とし て持つ変数に加算を行おうとした場合には、実行時エラーになります(脚注2)。

−通常の代入と多重代入−

前節では簡単な代入の例を示しました。Lua では多重代入も可能で、 1回の代入で2つの異なる代入操作をすることができます。

     s, v =  "Language Lua", 2

この例は "Language Lua" という文字列を変数 s に代入し、 2 という数値を変数 vに代入します。 また、左辺の変数の数が右辺の値の数と異なることもあります。その場合には 左辺の変数に nil が入れられるか、右辺の値が捨てられます。

     a, b = 2 
     c, d = 2, 4, 6

この例では a に 2、 b に nil が代入されます。 また c には 2、 d には 4 が代入され、6 という値は捨てられます。多重代入を利用することで、変数の値の交換を1つの命令で書くことがで きます。

     a,  b = b, a

この例では a が持っていた値が b に、 b が持っていた値 が a に代入されます。よって、一時的な変数を用意する必要はありません。 (脚注2: この振舞いはタグ関数を用いることで変えることができる。 Lua 参照マニュアルを参照。)

−演算子−

Lua言語には、算術演算子、関係演算子、論理演算子、文字列を連結する演算子があり、ま演算が行われるときに、できるだけ自動的に型変換されます。

○算術演算

Lua には一般の算術演算子があります。2項演算子には + (加算)、 - (減算)、 *(積算)、 / (除算) があります。また符合反転の - があります。 演算順序を変えるため、括弧を使うことができます。 以下は正しい式の例です。

     a = 2 *  3.4 
     b = (a + 1) / 3.4 
     c = 2 *  (-3)  

算術演算子は数値型の値にのみ使用できます(数値型に変換可能な文字列に対しても可能です。"自動的な変換" の節を参照)。演算子 ^ は通常は巾乗で、Lua 言語の数学関数ライブラリが使われます。この2項演算子 ^ は高い優先順位で処理されますが、内部的には、タグ関数を使って実装されています(Lua 参照マニュアル参照)。

○関係演算子

Lua には以下の関係演算子があります。

             <       小さい 
             >       大きい 
             <=      小さいか等しい 
             >=      大きいか等しい 
             ==      等しい 
             ~=      異なる

関係演算子は、結果が偽の時 nil を返し、真の時 1 を返します。

     a = 4 < 3 
     b = 4 > 3   

この例では a に nil が代入され、 b に 1 が代入されま す。演算子 >,<,>=,<= は数値型または文字列型の値で使用でき、真偽の判断を行います。 そして、それはタグ関数を使うことで変更できます (Lua 参照マニュアルを参照)。 等価演算子 (==) は、はじめに比較される値の型が等しいかを調べ、 異なる場合には nil を返します。型が同じ場合には、値が等しいか を調べます。 数値や文字列の等しさは通常の基準で判断されます。テーブル、関数、ユーザデータは参照が比較されます。 例えば2つのテーブルの場合、その2つのテーブルが同一の対象であるかどうかで判断されるので、同じ要素を持っているかどうかでは判断されません。 非等価演算子 (~=) は等価演算子の逆です。

○論理演算子

論理演算子は”かつ”、”または”、”でない”の3つです。

         and     かつ 
         or      または 
         not     でない   

and や or が複数ある場合には、左から右に評価されるので、全ての演算が評価される前に、式の値が決まってしまうことがあります。

     a = 23 
     b = 15 
     c = a < 20  or  b > a 
     d = a < 20 and  b > a   

この例では、 c と d は nil になります。 d の場合、まず a<20 が偽となり、論理演算子 and によって結合されているので、b>a を評価する必要はなく、この式に偽になります。

○連結演算子

2つの文字列を連結する演算子は、Lua では2つのピリオド( ..) で 表されます。連結演算子は2つの文字列を連結した文字列を返します。

     a = "Language" 
     b = "Lua" 
     c = a .. " " ..  b

この例では a の値に1文字空白が続き、b の値がつながったもの、つまり "Language Lua" という文字列が変数 c に代入されます。

調整

Luaでは自動的に型の変換が行われることがあります。文字列に算術演算子が使用されていて、その文字列が数値の場合、数値型への変換がされて演算されますが、変換できなければ、実行時エラーとなります。

     b = "53" 
     c = 2 + b  

この例では文字列 "53" は演算の前に数値に変換され、 c には 55 が 代入されます(変数 b は文字列型の値を持ったままです)。 他方で、文字列が必要な文脈で数値を使った場合には(連結演算子など)、 数値は対応する文字列に変換されます。数値が完全なものであれば、小数や指数は点なしの文字列になります。 数値が分数を含む場合には、小数や指数を含むことがあります(脚注3)。 例えば、

     print("result:  " .. 23)

は次のように出力されます。

     result:  23.  

tonumber というLua言語の標準関数は、可能な場合は文字列を数値型に変換します。数値を文字列に変換するには、 必要に応じて tostring 関数を使うことができます(9参照、 sprintf で %.16g の書式で変換します)。複数の演算子が使われている場合は優先順位があり、次の順に演算されます。また括弧を用いて演算順序を変えることができます。 

     ^
     not   - (単項) 
     *     / 
     +     -
     ..
     <     >    <=    ~=    == 
     and   or  

6. 流れの制御と局所変数

Lua にはプログラム言語として一般的な処理制御の機能があり、一群の命令をまとめたり、さらに何回も実行したりすることができます。また Lua では、宣言されたブロック中のみで有効の局所変数の宣言をすることもできます。

条件文 ( if )

Lua の基本的な条件文は if です。 if 文の形式は次のようになります。

    if  exp then 
        ブロック 
     end  

または、

     if exp then 
       ブロック1 
      else 
        ブロック2 
      end  

または、

     if exp1  then 
        ブロック1 
      elseif exp2 then 
       ブロック2  
       ... 
      elseif exp N then 
        ブロックN 
      else 
       ブロックN + 1 
      end 

はじめの形式では、式 exp が nil でない値の場合にブロックが実行されま す。2番目の形式では、式 exp が nil の場合にブロック 2 が実行され、 そうでなければブロック 1 が実行されます。3番目の形式では、多くの条件にもとづいて if-then-elseif-then- ... -else-end で条件判断する列を示しました。上記の例では、式 exp1 が真ならば( nil で無いならば)ブロック 1 が実行 され、式 exp2 が真ならば ブロック 2 が実行され、...、全ての式が偽ならば ( nil と等しいならば) ブロックN+1 が実行されます。

最初に条件式がある繰り返し (while)

Lua には繰り返しを記述するのに2種類の命令があります。 while repeat です。 while では繰り返しの中身を実行するかどうかの判断が先頭でなされます。一般的な形式は次のようになります。

     while expr do 
       ブロック 
     end  

expr が真である間( nil でない間)、ブロック内の命令が実行 されます。

例として、変数 n の階乗を計算する次のプログラムを示します。

     f =  1              --- 1 の階乗 
     i =  n              --- 繰り返しを制御する変数 
     while i > 0  do 
      f = f * i 
      i = i - 1 
     end  

繰り返しを実行し終えた後には、 f は n の階乗の値を持ち、 i は 0 になります(繰り返しの終了条件)。

最後に条件式がある繰り返し (repeat)

繰り返しの中身を実行するかどうかの判断を最後に行うのが repeat て す。一般的な形式は次のようになります。

     repeat 
      ブロック 
     until expr

repeat では、ブロック内の命令は最低でも1回実行されます (繰り返しを実行するかどうかの判断は繰り返しの最後でしか行われません)。また式 expr が真である間、繰り返されます。 上記と同じ階乗の計算を行うプログラムは次のようになります。

     f =  1              --- 1 の階乗
     i =  1              --- 繰り返しの制御変数 
     repeat 
     f = f * i  
      i = i + 1 
     until i > n    
局所変数の宣言

Lua では変数が局所的であることを宣言して使用します。 この局所変数の宣言はブロック内の系列の任意の位置に書くことができ、宣言されたブロックが終わると変数も無効になります。 大域変数と同じ名前の局所変数を宣言すると、一時的に大域変数を隠します (局所変数が宣言されたブロック内で)。 プログラム中にその変数名が現れた場合には、局所変数として扱われます。局所変数の宣言時の初期化は次のような構文になります。 例として以下のプログラムを示します。

     a =  2                --- 大域変数に2を代入 
     if a > 0  then 
      local  b = a         --- a の値(2)を初期値として局所変数 b  を宣言 
      a = a + 1            --- 大域変数 a に 1  を加える 
      local  a = b         --- b の値を初期値として局所変数 a  を宣言
      print(a)             --- 局所変数 a  の値(2)を表示 
     end                   --- 局所変数 a, b の有効範囲はここまで 
 
     print(a)              ---  大域変数 a の値(3)を表示    

1つの命令で複数の局所変数を宣言して初期化することもできます。

     local  a, b, c  =  2,  5+6,  -3    

この例では、 a は 2 を初期値とし、 b は 11 を、 c は -3 をそれぞれ初期値としています。初期化されない局所変数の値は nil とな ります。

7. 関数

Lua では関数はファーストクラスの型として扱われます。つまり、関数は変数に代入したり、他の関数に引数として渡したり、戻り値として返したりするこ とができます。関数を定義する時には、関数の名前を持つ大域変数に関数の機能を実現するプ ログラムを代入します。Lua の関数は大域環境中の任意の場所で定義できます。関数の定義の一般的な形式は次のようになります。

     function name ([ 引数のリスト ]) 
      命令のブロック 
     end
   

ここで name は関数が代入される大域変数の名前です。引数のリストは関数内の局所変数と同様に扱われ、関数呼び出しの時の引数で初期化されます。 関数呼び出しの一般的な形式は次のようになります。

     name ([ 引数のリスト  ]) 

関数名の後に括弧で囲まれた0個以上の引数のリストを続けて書きます。 name が関数を値として持たない場合(関数型の値では無い場合)には実行時エラーになります(タグ関数参照)。引数が指定された場合には、 まず引数は関数呼び出しの前に評価されます。次に関数定義に指定された仮引数と、関数呼び出しに指定された実引数との間で対応が取られます (この時に多重代入と同様な調整が行われて対応が決まります)。 Lua では引数は値渡し(by value)で渡されます。したがって、関数が呼び出された時点での実引数の値が、関数の仮引数の値にコピーされて関数内の局所変 数になります。関数の呼び出し元では、引数に指定した変数の値は呼び出し時のままで変わりません。 Lua の関数は 0個、1個、またはそれ以上の値を return 命令によって返すことができます。よって、関数呼び出しの時に変数への参照を引数として 渡す必要がありません。関数の実行中に return 命令が現れると、関数 の実行は終了して関数が呼び出された位置の次に制御が戻ります。 return 命令の後には0個以上の式のリストを書くことができます。一般的 の形式は次のようになります。

     return [ 式のリスト ] 

構文規則上、 return 命令はブロックの最後でなければなりません。 このことにより、 return 命令の後に置かれて決して実行されない隠れた命令を防ぐことができます。 return 文がブロックの最後でない場合には、文法エラーになります。 関数の戻り値は、関数呼び出しの時に書かれた変数リストに合わせて調整され ます。例として、座標 (x, y) を更新する次の関数を示します。

     function translate  (x, y, dx, dy) 
      return  x+dx,  y+dy 
     end 

次のように関数呼び出しが行われたとすると、

     a,b = translate(20, 30, 1, 2)  

a には 21 (=20+1) が代入され、 b には 32 (=30+2) が代入されます。 次のような関数呼び出しの場合には、

     a = translate(20, 30, 1,  2) 

関数の(2つ目の)戻り値は捨てられ、21 を受け取ります。次のような呼び出 しの場合には、

     a, b, c = translate(20, 30, 1, 2)  

a には 21 が代入され、 b には 32 が代入され、 c には nil が代入されます。 複数の値を返す関数呼び出しは値のリストと同一に扱われるわけではありませ ん。多重代入で調整が行われる時には、1つ目の戻り値だけが考慮されます。例として、少しに異なる2つの例を挙げます。はじめに、次の関数呼び出しの場合には、

     a, b, c = 10, translate(20, 30, 1, 2)   

a には 10 が代入され、 b には 21 が代入され、 c には 32 が代入されます。このように関数呼び出しがリストの最後の場合には、関数呼び出しが1つずら されます。しかし、次の呼び出しでは、

     a, b, c = translate(20, 30, 1, 2),  10   

驚くかもしれませんが、関数が返す値の個数によらず1つ目の返り値だけが 扱われ(この場合21)、もう1つの返り値の 32 は捨てられます。よって、 a には 21 が代入され、b には 10 が代入され、cにはnilが代入されます。 Lua では可変個の引数を受け取る関数を定義することができ、これは関数 定義の引数リストの最後にピリオドを3つ ( ...) つけることで表せます。この形式の関数では実引数と仮引数の対応づけだけで調整は行われません。引数リストに指定された数以上の実引数は、 arg という名前の暗黙の仮 引数に対応づけられます。argは常にテーブルとして初期化され、 フィールド n は余分な引数の数、フィールド1, 2 ... に順に実引数 の値が対応づけられます。では例として、2つの異なる関数定義をします。

     function  f(a, b) end 
     function g(a, b, ...)  end 

これらの関数呼び出しでは、実引数と仮引数との間で次のような対応づけが行なわれています。

     実引数           仮引数 
     f(3)             a=3,  b=nil 
     f(3,  4)         a=3,  b=4 
     f(3, 4, 5)       a=3,  b=4 
 
     g(3)             a=3,  b=nil, arg={ n=0 } 
     g(3,  4)         a=3,  b=4, arg={ n=0  } 
     g(3, 4, 5, 8)    a=3,  b=4, arg={ 5,8; n=2  }   

8. テーブル型と連想配列

Lua のテーブルは連想配列であり、数値や文字列などの nil を除く任 意の型の値をインデクスとすることができます。テーブルのこの柔軟性は、 Lua 中のオブジェクトの記述の基礎になっています。

テーブルの作成

テーブルは使う前に明示的に作成する必要があり、 { }で初期化を行います。次は空のテーブルを作成し、t という変数に代入する例です。

     t = {  }   

これにより、変数 t はテーブル型の値を持ちます。 代入の例は次のようになります。

     t[1] = 13 
    t[45] = 56 

テーブルのインデクスの値に上限はなく、テーブルの次元は必要に応じ て自動的に変更されます。また、次のように書くこともできます。

     t["name"] = t[1] + 2 

name という文字列のインデクスに対応するテーブル中の値として、 インデクス 1に対応する値(ここでは 13)に 2 を加えた値を代入します。よっ てインデクス"name" に対応する値は 15 になります。 また以下のように書くこともできます。

     t[t] =  5

これは t というテーブル型のインデクスに対応する値として 5 を代入し ています。テーブル t はインデクスとして有効な値です (一見混乱しているように見えますが、やっていることは間違っていません)。 また文字列をインデクスにするときには、ピリオドを使った記法を用いること ができます。例えば、

     t["version"]=  4 

これは以下と等価です。

     t.version = 4   

この記法の方が分かりやすいので、使うことをおすすめします。 ただし、文字列がスペースを含むときはピリオドを使った表記を用いることはできません。例:

     t["Rio De  Janeiro "] = 1994 

数値と同様に、任意の型の値をテーブルに代入することができます。 次に示すのは、テーブルt への正しい代入の例です。

     t[10] = "example" 
     t[2.34] = 12 
     t.tabela, t[0] = 3, 12 
     t[-234] = 0 
     a = "Lua" 
     t[a] = 5  

最後の例は注意深く見る必要があります。 a は "Lua" という値を 持つ変数と考えると、これは Lua というインデクスに対応する値として5 を代入していることになります。 しかし、t.a はt["a"] と 等価ですが、t[a] とは異なります。 ここでは t[a] と t.Lua が等価にになります。 インデクスに対応する値を一度も与えられていない(初期化されていない)テー ブル中の値は nil になります。したがって上の例の代入だけを行ったとすると、

     print(t[999) 

999 というインデクスに対応する値は初期化されていないため、この命令は nil を出力します。 テーブル中に他のテーブルを代入することもできるので、多次元配列が実現で きます。例えば、次のようになります。

     m = { } 
     m[1]  = { } 
     m[2]  = { } 
     m[1][1] =  1.0 
     m[2][1] =  0.0 

また次のように書くこともできます。

     s = { } 
     s.dist = { } 
     s.dist.name =  "Language Lua" 
     s.dist.version =  4 
値を指定したテーブルの初期化

Lua ではテーブルの作成時に初期化することができます。 テーブルの初期化は次のように行うことができます。

     { 式1, 式2, ...., 式N  } 

このようにテーブルを作成する { } の間に式のリストを書きま す。リストの要素はそれぞれ評価され、1番目の値はインデクス 1 に対応づけられ、2番目の値はインデクス 2 に、というぐあいになります。 したがって次の例は、

     t =  {23, 45, -7, "Lua", 6+4} 

以下と等価です。

     t = { } 
     t[1] =  23 
     t[2] =  45 
     t[3] =  -7 
     t[4] =  "Lua" 
     t[5] =  6+4 

初期化を行った後でも、テーブルに他の値を代入することはできます。 テーブルにはいつでも nil 以外の任意のインデクスに対応して任意の値を代入することができます。例えば初期化の後に、次のような命令を実行す ることができます。

t[67] = 0 t["Lua"] = "example"

フィールドを指定したテーブルの初期化

Lua ではフィールドを指定してテーブルを初期化することもできます (訳注: インデクス i に対応する値を、テーブルのフィールド i とも呼びます)。 

一般的な形式は次のようになります。 
 
     { [ 式A ] = 式1, [ 式B ] = 式2...  } 

初期化の際に、インデクスと値の対であるフィールドを指定することができる。テーブルのインデクスが文字列の場合は、次のように単純化することができる。

     string1 = expr1 

よって次の例は、

     t = { name = "Language Lua  ", [1]= 3.0, [f(x)] = g(y) } 

以下と等価である。

     t = { } 
     t.name = " Language Lua "  
     t[1] = 3.0 
     t.[f(x)] = g(y) 

同様に、テーブルにはいつでも nil 以外の任意のインデクスに対応し て任意の値を代入することができます。例えば初期化の後に、次のようにすることができます。

       t["Lua"]="  example " 
両方の形式を混在させた初期化

これまでの2つの初期化の形式を組み合わせて使うことができます。 一般的な形式は次のようになります。

     table = { 式のリスト ; フィールドのリスト  } 

セミコロン で区切られた前半に、連続した数値インデクスで初期化 される式のリストを書き、後半にフィールドのリストを書きます。次の例は、

     t = { nome =  "Linguagem Lua", [1] = 3.0, [f(x)] = g(y) } 
   t = { 23,45,-7;
          nome="Linguagem  Lua", 
          versao=3.0, 
          [4]=80 
     } 

以下と等価です。

     t = {  } 
     t[1] = 23 
     t[2] = 45 
     t[3] = -7 
     t.nome = "Linguagem  Lua" 
     t.versao = 3.0 
     t[4] =  80 

Lua が生成するエラーは以下のように扱われて、まとめられてプログラマに提 示されます。 コンパイル中や実行時にエラーが起こると、エラー処理関数 _ERRORMESSAGE が実行され、Lua を実行する関数 (lua_dofile, lua_dostring, tolua_dobuffer, lua_callfunction) が終了され、エラーの状況が返されます。 Lua はエラー処理関数に対し、エラーの説明が書かれた文字列を引数として与 えます。初期時のエラー関数は単にその文字列を標準出力に出力します。 Lua の標準入出力ライブラリは関数呼び出しのスタックの履歴等の情報を表示 するため、エラー処理関数を再定義しています。Lua version 4 からは、関数呼び出しのスタックの履歴等の情報が得られるようになりました。

10.定義済みの関数

Luaでは常に利用できる関数が、プログラミングに有用な部品として用意されています。これらの関数は実行環境が変わっても有効です。

dofile(filename) 
filename   実行するファイル名 

指定されたファイルをLua言語のプログラムとして実行します。エラーで実行できなかった場合は nil が返されます。正常終了した場合はプログラムの戻り値をそのまま返しますが、プログラムの戻り値が nil の時には、nil でない何らかの値になります。

(例)

     if not dofile("bib.lua") then
         print("It is not possible to open the library of functions") 
     end 
dostring( string [, name ] )

文字列をLua言語のプログラムとして実行し、その戻り値を返します。実行が失敗した場合はnilとなり、成功ししてnilを返している場合は、nilでない何らかの値になります。 (例) botao1,botao2,...という名前のテーブルの内容を集約する。

     i  = 1; botao = { } 
     b  = dostring("return botao"..i ) 
     while b do 
        botao[i] = b 
        i = i + 1 
        b = dostring("return  botao"..i) 
     end 
next( table, index )

テーブルとそのインデクスを指定して、次の要素のインデクスと値を返します。indexにnilを指定すると、一番最初の要素のインデクスと値が得られます。これを使ってテーブルのすべての要素を列挙することができます。インデクスは数値の他に、文字列など他のデータ型を使うこともできます。戻り値は、インデクスと値の2つで、次の要素がなくなるとnilになります。Lua言語では、文字列のインデクスを使うときも宣言などは必要ありません。指定されたインデクスが存在しないときにはその値はnilという意味になりますが、next()では存在するものだけを順に得ることになります。要素の順番は、Luaによって管理されていて、作成した順ではありません。

(例) テーブルtの内容をすべてプリントするには次のようにします。

     field,value = next(t, nil)     --テーブルの最初の要素 
     while field do                 --存在するまで 
       print(field, "=", value)     --内容を出力 
       field,value = next(t, field) --次の要素 
     end
nextvar( name )

大域変数をすべて列挙するのに使います。1つのパラメータ、つまりnameで指定した大域変数の次の大域変数を得ます。nilを指定すると、一番始めの大域変数が得られます。戻り値は、変数の名前とその値の2つになります。

(例) 大域変数をすべて列挙するには次のようにします。

     name,value = nextvar(nil)       --先頭にある大域変数 
     while name do                   --nameが存在すれば 
      print(name, "=", value)        --その内容を表示 
      name,value = nextvar(name)     --次の大域変数 
     end
type( value )

引数に指定された対象の型を文字列で返します。戻り値は、"nil", "number", "string", "table", "function", "userdata" のどれかになります。

(例)

     t =  { } 
     print(type(2.4),type("Alo"),type(t),type(t[1]),type(print) 

(結果)

     number 
     string 
     table 
     nil 
     function
tonumber( e [,base] )

文字列型になっている数字を数値データに変換します。変換できなかった時はnilを返します。10進数以外の時は、2番目のパラメータに2〜32の数値を指定します。

(例)

     print(tonumber("34.56 "), tonumber("3.2 X"), tonumber(2)) 

(結果)

     34.56 
     nil 
     2 
tostring( e )

数値データを文字列データに変換します。

(例)

     print(tostring(3.2), tostring({10,20,30}),tostring(print))

(結果)

     3.2 
     table: 0x324a43 
     function: 0x63ed21 
print( expr1, expr2, ... )

与えられたデータを標準出力に出力します。出力の書式を整えるにはformat( )関数を使います。

(例)

     print( 2*3+2 ) 
     print( "valor = ", valor ) 
error( msg )

実行時エラーを生成し、与えられた文字列を標準出力に出力し、実行を中断します。

call( func, arg [,retmode] )

与えられた関数を、2番目の引数で実行します。arg はテーブルで、数値のインデクスの順に引数として利用されます。引数の必要ないときには空のテーブルを与えます。3番目のパラメータに"pack"を与えると、関数の戻り値はテーブルに入れられます。

     a = call( sin, {5} )                a = 0.871557 
     a = call( max, {1,4,5;n=2})           a = 4 
     t = { x=1  } 
     a = call( next,{t,nil;n=2},"pack" )   a={"x",1;n=2} 
assert( value [,message] )

引数で与えられたデータがnilならばエラーになり、2番目のパラメータに与えた文字列をプリントします。

(例)

     assert( readfrom(FILE),"cannot open" .. FILE )

11. ライブラリ関数

Luaでは、数学、文字列、ファイル入出力の3つの基本ライブラリ関数が用意されています。

文字列処理ライブラリ

文字キャラクタの列による文字列を扱う汎用の関数として、文字列の長さを調べたり、部分文字列をとったりする関数が使えます。

strfind( str, pattern [, init [, plain ]] )

文字列strの中に、patternで指定した文字列を探し、初めの場所と終わりの場所の2つの数値が戻り値になります。initで検索を始める最初の場所を指定できます。また、plainでnil以外だった場合は、パターンマッチングの機能を使わずに単純な文字列比較を行います。文字列が見つからなかった場合はnilが戻り値となります。パターンマッチにキャプチャを使った場合は、キャプチャに対応する文字列が戻り値に追加されます。

(例)

     i, f = strfind("Linguagem Lua 3.0", "Lua")
     print( i, f )

(結果)

     11 13,

(例)

     data = "13/4/1997"
     i, f, day, month, year = strfind(data, "(%d*)/(%d*)/(%d*)")
     print( day,month, year )

(結果)

     13, 4, 1997.
文字パターン

文字の種類を表現するために、以下の記述が用いられます。

     x                  (x が ^$()%.[]*+-? のどの文字でもない場合) 文字 x そのものを表します。
     %x                xはアルファベットや数字以外の記号で、その記号自体を表します。特殊文字を表すのに使います。
     .                  すべての文字
     %a                 アルファベット文字
     %A                 アルファベット文字以外
     %d                 数値(10進数)
     %D                 数値以外
     %l                 アルファベット小文字
     %L                 アルファベット小文字以外
     %s                 空白文字(スペース、タブ、quebras de linha)
     %S                 空白文字以外
     %u                 アルファベット大文字
     %U                 アルファベット大文字以外
     %w                 文字と数字
     %W                 文字と数字以外
     [文字パターン列]   指定した文字のうちのどれか
     [^文字パターン列]  指定した文字のどれでもない

パターン要素は、次のいずれかです。

・文字パターン

・文字パターンの後に * をつけたものは、その文字の 0 回以上の繰り返しを表します。マッチする範囲ができるだけ長い文字列になるようにします。

・文字パターンの後に - をつけたものは、その文字の 0 回以上の繰り返しですが、 * とは異なり、一致する範囲がで きるだけ短い文字列になるようにします。

・文字パターンの後に + をつけたものは、その文字の 1 回以上の繰り返しを表します。一致する範囲ができるだけ長い文字列になるようにします。

・文字パターンの後に ? をつけたものは、その文字の 1 回または0 回の出現を表します。

・% n ここで n は 1 から 9 までの数値で、n 番目のキャプチャした部分文字列を表します。

・%b xy ここで、 x と y は異なる文字で、 x で始まり y で終わる文字列を表します。例えば、 %b() は括弧の対応が正しく取れた文字列に一致します。

文字パターンの系列でひとつのパターンを表現できます。パターン先頭の ^ は対象文字列の先頭を意味します。パターン最後の $ は対象文字列の 最後を意味します。パターン中の他の位置に置かれた場合は、^ や $ は特別な意味を持たず、それ自身を表します。

パターン中には括弧で囲まれたパターンが含まれてい留場合は、キャプチャと呼ばれます。マッチングが起こった場合には、キャプチャの部分にマッチした文字列は保存され、後で用いることができます。キャプチャは左括弧に対応する番号がつけられます。

例えば、"(a*(.)%w(%s *))"では、a*(.)%w(%s *)に合致する部分文字が第1番目のキャプチャになり、2番目に(.)に合致した一つの文字、%s *が3番目になります。

(例)

     function name (arg1,arg2)
     (function %s*(%a+)(%b())) 
 

によって、Lua言語の関数定義は関数の名前 (%a+)と、そのパラメータリスト(%b()をキャプチャとして得ています。

strlen( str )

文字列strの長さを戻り値として返します。

(例)

     print(strlen("Linguagem Lua"))

(結果)

     13.
strsub( str, I [, j ] )

文字列strのl番目から番目までの文字で新たに文字列データを作って返します。j がない場合は最後の文字までとなります。文字列の最後を −1として数えて、に負の値を指定することもできます。例えば、strsub(str, 1, j) の場合は番目の文字までを返し、strsub(str, i) はi番目から後の文字を返します。

(例)

     a = "Linguagem Lua"
     print(strsub(a, 11))

(結果)

     Lua.
strlower( str )

文字列strを小文字にします。

(例)

     print(strlower("Language Lua"))

(結果)

     language lua
strupper( str )

文字列strを大文字にします。

(例)

     print(strupper("Linguagem Lua"))

(結果)

     LANGUAGE LUA.
strrep( str, n )

文字列strをn回繰り返した文字列を作ります。

(例)

     print(strrep( "0123456789", 2 ))

(結果)

     01234567890123456789
ascii( str [, I] )

文字列strl番目の文字のASCIIコードを返します。

(例)

     print(ascii("abc",2))

(結果)

     42.
format( formatstring, exp1, exp2, ... )

文字列formatstringに従ってそれ以降のexp1,exp2,の値を指定した書式で文字列に変換します。書式は%に続けて表示の型を示すアルファベット文字をつけた形式でC言語におけるprintfとほぼ同じです。変換した文字列を戻り値として返します。

(例)

     name = "Car"
     id = 123
     cmd = format( "insert into table (name, id)
                    values (%s , %d)" , name, id )

SQLの挿入コマンド(insert)となる文字列を、テーブルのidと名前を使って作っている例です。

     print( format( "%c", 65 ) )

このようにして、データの文字を表すANSI(American NationalStandard Code for Information Interchange) のコード変換が可能です。

(例)

     a = 123.456
     print ( format( "%+010.2f", a ) )

(結果)

     +000123.46.
 
     %s
     %q      では
     %s      文字列
     %q      文字列を引用符でかこむ
     %c      文字
     %d      10進整数
     %i      %dと同じ
     %u      inteiro sem sinal
     %o      8進整数
     %x      16進整数(abcdef)
     %X      16進整数(ABCDEF)
     %f      実数 [-]ddd.ddd
     %e      実数 [-]d.ddd e[+/-]ddd
     %g      実数 %fまたは %e
     %E      実数 %eと同じだが大文字のEを使う
     %%      %文字そのもの

書式制御のために、占める文字列の最小サイズを修飾子として%のあとに指定します。(former: %10d)負の値は、左づめを指定します。+を付けると数値の前に+か−を付けます。さらにピリオドに続けて、小数点以下の桁数を指定できます。(former: %.2f). 0を指定すると数値の左に0を追加します。

gsub( str, pattern, repl [, n ] )

文字列strからパターン文字列patternを探し、文字列replで置換します。replに関数を指定することもでき、関数を実行した結果で置換します。パターンはstrfindの時と同じで、パターンが見つかるごとに置換が行われますが、4番目に指定したで置換回数の最大を指定できます。パターンがキャプチャを含んでいると、それらはrepl文字列中にある%1,%2に展開されます。replが関数の時は、キャプチャされた文字列が関数のパラメータとして渡されます。戻り値は、置換した文字列と、置換した回数です。

(例)

     print( gsub( "Linguagem Lua 3.0", " ", "+" ) )

(結果)

     Linguagem+Lua+3.0.

(例2)

     texto = "    Linguagem       Lua       3.0 "
     print(  gsub( texto,  "  *",  "  " ) )

(結果2) 置換は行われないので、

     "Linguagem Lua 3.0 ".

(例3)キャプチャ機能

     lista  = [[
     arq.txt
     texto.txt
     r.txt
     ]]
     print( gsub( lista, "(.*)%.txt\n",  "move %1.txt %1.bak\n"  ))

(結果)

     move arq.txt arq.bak
     move texto.txt texto.bak
     move r.txt r.bak
数学ライブラリ

数値データの最大値、最小値を得る関数では、可変個数の引数を使うことができます。

     min( expr1, expr2, ..., exprN)
     max( expr1, expr2, ..., exprN)

(例)

     print(min(23, 5, 123, 3))
     print(max(23, 5, 123, 3))

(結果)

     3 123.

C 言語で利用されている数学関数ライブラリも利用できます。角度の単位はラジアンではなく度です。

     log(value)         logaritmo de value na base e
     log10(value)       logaritmo de value da base 10
     cos(angle)         cosseno de angle (especificado em graus)
     sin(angle)         seno de angle (especificado em graus)
     tan (angle)        tangente de angle (especificado em graus)
     acos(value)        arco cosseno, em graus, de value
     asin(value)        arco seno, em graus, de value
     atan(value)        arco tangente, em graus, de valuea
     tan2(y,x)          arco tangente, em graus, de y/x
     deg(angle)         converte angle (especificado em radianos) para graus
     rad(angle)         converte angle (especificado em graus) para radianos
     abs(value)         valor absoluto de value
     sqrt(value)        raiz quadrada de value
     ceil(value)        inteiro imediatamente inferior a value
     floor(value)       inteiro imediatamente superior a value
     mod(value,div)     resto da divis黍 inteira de value por div
入出力関数

オペレーティングシステムにおいて、ファイルあるいは装置からのデータの入出力を行うライブラリです。lua言語では、ファイルハンドルと呼ばれる変数でファイルを指定しますが、デフォルトの入力と出力の2つのファイルハンドルはファイルハンドルを省略することができます。デフォルトのファイルハンドルとして (_ INPUT)と(_ OUTPUT)の大域変数が使われます。C 言語での標準入出力である stdin、stdout、stderr も、大域変数 _ STDIN、_STDOUT 、_ STDERR で参照することができます。初期状態では_ INPUT は_STDIN に、 _ OUTPUT は_ STDOUTに設定されています。

readfrom( filename )

指定したファイルをデフォルトの読み取り用にオープンし、ファイルハンドルを_INPUTに設定します。filenameを指定しなかった時は、現在のファイルがクローズされてデフォルトの入力ファイルを_STDINに戻します。戻り値としてファイルハンドルが得られます。失敗したときはnilと、エラーメッセージの2つが得られます。

(例)

     readfrom( "c:\\txt\\b.txt" )
writeto( [filename ])

指定したファイルを書き込み用にオープンし、ファイルハンドルを_OUTPUTに設定します。ファイルが存在すればその内容は消去されて新しいファイルになります。成功するとファイルハンドルを戻り値として返します。失敗したときはnilと、エラーメッセージの2つが得られます。filenameを指定しなかった時は、現在のファイルはクローズされてデフォルトの出力を_STDINに戻します

(例)a.txt ファイルを新規作成して文字を書く。すでに存在すると、何もしない。

     if writeto( "a.txt" ) then
       write( "result is 123" )
       writeto( )
     end
appendto( [filename] )

指定したファイルを追加書き込みでオープンし、writeto()と同様にします。既存のファイルの内容は消去されません。

(例)a.txtというフィアルに文字を追記する。ファイルがなければ何もしない。

     if appendto( "a.txt") then
       write( "result is 123" )
       appendto ()
     end
read( [format] )

現在の入力装置_ INPUTからデータを読み込みます。読み込んだデータを戻り値として返します。失敗した場合は、 nilになります。formatは、次のいずれかの文字列です。

     "*l",     1行単位
     "*n",     単語単位
     "*w",     ファイルすべて
     "*n"      数値データ

指定のないときは行単位になります。また、数値を指定すると、指定したバイト数の読み込みをします。(訳者注:バージョン3.2まで利用されていたパターンでの読み込みは廃止されました)

(例)ファイルの数値をすべて読んでテーブルに入れます。

     data = { }
     i = 1readfrom( "datas.txt" )
     repeat
        data[i] = read( "*n" )
        i = i + 1
     until data == nil
     readfrom()
write( value1, value2, ...)

現在の出力装置 (_OUTPUT).に与えられたデータvalue1, value2, ... を出力します。失敗したときに戻り値はnilとなり、成功するとそれ以外の値となります。print()と違って、データは数値あるいは文字列でなければエラーとなります。

(例)

     write(  "a = ", a  )
remove( filename )

filenameで指定されたファイルを削除します。失敗したときは戻り値がnilになります。

(例)

     a, error = remove( "c:\doc\arq.txt" )
     if not a then
       print( error )
     end
rename( name1, name2 )

name1のファイル名をname2に変更します。失敗した時の返り値はnilになります。

(例)

     a, error = rename( "arq.txt", "arquivo.txt" )
     if not a then
       print( error )
     end

arq.txt を arquivo.txt に変更します。変更できなければメッセージを出力します。

tmpname( )

一時ファイルとして安全に使えるファイル名を返り値として返します。

(例)作業ファイルに書き込み準備する

     filename = tmpname( )
     writeto( filename )
date( [format] )

現在の日付と時刻を取得して文字列を戻り値として返します。書式は、unixのstrftime()関数で使われているもので、省略するとそのオペレーティングシステムで一般的な形式で出力します。

     %a        省略形の曜日
     %A        完全な曜日
     %b        省略形の月
     %B        月
     %c        一般的な日付と時刻
     %d        日付 01 〜 31
     %H        時 00 〜 23
     %I        時間 01 〜 12
     %j        今年の通算日数 001 〜 366
     %m        月 01 〜12
     %M        分 00 〜 59
     %P        午前午後 am/pm
     %S        秒 00 〜 60
     %U        今年の通算週数 日曜から 00 〜 53
     %w        今週の日曜からの日数  0 〜 6
     %W        今年の通算週数月曜から 00 〜 53
     %x        一般的な日付表記
     %X        一般的な時間表記
     %y        年の下2桁
     %Y        4桁の年
     %z        タイムゾーン
     %%        %自体を表す

(例)

     print( date( "day %d month %B" ) )

(結果)

     day 14 month August
exit( [code] )

プログラムの実行を終了して数値codeをオペレーティングシステムにおけるプログラムの終了値とします。codeを指定しないときは1です。この関数は戻り値はありません。

(例)大域変数termina=programaが設定されていると、プログラムを終了させます。

     if termina_programa then
       exit( )
     end
getenv( varname )

オペレーティングシステムから与えられる環境変数から、文字列varnameで示されるデータを戻り値として返します。環境変数が存在しないときはnilになります。

(例)

     print( getenv( "REMOTE_HOST" ) )

環境変数 REMOTE_HOSTをプリントします。

execute( command )

オペレーティングシステムにおいて、文字列 command を実行コマンドとして実行します。戻り値はコマンドの終了コードで、システムに依存します。

(例)

     execute( "mkdir c:\data" )

最後に

Luaの特徴の一つに、産業界と大学の両面で利用されていることがあげられます。PUC-Rioで開発されたプログラムは産業界では数十のプロジェクトに利用され、学術的にも多くの反響を呼び、多数の学位論文や学術論文に掲載されました。

著作権表示

Copyright (c) 1994-1998 TeCGraf, PUC-Rio. Written by Waldemar Celes Filho, Roberto Ierusalimschy, Luiz Henrique de Figueiredo. All rights reserved.

Permission is hereby granted, without written agreement and without license or royalty fees, to use, copy, modify, and distribute this software and its documentation for any purpose, subject to the following conditions:

* The above copyright notice and this permission notice shall appear in all copies or substantial portions of this software.

* The name "Lua" cannot be used for any modified form of this software that does not originate from the authors. Nevertheless, the name "Lua" may and should be used to designate the language implemented and described in this package, even if embedded in any other system, as long as its syntax and semantics remain unchanged.

The authors specifically disclaim any warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The software provided hereunder is on an "as is" basis, and the authors have no obligation to provide maintenance, support, updates, enhancements, or modifications. In no event shall TeCGraf, PUC-Rio, or the authors be liable to any party for direct, indirect, special, incidental, or consequential damages arising out of the use of this software and its authors be liable to any party for direct, indirect, special, incidental, or consequential damages arising out of the use of this software and its documentation.

[EOF]