この初版は Lua 5.0 向けに書かれています。後のバージョンにも概ね当てはまりますが、いくつかの違いがあります。
第4版は Lua 5.3 を対象としており、Amazon やその他の書店で入手できます。
本書をご購入いただくことで、Lua プロジェクトの支援にもなります。
![]() |
Luaプログラミング | ![]() |
第I部 言語 第6章 関数についてさらに詳しく |
Lua の関数は、適切なレキシカルスコープを持つファーストクラス値です。
関数が「ファーストクラス値」であるとはどういう意味でしょうか?Lua では、関数は数値や文字列のような従来の値と同じ権利を持つ値であることを意味します。関数は変数(グローバル変数とローカル変数の両方)やテーブルに格納でき、引数として渡すことができ、他の関数から返すことができます。
関数が「レキシカルスコープ」を持つとはどういう意味でしょうか?関数がそれを囲む関数の変数にアクセスできることを意味します。(また、Lua がラムダ計算を適切に含んでいることも意味します。)この章で見ていくように、この一見無害に見える特性は言語に大きな力を与えます。なぜなら、関数型言語の世界からの強力なプログラミング手法の多くを Lua で適用できるようになるからです。関数型プログラミングに全く興味がない場合でも、これらの手法を探求する方法を少し学ぶ価値があります。なぜなら、プログラムをより小さく、よりシンプルにすることができるからです。
Lua において少し難しい概念は、他のすべての値と同様に、関数は匿名であるということです。名前がありません。 `print` という関数名について話すとき、実際にはその関数を保持する変数について話しています。他の値を保持する他の変数と同様に、そのような変数をさまざまな方法で操作できます。次の例は、少し馬鹿げていますが、その点を示しています
a = {p = print} a.p("Hello World") --> Hello World print = math.sin -- `print' now refers to the sine function a.p(print(1)) --> 0.841470 sin = a.p -- `sin' now refers to the print function sin(10, 20) --> 10 20後ほど、この機能のより有用なアプリケーションを見ていきます。
関数が値である場合、関数を生成する式はありますか?はい。実際、Lua で関数を記述する通常の方法は、例えば
function foo (x) return 2*x endは、私たちが*糖衣構文*と呼ぶものの単なるインスタンスです。言い換えれば、単に
foo = function (x) return 2*x endを記述するきれいな方法です。つまり、関数定義は実際には、 `"function"` 型の値を変数に代入する文(より具体的には代入)です。 `{}` がテーブルコンストラクタであるように、式 `function (x) ... end` を関数コンストラクタと見なすことができます。このような関数コンストラクタの結果を*匿名関数*と呼びます。通常は関数をグローバル名に割り当てて名前のようなものを付けますが、関数が匿名のままになる場合があります。いくつかの例を見てみましょう。
テーブルライブラリは、テーブルを受け取り、その要素をソートする関数 `table.sort` を提供します。このような関数は、昇順または降順、数値またはアルファベット順、キーでソートされたテーブルなど、ソート順序に無制限のバリエーションを許可する必要があります。あらゆる種類のオプションを提供しようとする代わりに、 `sort` は単一のオプションパラメータを提供します。これは*順序関数*です。2つの要素を受け取り、最初の要素がソートで2番目の要素の前に来るかどうかを返す関数です。例えば、次のようなレコードのテーブルがあるとします。
network = { {name = "grauna", IP = "210.26.30.34"}, {name = "arraial", IP = "210.26.30.23"}, {name = "lua", IP = "210.26.23.12"}, {name = "derain", IP = "210.26.23.20"}, }`name` フィールドで逆アルファベット順にテーブルをソートしたい場合は、次のように記述します。
table.sort(network, function (a,b) return (a.name > b.name) end)その文で匿名関数がどれほど便利かをご覧ください。
`sort` のように、別の関数を引数として受け取る関数は、*高階関数*と呼びます。高階関数は強力なプログラミングメカニズムであり、関数引数を作成するための匿名関数の使用は、大きな柔軟性の源です。しかし、高階関数には特別な権利がないことを忘れないでください。それらは、Lua が関数をファーストクラス値として扱うことができるという単純な結果です。
引数としての関数の使用を説明するために、一般的な高階関数である `plot` の単純な実装を作成します。これは、数学関数をプロットします。以下に、ANSI端末に描画するためにいくつかのエスケープシーケンスを使用したこの実装を示します。(このコードを端末の種類に適合させるために、これらの制御シーケンスを変更する必要がある場合があります。)
function eraseTerminal () io.write("\27[2J") end
-- writes an `*' at column `x' , row `y' function mark (x,y) io.write(string.format("\27[%d;%dH*", y, x)) end
-- Terminal size TermSize = {w = 80, h = 24} -- plot a function -- (assume that domain and image are in the range [-1,1]) function plot (f) eraseTerminal() for i=1,TermSize.w do local x = (i/TermSize.w)*2 - 1 local y = (f(x) + 1)/2 * TermSize.h mark(i, y) end io.read() -- wait before spoiling the screen endこの定義があれば、次のような呼び出しでサイン関数をプロットできます。
plot(function (x) return math.sin(x*2*math.pi) end)(値を適切な範囲に入れるために、データを少し調整する必要があります。) `plot` を呼び出すと、そのパラメータ `f` は指定された匿名関数の値を取得し、それが **for** ループ内で繰り返し呼び出されてプロットの値を提供します。
Lua では関数はファーストクラス値であるため、グローバル変数だけでなく、ローカル変数やテーブルフィールドにも格納できます。後ほど見ていくように、テーブルフィールドでの関数の使用は、パッケージやオブジェクト指向プログラミングなど、Lua の高度な使用のための重要な要素です。
Copyright © 2003–2004 Roberto Ierusalimschy. All rights reserved. 著作権 © 2003–2004 Roberto Ierusalimschy. 無断転載を禁じます。 | ![]() |