Luaにおけるグローバル環境、パッケージ、モジュール組織構造の解析

3397 ワード

モジュールはライブラリであり、パッケージは一連のモジュールです.Luaでは、requireによってモジュールをロードし、tableを表すグローバル変数を得ることができます.Luaは、そのすべてのグローバル変数を「環境」と呼ばれる通常のtableに保存します.本稿では,まず環境のいくつかの実用技術を紹介し,次にモジュールを参照し,モジュールを記述するための基本的な方法を紹介する.
1.環境Lua環境tableをグローバル変数_に保存Gでは、アクセスと設定が可能です.グローバル変数を操作したい場合がありますが、その名前は別の変数に格納されているか、value=G[varname]を使用して、動的な名前のグローバル変数を取得します.
環境に関する大きな問題は、グローバルであり、プログラムのすべての部分に影響を与えることです.Lua 5は、各関数がサブセットを持つ環境でグローバル変数を検索することを可能にし、setfenvによって1つの関数の環境を変更することができ、1番目のパラメータが1であれば現在の関数、2であれば現在の関数を呼び出す関数(順次類推)、2番目のパラメータは新しい環境tableである.

a = 1
setfenv(1, {})
print(a) --    ,print   nil。          ,             table

上記の問題を回避するためにsetfenv(1,{_G=_G})を使用して元の環境を保存し、G.printが参照します.もう1つの新しい環境を組み立てる方法は、継承を使用します.次のコードの新しい環境は、ソース環境からprintとaを継承し、任意の付与値は新しいtableで発生します.

a = 1
local newgt = {}
setmetatable(newgt, {__index = _G})
setfenv(1, newgt)
print(a)


2.モジュールとパッケージ2.1呼び出しモジュール
モジュールmodのfooメソッドを呼び出すには、次のようなrequire関数を使用してロードします.

require "mod"
mod.foo()
--   
local m = require "mod"
m.foo()

require関数の動作:(requireが使用するパス検索ポリシーについては後述)package.loadedというtableでモジュールがロードされているかどうかをチェックすると、対応する値(1つのモジュールが1回しかロードされないことがわかります)=>ロードされていないことを返し、packageで試してみます.preloadで入力されたモジュール名を問い合せる==>>関数を見つけ、その関数をモジュールとするローダ====>>が見つからない場合は、LuaファイルまたはCライブラリからモジュールをロードしよう======>>Luaファイルを見つけ、loadfileでファイルをロードする======>>Cライブラリを見つけ、loadlibでファイルをロードします
2.2使用環境
次のコードでは、環境を使用して複数のモジュールを作成する方法を説明します.

--     
local modname = "complex"
local M = {}
_G[modname] = M
package.loaded[modname] = M

--               
local _G = _G --         ,      _G.print   
local io = io

--            
setfenv(1, M)

function new(r, i) return {r=r, i=i} end

function add(c1, c2)
  return new(c1.r + c2.r, c1.i + c2.i)
end


このように関数addを宣言するとcomplexとなる.addは、同じモジュールを呼び出す他の関数にも接頭辞を付ける必要はありません.
2.3 module関数
Lua 5.1は、環境を定義する一連の機能を網羅する新しい関数moduleを提供します.モジュールの作成を開始すると、前の設定コードの代わりにmodule(「modname」,package.seeall)を直接使用できます.1つのモジュールファイルの先頭にこの呼び出しがある場合、後続のすべてのコードはモジュール名と外部名を限定する必要はなく、モジュールtableに戻る必要もありません.
2.4サブモジュールとパッケージ
Luaは、名前の階層を1つのポイントで区切る階層を持つモジュール名をサポートします.例えばモジュール名mod.subはmodのサブモジュールです.パッケージ(package)は、Luaの髪型の単位である完全なモジュールツリーです.サブモジュールファイルを検索すると、requireはポイント番号をディレクトリ区切り記号として検索します.つまり、require「a.b」を呼び出すと、./a/b.lua,/usr/local/lua/a/b.lua,/usr/local/lua/a/b/init.lua.このロードポリシーにより、パッケージのすべてのモジュールを1つのディレクトリに整理できます.
2.5カスタムでluaモジュールをロードLua 5.1以降、Luaには標準的なモジュール管理ライブラリがあります.したがって、すべてのモジュールのロードはrequireによって完了します.requireの設計は拡張性があり、いくつかの定義されたloaderから新しいモジュールを1つずつロードしようとします.システムライブラリには、ロード済みモジュール、Luaモジュール、およびC拡張モジュール(C拡張モジュールのロードを実現するために2つのloaderを使用する)をそれぞれ実現する4つのloaderが用意されています.これらのloaderはCFunctionの形式でrequireの環境の中の1つのtableに置かれている.
luaモジュールのロード形式を変更するには、新しいloaderを交換または追加するだけでいいです.
やるべきことはloadlibを真似するだけだ.cのloader_Lua関数は、たとえば私たちのプロジェクトでは、カスタムフォーマットパケットから暗号化されたLuaコードファイルをロードすることができます.その後、Cコードを数行書き、requireの環境(lua_getfenvを使用)を取得し、「loaders」というtableを取り出し、新しいカスタムloaderをindex 2の場所に挿入します.
具体的なコードは詳しく説明しません.よく読んでください.requireの実装(loadlib.cで)は分かりやすい.私たちの仕事全体が分析から実現まで2時間を超えていないのは、Luaの良い設計のおかげですね.Dは、ネットワーク接続されたデータストリームからLuaモジュールをロードしたい場合や、http/ftpプロトコルでダウンロードしたい場合でも、通用するでしょう.