この初版はLua 5.0用に書かれました。後続のバージョンにも大部分は適用されますが、いくつかの違いがあります。
第4版はLua 5.3を対象としており、Amazonおよびその他の書店で入手できます。
本書を購入することで、Luaプロジェクトの支援にもなります。
![]() |
LuaプログラミングLua | ![]() |
第I部 言語 第8章 コンパイル、実行、およびエラー |
Luaをインタプリタ言語と呼んでいますが、Luaは常にソースコードを中間形式に事前にコンパイルしてから実行します。(これは大した問題ではありません。ほとんどのインタプリタ言語は同じことをします。)コンパイル段階の存在は、Luaのようなインタプリタ言語では場違いに聞こえるかもしれません。しかし、インタプリタ言語の特徴はコンパイルされないことではなく、コンパイラが言語ランタイムの一部であり、そのため、動的に生成されたコードを実行することが可能(そして容易)であることです。`dofile`のような関数の存在こそが、Luaをインタプリタ言語と呼ぶことを可能にしていると言えるでしょう。
これまでは、Luaコードのチャンクを実行する一種のプリミティブな操作として`dofile`を紹介しました。`dofile`関数は実際には補助関数であり、`loadfile`が実際の処理を行います。`dofile`と同様に、`loadfile`もファイルからLuaチャンクを読み込みますが、チャンクを実行しません。代わりに、チャンクをコンパイルして、コンパイルされたチャンクを関数として返します。さらに、`dofile`とは異なり、`loadfile`はエラーを発生させず、代わりにエラーコードを返すため、エラーを処理できます。`dofile`は次のように定義できます。
function dofile (filename) local f = assert(loadfile(filename)) return f() end`loadfile`が失敗した場合にエラーを発生させるために`assert`を使用していることに注意してください。
簡単なタスクの場合、`dofile`は1回の呼び出しですべての処理を行うため便利です。しかし、`loadfile`の方が柔軟性があります。エラーが発生した場合、`loadfile`は**nil**とエラーメッセージを返すため、エラーを独自のやり方で処理できます。さらに、ファイルを複数回実行する必要がある場合、`loadfile`を1回呼び出して、その結果を複数回呼び出すことができます。プログラムはファイルを1回だけコンパイルするため、これは`dofile`を複数回呼び出すよりもはるかに安価です。
`loadstring`関数は`loadfile`に似ていますが、ファイルではなく文字列からチャンクを読み込む点が異なります。例えば、以下のコードの後
f = loadstring("i = i + 1")`f`は、呼び出されると`i = i + 1`を実行する関数になります。
i = 0 f(); print(i) --> 1 f(); print(i) --> 2`loadstring`関数は強力ですが、注意して使用する必要があります。また、(代替案と比較して)高価な関数であり、理解できないコードにつながる可能性があります。使用する前に、より簡単な解決策がないことを確認してください。
Luaは、独立したチャンクを匿名関数の本体として扱います。例えば、チャンク`"a = 1"`の場合、`loadstring`は次のものと同等のものを返します。
function () a = 1 end他の関数と同様に、チャンクはローカル変数と戻り値を宣言できます。
f = loadstring("local a = 10; return a + 20") print(f()) --> 30
`loadstring`と`loadfile`のどちらもエラーを発生させません。いかなる種類のエラーの場合も、両方の関数は**nil**とエラーメッセージを返します。
print(loadstring("i i")) --> nil [string "i i"]:1: `=' expected near `i'さらに、両方の関数はどのような副作用もありません。チャンクを内部表現にコンパイルして、匿名関数として結果を返すだけです。よくある間違いは、`loadfile`(または`loadstring`)が関数を定義すると仮定することです。Luaでは、関数の定義は代入であり、そのため、コンパイル時ではなく実行時に実行されます。例えば、次のような`foo.lua`というファイルがあるとします。
-- file `foo.lua' function foo (x) print(x) end次に、次のコマンドを実行します。
f = loadfile("foo.lua")このコマンドの後、`foo`はコンパイルされますが、まだ定義されていません。定義するには、チャンクを実行する必要があります。
f() -- defines `foo' foo("ok") --> ok
簡単な`dostring`(つまり、チャンクを読み込んで実行する)を実行する場合は、`loadstring`の結果を直接呼び出すことができます。
loadstring(s)()ただし、構文エラーがあると、`loadstring`は**nil**を返し、最終的なエラーメッセージは`"attempt to call a nil value"`になります。より明確なエラーメッセージを得るには、`assert`を使用してください。
assert(loadstring(s))()
通常、リテラル文字列に対して`loadstring`を使用することは意味がありません。例えば、以下のコード
f = loadstring("i = i + 1")は、おおよそ次と同等です。
f = function () i = i + 1 endしかし、2番目のコードの方がはるかに高速です。チャンクがコンパイルされるときに1回だけコンパイルされるためです。最初のコードでは、`loadstring`を呼び出すたびに新しいコンパイルが行われます。ただし、`loadstring`は字句スコープを使用してコンパイルしないため、2つのコードは完全に同等ではありません。違いを見るために、前の例を少し変更してみましょう。
local i = 0 f = loadstring("i = i + 1") g = function () i = i + 1 end`g`関数は期待通りにローカル変数`i`を操作しますが、`f`関数はグローバル変数`i`を操作します。これは、`loadstring`が常にグローバル環境で文字列をコンパイルするためです。
`loadstring`の最も典型的な用途は、外部コード、つまりプログラムの外部から取得されるコードを実行することです。例えば、ユーザーが定義した関数をプロットしたい場合、ユーザーは関数コードを入力し、`loadstring`を使用してそれを評価します。`loadstring`はチャンク、つまりステートメントを期待することに注意してください。式を評価する場合は、与えられた式の値を返すステートメントになるように、前に**return**を付ける必要があります。例を見てください。
print "enter your expression:" local l = io.read() local func = assert(loadstring("return " .. l)) print("the value of your expression is " .. func())
`loadstring`によって返される関数は通常の関数なので、複数回呼び出すことができます。
print "enter function to be plotted (with variable `x'):" local l = io.read() local f = assert(loadstring("return " .. l)) for i=1,20 do x = i -- global `x' (to be visible from the chunk) print(string.rep("*", f())) end
外部コードを実行する必要がある製品レベルのプログラムでは、`loadstring`によって報告されるエラーを処理する必要があります。さらに、コードを信頼できない場合は、コードを実行する際に不快な副作用を回避するために、保護された環境で新しいチャンクを実行することを検討してください。
Copyright © 2003–2004 Roberto Ierusalimschy. All rights reserved. | ![]() |