Luaの詳細な機能(閉鎖、非グローバル関数、末尾呼び出し)

その他の機能について

Luaの関数値は、(ファーストクラス値)のスコープ単語(語彙スコープ)と最初のクラスです。Luaの関数と他の値(数値、文字列)で、関数、変数に格納されてもよい、またはパラメータの関数として、テーブルに記憶することができる、また、戻り値として機能することができる:最初の値を指し

言葉は、法曹を参照:ネストされた関数は、外側の彼の関数の変数にアクセスすることができます。この機能は、Luaは、強力なプログラミング機能を提供しています。

Luaの、機能を理解するために少しハードの機能できなく名前、匿名。我々は(例えば、印刷など)の関数名を参照すると、変数の他のタイプが同じ値を保持するように、変数が、実際には関数へのポインタであることを特徴とします。

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

これは実際のLuaの使用は、「シンタックスシュガー」(シンタックスシュガー)の結果を提供され、次のように本来の機能であります:

foo = function (x) return 2*x end

関数定義は、関数が変数の変数型に割り当てられている、実際には代入文です。我々が作成し、テーブルとしての使用{}機能を定義する機能(X)...エンドを使用します。

ソートテーブル標準ライブラリ関数、入力パラメータとしてテーブルのソートテーブル要素を受け入れ、。この関数は、昇順または降順でソートされ、異なる種類の値(文字列または数値)ことができなければなりません。Luaはこれらのケースのニーズを満たすために多くのパラメータとして提供していないが、ソート機能は、(C ++関数オブジェクトと同様に)パラメータとして受け付け、ソートソート機能は、入力パラメータとして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"}, 
} 

私たちは、ドメイン名のリストをソートしたい場合は:

table.sort(network, function (a,b) 
return (a.name > b.name) 
end) 

他のパラメータの関数としての機能は、機能を進め、そしてLuaのより高いレベルの機能特権が、第一種のLUA処理結果の単純な関数の関数としてではないのLUAで言及されています。
ここでは、描画機能の例です。

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) 

画面上の正弦波出力。
テーブル内のファーストクラスの値は、このセクションでは、後のセクションで説明し、アプリケーションのファンクションキーLuaの実装オブジェクト指向のパケット機構です。

クロージャ

関数が別の関数定義の中にネストされている場合、関数本体は、外部の関数内のローカル変数にアクセスすることができ、我々は、このような特徴語スコープを呼び出します。これは明確に思えますが、プログラミング言語の場合、法律専門家を加えた単語ファーストクラスの機能は、言語はめったにそのようなサポートを提供しない、強力な概念されていません。

簡単な例で見てみましょうが、学生の名前と学生の名前とスコアに対応するテーブルのリストがあるとします。

今の学生の考えが低い生徒の成績に高いから選別、あなたがこれを行うことができます:

names = {"Peter", "Paul", "Mary"} 
grades = {Mary = 10, Paul = 7, Peter = 8} 
table.sort(names, function (n1, n2) 
return grades[n1] > grades[n2] -- compare the grades 
end) 
假定创建一个函数实现此功能:
function sortbygrade (names, grades) 
 table.sort(names, function (n1, n2) 
 return grades[n1] > grades[n2] -- compare the grades 
end) 
end 

例としては、グローバル変数はローカル変数、ローカル変数我々は呼んで外部(外部ローカル変数)または上位値ではありません内部のパラメータは匿名関数に比べて、成績を成績をsortbygradeアクセスすることができ、ソート機能sortbygrade無名関数の内部に含まれています。(上位値の意味は少々誤解を招くかもしれません、しかし、Luaの中に彼の存在は歴史にそのルーツを持っており、彼のブリーフは、外部ローカル変数と比較)。
次のコードを見てください:

function newCounter() 
local i = 0 
return function() -- anonymous function 
 i = i + 1 
 return i 
end 
end 
c1 = newCounter() 
print(c1()) --> 1 
print(c1()) --> 2 

私たちは、私が範囲を超えて匿名関数を呼び出したとき、私は戻ってきたnewCounter機能を作成しましたので、私上位値匿名関数の使用は、彼のカウント数を保存します。しかし、アイデアをLuaのクロージャはこの状況を正しく処理します。簡単に言えば、クロージャは、それが正常にアクセスできる機能を加えた上位値です。我々は再びnewCounterを呼び出した場合、我々は新しいクロージャ変数iに新しい役割を得たので、我々は、新しいローカル変数iを作成します。

c2 = newCounter() 
print(c2()) --> 1 
print(c1()) --> 3 
print(c2()) --> 2 

C1、C2は、同じ機能に基づいていますが、同じローカル変数二つの異なるクロージャの異なるインスタンスで動作しています。

厳密に言えば、クロージャは、関数がちょうどプロトタイプ宣言の閉鎖で、値が関数を参照していないこと、それにもかかわらず、ケースは混乱につながらない中で、我々はその閉鎖に代わって長期的な機能を継続して使用します。

そのような先進機能パラメータ(ソート)の正面に見られるような便利な機能を提供するという文脈におけるクロージャ;およびネストされた関数の関数として(newCounter)。このメカニズムは、私たちの世界でのLuaの関数でプログラミングの素晴らしい組み合わせを可能にします。クロージャはまた、あなたがボタンのシリーズを作成する必要がGUI環境のように、コールバック関数に使用することができますが、コールバック関数は、ユーザーがボタンを押したときにボタンが少し異なるが押された場合、タスクは異なる処理を必要とするかもしれないと呼ばれています。

具体的には、小数の計算は、それぞれの桁に対応する10と同様のボタンが必要、あなたは以下の機能を使用して作成することができます。

function digitButton (digit) 
return Button{ label = digit, 
 action = function () 
 add_to_display(digit) 
 end 
 } 
end 

この例では、我々はそのボタンが新しいボタンを作成するために使用されるツールであると仮定し、ラベルがあるボタンのラベル、ボタンが押されたときにアクション・コールバック関数が呼び出されます。(実際に閉鎖、彼が上位値の桁を訪問したため)。

タスクdigitButtonの復帰が完了した後、範囲外のローカル変数の桁は、コールバック関数が呼び出されると、あなたはまだローカル変数の桁にアクセスすることができます。

閉鎖全く異なる文脈の使用も有用です。関数は、通常の変数に格納されているため、我々は簡単に再定義または関数を事前に定義することができます。あなたが必要なとき通常、元の関数は、関数を再定義することができ、新たな実装を持っています。たとえば、引数としてラジアンではなく度を受け入れるために罪を再定義することができます。

oldSin = math.sin 
math.sin = function (x) 
return oldSin(x*math.pi/180) 
end 
更清楚的方式:

do 
local oldSin = math.sin 
local k = math.pi/180 
 math.sin = function (x) 
 return oldSin(x*k) 
end 
end 

だから我々は、ローカル変数に配置されたオリジナル版を入れて、アクセス罪への唯一の方法は、機能の新バージョンです。

私たちはしばらくの間実行します(たとえば、当社のネットワーク・サーバ・コード上で実行されているへのアクセスなど)のコードは、セキュリティ環境を信用していないときに我々は、(同じサンドボックスでもサンドボックスとして知られており、Javaの)安全な環境を作成することができるのと同じ特性を使用して例えば、我々が開いているプログラムを制限するために、ライブラリを開くioの閉鎖再定義ファイルを使用することができ、必要とされています。

do 
local oldOpen = io.open 
 io.open = function (filename, mode) 
 if access_OK(filename, mode) then
 return oldOpen(filename, mode) 
 else 
 return nil, "access denied"
 end 
end 
end 

非グローバル関数

(Luaの標準ライブラリのほとんどは、このようなio.read、math.sinを達成するために、このメカニズムを使用しています)ドメインのテーブルとして機能:グローバル変数の関数としてのLuaはローカル変数として使用することができ、我々はいくつかの例を見てきました。この場合、我々は機能とテーブルの構文に注意を払う必要があります。

1. 表和函数放在一起
Lib = {} 
Lib.foo = function (x,y) return x + y end
Lib.goo = function (x,y) return x - y end
2. 使用表构造函数
Lib = { 
 foo = function (x,y) return x + y end, 
 goo = function (x,y) return x - y end
} 
3. Lua 提供另一种语法方式
Lib = {} 
function Lib.foo (x,y) 
return x + y 
end 
function Lib.goo (x,y) 
return x - y 
end 

我々はローカル変数に格納されて機能したとき、私たちは地元の機能の一定範囲内のローカル変数と同様に有効であるローカル関数を取得します。パッケージで定義されている。これは非常に便利です:Luaの関数処理としてチャンクは、チャンクローカル関数(チャンクでのみ表示)で宣言することができるので、単語法曹界は、パッケージ内の他の機能は、この関数を呼び出すことができることを確認します。ここでは地元の関数を宣言するための2つの方法があります。

1. 方式一
local f = function (...) 
 ... 
end 
local g = function (...) 
 ... 
 f() -- external local `f' is visible here 
 ... 
end 
2. 方式二
local function f (...) 
 ... 
end 

もう一つ注意すべきは、方法の声明でローカル再帰関数ということです。

local fact = function (n) 
if n == 0 then 
 return 1 
else 
 return n*fact(n-1) -- buggy 
end 
end 
上面这种方式导致 Lua 编译时遇到 fact(n-1)并不知道他是局部函数 fact,Lua 会去查找是否有这样的全局函数 fact。为了解决这个问题我们必须在定义函数以前先声明:
local fact 
fact = function (n) 
if n == 0 then 
 return 1 
else 
 return n*fact(n-1) 
end 
end 

実際には、この内部事実(n-1)は、関数呼び出しにローカル呼び出しは、実際には、動作時間の正確な値を得ることができます。
しかし
、彼はLuaは再帰関数を直接定義することができたときに構文は次の2つの方法の使用を可能にする拡張されます。

間接的な再帰関数を定義するときに最初に、ローカル定義をすることができます宣言する必要があります。

local f, g -- `forward' declarations 
function g () 
 ... f() ... 
end 
function f () 
 ... g() ... 
end 

適切な末尾再帰(適切な末尾再帰)

(再帰の概念に関連していないが、適切な末尾再帰、一部の図書用語「末尾再帰」)もう一つの興味深い特徴は、Luaの機能を正しくテール呼び出し元のプロセスです。

最後の呼び出しは、最後のアクションは、関数が、我々はこの呼び出しの終了が行われたと言ったときに別の関数を呼び出すことで同様の後藤関数呼び出しの終わりです。例えば:

function f(x) 
return g(x) 
end 

Gは、末尾再帰を呼び出しています。

G Fコール・ケースの後に何もしないで、この場合は、ときに呼び出された関数G呼び出し側fに戻す必要はありません。だから、スタック内の発信者に関する情報を保持するために、発信者のニーズの終了後。そのようなテール呼び出しを扱うときに、追加のスタックせずにこの機能を使用するには、Luaのインタプリタなどの一部のコンパイラは、我々は正しい言語サポートの末尾呼び出し、これを呼び出します。

末尾呼び出しは、スタック領域の使用を必要としないので、その後、末尾呼び出しの再帰レベルは無制限することができます。例えば、どんなにnの値は、スタックオーバーフローを起こさないものを次の呼び出し。

function foo (n) 
if n > 0 then return foo(n - 1) end
end 

あなたは末尾呼び出しが何であるかを明確にする必要がありますので注意してください。
関数呼び出しの後にいくつかの発信者は、他の機能は、他のことをしないではなく、末尾呼び出しました。例えば:

function f (x) 
 g(x) 
return 
end 

それは尾呼び出し、同じではないので、上記の例では、呼G F、Gの戻り値は、破棄されるべき
次の例も末尾呼び出し:

return g(x) + 1 -- must do the addition 
return x or g(x) -- must adjust to 1 result 
return (g(x)) -- must adjust to 1 result 

同様の呼び出しLuaのリターングラム(...)は、この形式末尾呼び出しです。表現のLuaの値は、呼び出しの前に計算されますので、しかし、GとGのパラメータは、複雑な式にすることができます。たとえば、次の呼び出しは、末尾再帰であります:

return x[i].foo(x[j] + a*b, i + j) 

末尾呼び出しのgotoとして理解することができる、呼状態機械プログラムテール分野で有用です。ステートマシンのアプリケーションは、それぞれの状態を記憶する機能を必要とし、状態は、ジャンプ(または呼び出し)特定の機能を変化させます。私たちは、一例として、迷路ゲームを考えてみましょう。多くの迷路の部屋があり、各部屋には4つのドア、東と西、移動入力の方向の各ステップを持っており、それは、対応する部屋の到来の中に方向方向であれば、それ以外のプログラムは警告メッセージを出力します。

目標は次のとおりです。始めた部屋から部屋の目的を達するために。
迷路ゲームは、典型的なステート・マシンで、各部屋には、現在の状態です。私たちは別の部屋に1つの部屋から移動するために末尾再帰を使用し、各部屋のために、この迷路ゲームを達成するための関数を書くことができます。次のように4つの部屋の迷路コードは次のとおりです。

function room1 () 
local move = io.read() 
if move == "south" then
 return room3() 
elseif move == "east" then
 return room2() 
Programming in Lua 38
Copyright ® 2005, Translation Team, www.luachina.net 
else 
 print("invalid move") 
 return room1() -- stay in the same room 
end 
end 
function room2 () 
local move = io.read() 
if move == "south" then
 return room4() 
elseif move == "west" then
 return room1() 
else 
 print("invalid move") 
 return room2() 
end 
end 
function room3 () 
local move = io.read() 
if move == "north" then
 return room1() 
elseif move == "east" then
 return room4() 
else 
 print("invalid move") 
 return room3() 
end 
end 
function room4 () 
 print("congratilations!") 
end 

私たちは、ゲームを開始するROOM1()を呼び出すことができます。
適切な末尾呼び出しがなければ、すべての動きは、スタックオーバーフローを引き起こすことができるいくつかの回を移動した後、スタックを作成する必要があります。
別の関数のgotoに毎回のみ末尾呼び出しは、伝統的な関数呼び出しではありませんので、しかし、右テールのテールは、無制限に通話を呼び出します。

公開された252元の記事 ウォンの賞賛151 ・は 10000 +を見て

おすすめ

転載: blog.csdn.net/qq_39885372/article/details/104322343