15 Modüller ve Paketler

15 Modüller ve Paketler


Lua genelde politikalar belirlemez. Bunun yerine geliştirici gruplarının kendileri için en uygun politikaları uygulayabilmesini sağlayacak kadar güçlü mekanizmalar sunar. Bununla birlikte bu yaklaşım modüller için iyi işlemez. Modül sisteminin ana hedeflerinden biri farklı gruplarının kod paylaşımına imkan vermektir. Ortak bir politikanın olmaması bu paylaşıma sekte vurmaktadır.

Sürüm 5.1'den başlayarak Lua modüller ve paketler (bir paket modüllerin koleksiyonudur) için ilkeler kümesi tanımlamıştır . Bu ilkeler dilden ekstra bir şey istemez; programcılar şimdiye kadar gördüklerimizi kullanarak bunları uygulayabilirler: tablolar, fonksiyonlar, metatablolar ve ortamlar. Bununla birlikte iki önemli fonksiyon bu ilkelerin benimsenmesini kolaylaştırır: modülleri kullanmak için require, modülleri oluşturmak için module. Programcılar bu fonksiyonları farklı ilkelerle yeniden uyarlamada serbesttir. Elbette alternatif uygulamalar programların yabancı modülleri kullanamayacağına, modüllerin yabancı programlarca kullanılamayacağına götürebilir.

Kullanıcının bakış açısından bir modül require ile yüklenebilen, içerdiği tabloyu tek bir global isimle tanımlayan bir kütüphanedir. Modülün fonksiyonları ve sabitleri gibi dışa aktardığı her şey bir ad alanı olarak çalışan bir tablonun içinde tanımlanır. iyi huylu bir modül aynı zamanda require için bu tabloyu döndürmek üzere  düzenlenmiş olanıdır.

Modül uygulamasında tabloları kullanmanın bariz bir yararı, modülleri başka herhangi bir tablo gibi manipüle edebilmemiz ve ekstra imkanlar oluşturmak için Lua'nın tüm gücünü kullanabilmemizdir. Çoğu dilde modüller birinci sınıf değer değildir (diğer bir deyişle, fonksiyonlara argüman olarak geçilemez, değişkenlerde saklanamazlar.), bu nedenle bu dillerin modüller için sunmak istedikleri her ekstra imkan için özel mekanizmalara ihtiyacı vardır. Lua'da özgürce ekstra imkanlara ulaşabilirsiniz.

Örneğin bir kullanıcının bir modülden bir fonksiyonu çağırmasının farklı yolları vardır. En basit olanı şu:

require "mod"
mod.foo()

Modül için daha kısa bir isim tercih ederseniz bunun için lokal bir ad ayarlayabilirsiniz:

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

Aynı zamanda fonksiyonlarını da yeniden adlandırabilirsiniz:

require "mod"
local f = mod.foo
f()

Bu imkanlarla ilgili hoş olan, dilden ekstra bir destek istememesidir. Dilin zaten sunduğu şeyler kullanılır.

15.1 require Fonksiyonu

Lua,  modülleri yüklemek için require adlı üst düzey bir fonksiyon sunar. Bu fonksiyon, modül ile ilgili üslenimleri minimum tutmaya çalışır. require için bir modül, sadece bazı değerleri (fonksiyonlar veya fonksiyonları içeren tablolar gibi) tanımlayan herhangi bir kod öbeğidir.

Bir modülü yüklemek için sadece require "modulismi" diyoruz. Genellikle bu çağrı, modülün fonksiyonlarından oluşan bir tablo döndürür ve bu tabloyu içeren global bir değişkeni de aynı zamanda tanımlar. Bununla birlikte bu eylemler modül tarafından yapılır require tarafından değil, bu nedenle bazı modüller diğer başka değerleri döndürmeyi veya farklı yan etkilere sahip olmayı seçebilir.

Hazırda yüklü olacağını bilseniz bile ihtiyacınız modülleri require etmek her zaman iyi bir programlama uygulamasıdır. Standart kütüphaneleri bu kuraldan hariç tutabilirsiniz çünkü bunlar Lua'da ön-yüklemeye tabi. Bununla birlikte bazı insanlar onlar için bile require kullanmayı tercih ederler:

local m = require "io"
m.write("merhaba dunya\n")

Liste 15.1 require 'nin davranışını detaylandırmakta. İlk adım modülün halihazırda yüklü olup olmadığını package.loaded tablosunda kontrol etmektir. Eğer öyleyse require karşılık gelen değerini döndürür. Bu şekilde, bir modül bir kez yüklendi mi modülü tekrar yüklemeden require'a diğer çağrılar aynı değeri döndürür.

Modül halihazırda yüklenmediyse, require bu modül için bir yükleyici bulmaya çalışır. (Bu adım, 15.1 listesinde findloader soyut fonksiyonu ile gösterilmekte.) İlk girişimi package.preload tablosunda verilen kütüphane adını sorgulamaktır. Orada bir fonksiyon bulursa, bu fonksiyonu modül yükleyici olarak kullanır. Bu preload tablosu bazı geleneksel olmayan durumları işlemek için kapsamlı bir metot sağlar ( Lua 'a statik linkli C kütüphaneleri gibi). Genellikle, bu tablonun modül için bir girişi yoktur, bu nedenle require modülü yüklemek için önce bir Lua dosyası için ve daha sonra  bir C kütüphanesi için arama yapar.

Liste 15.1. require fonksiyonu:

function require(name)
    if not package.loaded[name] then -- modul hazırda yüklü değil?
        local loader = findloader(name)
        if loader == nil then
            error("unable to load module " .. name)
        end
        package.loaded[name] = true -- modulü yüklü olarak işaretle
        local res = loader(name) -- modulü ilkle
        if res ~= nil then
            package.loaded[name] = res
        end
    end
    return package.loaded[name]
end

require verilen modül için bir Lua dosyası bulursa onu loadfile ile yükler; aksi takdirde bir C kütüphanesi bulursa loadlib ile onu yükler. Hem loadfile hem de loadlib'in çalıştırmadan kodları sadece yüklediğini unutmayın. Kodu çalıştırmak için require tek argümanla, modül adı ile onu çağırır. Eğer yükleyici herhangi bir değer döndürürse, require, bu değeri döndürür ve bu aynı kütüphane için gelecek çağrılarda bu aynı değeri döndürmek için package.loaded tablosunda saklar. Yükleyici hiçbir değer döndürmezse, require, ne olursa olsun package.loaded tablosundaki değeri döndürür. Bu bölümde daha sonra göreceğimiz üzere, bir modül require tarafından döndürülen değeri doğrudan package.loaded'a koyabilir.

Önceki kodda önemli bir ayrıntı, yükleyici çağrılmadan önce require, package.loaded'de ilgili alana true atayarak halihazırda modül yüklü olarak işaretlemesidir. Bu şekilde, modül başka bir modüle gereksiniyor ve bu da özgün modüle gereksiniyorsa  yineleme oluşur,  bu son çağrı sonsuz döngüden kaçındırarak require'i hemen döndürür.

Aynı kütüphaneyi iki kez yüklemeye zorlamak için sadece package.loaded'den kütüphane girişini siliyoruz. Örneğin, başarılı bir require "foo" sonrası package.loaded["foo"] nil olmayacaktır. Aşağıdaki kod kütüphaneyi tekrar yükleyecektir:

package.loaded["foo"] = nil
require "foo"

Bir dosya ararken require, tipik path'lerden biraz farklı bir path kullanır. Çoğu program tarafından kullanılan path, belirli bir dosyayı aramak için dizin (directory) listesidir. Bununla birlikte, ANSI C (Lua'nın çalıştığı soyut platform) dizin konseptine sahip değildir. Bu nedenle, require tarafından kullanılan path bir şablonlar listesidir, bu şablonlardan her biri modül adını (require argümanı) bir dosya adına dönüştürmenin alternatif yolunu belirtir . Daha spesifik olarak, path'deki her bileşen isteğe bağlı soru işaretleri içeren bir dosya ismidir. Her bileşen için require tüm '?' 'leri modül adıyla değiştirir ve ortaya çıkan ada sahip bir dosya olup olmadığını kontrol eder; yoksa, bir sonraki bileşene gider. Bir path'deki bileşenler noktalı virgüllerle ayrılır (çoğu işletim sisteminde dosya adları için nadiren kullanılan bir karakter). Örneğin path şu ise:

?;?.lua;c:\windows\?;/usr/local/lua/?/?.lua

require "sql" isteği aşağıdaki dosyaları açmaya çalışacaktır:

sql
sql.lua
c:\windows\sql
/usr/local/lua/sql/sql.lua

require fonksiyonu yalnızca  soru işareti ve noktalı virgüle (bileşen ayırıcı olarak) hükmedebilir;
dizin ayırıcıları veya dosya uzantıları gibi şeyler path'in kendisi tarafından tanımlanır.

require'nın Lua dosyalarını aramak için kullandığı path package.path  değişkenindedir daima. Lua başladığında, bu ortam değişkeni tanımlanmazsa, derlenmiş-tanımlanmış varsayılan path ile veya LUA_PATH  ortam değişkeni değeri ile  bu değişkeni ilkler. LUA_PATH kullanıldığında Lua tüm ";;" alt stringi  için varsayılan path yerleştirmesi yapacak. Örneğin, LUA_PATH 'i “mydir/?.lua;;” olarak ayarlarsanız, nihai path varsayılan path'i izleyen “mydir/?.lua” şeklinde olacak.

require modül adıyla uyumlu bir Lua dosyası bulamazsa,  C kütüphanesi arar. Bu arama için,  package.cpath değişkeninden ( package.path yerine) path'i alır. Bu değişken ortam değişkeni LUA_CPATH 'den (LUA_PATH yerine) ilk değerini alır . Unix'te bu değişken için tipik bir değer şu şekildedir:

./?.so;/usr/local/lib/lua/5.1/?.so

Dosya uzantısının path ile tanımlandığını unutmayın (örneğin önceki örnek tüm şablonlar için .so kullanır). Windows'da tipik bir path daha çok şu şekilde:

.\?.dll;C:\Program Files\Lua501\dll\?.dll

C kütüphanesi bulunduktan sonra bölüm 8.2'de ele aldığımız package.loadlib ile yüklenir. Lua chunklarının aksine, C kütüphaneleri tek bir ana fonksiyon tanımlamaz. Bunun yerine, birkaç C fonksiyonunu dışa aktarabilirler. İyi-huylu C kütüphaneleri, require'nın kütüphane linklemesi sonrası çağırmaya giriştiği bir fonksiyon olan luaopen_modname isimli bir fonksiyon ihraç eder. Bölüm 26.2'de C kütüphanelerinin nasıl yazılacağını ele alacağız.

Genellikle orijinal isimleriyle modülleri kullanırız ancak bazen isim çakışmalarını önlemek için bir modülü yeniden adlandırmalıyız. Tipik bir durum, örneğin test için aynı modülün farklı sürümlerini yüklememiz gerektiği durumdur. Bir Lua modülünün içsel olarak ismini sabitlemekten(daha sonra göreceğimiz üzere)  ziyade, adını kolayca değiştirmek üzere onu düzenleyebiliriz. Tabi luaopen_* fonksiyonunun adını düzenlemek üzere binary modülü editleyemeyiz. Bu tür yeniden adlandırmalara imkan vermek için require küçük bir hile kullanır: modül adı bir tire içeriyorsa, require, luaopen_* fonksiyon adını oluşturulurken baştan tireye kadar çıkarma yapar. Örneğin, bir modül a-b olarak adlandırılırsa, require, open fonksiyonunun luaopen_a-b (ki geçerli bir C adı olmazdı) yerine luaopen_b isimli olmasını bekler . Yani, mod isminde iki modül kullanmamız gerekiyorsa, bunlardan birini v1-mod'a (veya -mod'a, veya bunun gibi bir şeye) yeniden adlandırabiliriz. m1=require "v1-mod" dediğimizde, require hem yeniden adlandırılmış v1-mod dosyasını hem de bu dosya içindeki luaopen_mod orijinal adlı fonksiyonu bulacaktır.

15.2 Modüller yazmada temel yaklaşım

Lua'da bir modül oluşturmanın en temel yolu gerçekten basittir: bir tablo oluşturuyoruz, içine ihraç etmek istediğimiz tüm fonksiyonları koyuyoruz ve bu tabloyu return ettiriyoruz. Liste 15.2 bu yaklaşımı göstermektedir. inv 'i , chunk'a lokal olarak deklare ederek basitçe özel adla nasıl tanımladığımıza dikkat.

Modüller için tabloların kullanımı, gerçek modüllerle sağlananla tam olarak aynı işlevselliği sağlamaz. İlk olarak, modül adını her fonksiyon tanımlamasına açıkça koymalıyız. İkinci olarak, aynı modül içindeki başka bir fonksiyonu çağıran bir fonksiyon, çağrılan fonksiyonun adını nitelemeli. Bu sorunları modül için sabit lokal ad (M, örneğin) kullanarak  ve daha sonra bu lokali modülün nihai adına atayarak iyileştirebiliriz. Bu prensibi izleyerek önceki modülümüzü şöyle yazacağız:

local M = {}
complex = M -- modül ismi

M.i = {r = 0, i = 1}
function M.new(r, i) return {r = r, i = i} end

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

  <önceki gibi>

Bir fonksiyon aynı modül içindeki başka bir fonksiyonu çağırdığında (veya kendisini yinelemeli çağırdığında), yine adı öneklemesi gerekir. En azından, iki fonksiyon arasındaki bağlantı artık modül adına bağlı değil. Ayrıca, tüm modülde modül adını yazdığımız sadece bir yer var. Aslında, modül adını tamamen yazmaktan kaçınabiliriz, çünkü require modüle bir argüman olarak onu geçer:

local modname = ...
local M = {}
_G[modname] = M

M.i = {r=0, i=1}
<önceki gibi>



Liste 15.2. Basit bir modül:

complex = {}

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

-- 'i' sabitini tanımla
complex.i = complex.new(0, 1)

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

function complex.sub(c1, c2)
    return complex.new(c1.r - c2.r, c1.i - c2.i)
end

function complex.mul(c1, c2)
    return complex.new(c1.r * c2.r - c1.i * c2.i, c1.r * c2.i + c1.i * c2.r)
end

local function inv(c)
    local n = c.r ^ 2 + c.i ^ 2
    return complex.new(c.r / n, -c.i / n)
end

function complex.div(c1, c2)
    return complex.mul(c1, inv(c2))
end

return complex

Bu değişiklikle, bir modülü yeniden adlandırmak için yapmamız gereken tek şey onu tanımlayan dosyayı yeniden adlandırmaktır.

Başka bir küçük iyileştirme kapanış return deyimi ile ilgilidir. Modülün başında tüm modülle ilgili kurulum görevlerini konsantre olabilirsek güzel olurdu. return deyimine ihtiyacı ortadan kaldırmanın bir yolu, modül tablosunu direkt package.loaded'e atamakdır:

local modname = ...
local M = {}
_G[modname] = M
package.loaded[modname] = M
<önceki gibi>

Bu atama ile, modülün sonunda M 'i return etmeye gerek duymayız: unutmayın, bir modül bir değer döndürmezse, require , package.loaded[modulismi] 'nin mevcut değerini döndürür.

15.3 Ortamları Kullanma

Modülleri oluşturmak için bu temel yöntemin önemli bir dezavantajı, programcıdan özel dikkat istemesidir. Aynı modüldeki diğer public(herkeze açık) girişlere erişirken adları nitelemelidir. Bir fonksiyonun durumunu private'den(özelden) public'e(veya public'den private'e) değiştirdiğinde çağrıları değiştirmek zorundadır. Dahası, private deklarasyonda local 'i unutmak çok kolaydır.

fonksiyon ortamları, tüm bu sorunları çözen modüller oluşturmak için ilginç bir teknik sunar. Modül ana chunk'ı özel bir ortama sahip olduğunda, sadece tüm fonksiyonları bu tabloyu paylaşmakla kalmaz, aynı zamanda tüm global değişkenleri de bu tabloya gider. Bu nedenle, tüm public fonksiyonları global değişkenler olarak deklare edebiliriz ve otomatik olarak ayrı bir tabloya gideceklerdir. Modülün yapması gereken tek şey, bu tabloyu modül adına  ve package.loaded'e atamak. Sonraki kod parçası bu tekniği göstermektedir:

local modname = ...
local M = {}
_G[modname] = M
package.loaded[modname] = M
setfenv(1, M)

Şimdi, add fonksiyonunu bildirdiğimizde, complex.add'e gider

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

Ayrıca, herhangi bir önek olmadan aynı modülden diğer fonksiyonları çağırabiliriz. Örneğin, add ortamından new'i alır, yani complex.new'i alır.

Bu metot, programcı için biraz ekstra çalışma ile modüller için iyi bir destek sunar. Öneklere hiç ihtiyaç yok. ihraç edilen ile private fonksiyon arasında fark yok. Programcı local'i unutursa, genel isimalanını kirletmez; bunun yerine, private fonksiyon yalnızca herkese açık olur(public).

Eksik olan, tabii ki, diğer modüllere erişim. Ortamımızı boş M tablosu yaptıktan sonra önceki tüm global değişkenlere erişimi kaybediyoruz. Bu erişimi kurtarmanın çeşitli yolları vardır,  herbirinin artıları ve eksileri ile.

En basit çözüm daha önce gördüğümüz gibi  mirastır:

local modname = ...
local M = {}
_G[modname] = M
package.loaded[modname] = M
setmetatable(M, {__index = _G})
setfenv(1, M)

(setfenv çağrılmadan önce setmetatable'i çağırmalısınız; nedenini söyleyebilir misiniz?) Bu yapı ile modül, her erişim için küçük bir yük ödeyerek herhangi bir global tanımlayıcıya doğrudan erişime sahiptir. Bu çözümün komik bir sonucu, kavramsal olarak modülünüzün artık tüm global değişkenleri içermesidir. Örneğin, modülünüzü kullanan birileri, complex.math.sin(x) yazarak standart sine fonksiyonunu çağırabilir. (Perl'in paket sistemi de bu özelliğe sahiptir.).

Diğer modüllere erişmenin bir başka hızlı yöntemi, eski ortamı tutan bir local bildirimdir:

local modname = ...
local M = {}
_G[modname] = M
package.loaded[modname] = M
local _G = _G
setfenv(1, M)

Artık _G. ile her global değişkenin adını öneklemeniz gerekir ancak erişim biraz daha hızlıdır çünkü metametot dahil değildir.

Daha disiplinli bir yaklaşım, yalnızca ihtiyacınız olan fonksiyonları veya en çok ihtiyacınız olan modülleri lokal olarak beyan etmektir:

-- modül kurulumu
local modname = ...
local M = {}
_G[modname] = M
package.loaded[modname] = M

-- ihraç bölümü:
-- dışardan ihtiyaç duyulan bu modüldeki herşeyi deklare et
local sqrt = math.sqrt
local io = io

-- burdan sonra artık harici erişim yok
setfenv(1, M)

Bu teknik daha fazla çalışma gerektirir ancak modül bağımlılıklarınızı daha iyi belgelemektedir. Aynı zamanda önceki düzendeki kodlardan daha hızlı çalışan kodlarla sonuçlanır.

15.4 module Fonksiyonu

Muhtemelen önceki örneklerimizdeki kod tekrarlarını fark ettiniz. Hepsi şu aynı şablonla başladı:

local modname = ...
local M = {}
_G[modname] = M
package.loaded[modname] = M
<harici erişim için kurulum>
setfenv(1, M)

Lua 5.1, bu işlevselliği paketleyen module olarak adlandırılan yeni bir fonksiyon sağlar. Bu önceki kurulum kodu yerine, devamdaki gibi basitçe bir modülü başlatabiliriz:

module(...)

Bu çağrı yeni bir tablo oluşturur, uygun global değişkene ve loaded tablosuna atar ve ardından tabloyu ana chunk'ın ortamı olarak set eder.

Varsayılanda, module harici erişim sağlamaz: onu çağırmadan önce erişmek istediğiniz harici fonksiyonlar veya modüller ile uygun lokal değişkenleri bildirmelisiniz. Ayrıca module çağrısına package_seeall seçeneğini ekleyerek harici erişim için mirası kullanabilirsin . Bu seçenek aşağıdaki kodun eşdeğerini yapar:

setmetatable(M, {__index = _G})

Böylece, dosyanın başında basitçe

module(..., package.seeall)

deyimini ekleyerek modüle döndürür onu. normal Lua kodu gibi her şeyi yazabilirsiniz. Ne modül adlarını ne de harici isimleri nitelemeye ihtiyaç duyarsınız. Modül adını yazmanıza gerek yoktur (aslında modül adını bilmeniz bile gerekmez). Modül tablosunu döndürme konusunda endişelenmenize gerek yok. Tek yapmanız gereken o tek deyimi eklemektir. 

Module fonksiyonu bazı ekstra olanaklar sağlar. Çoğu modülün bu imkanlara ihtiyacı yoktur ancak bazı dağıtımlar bazı özel muamelelere ihtiyaç duyar (örneğin, hem C fonksiyonları hem de Lua fonksiyonları içeren bir modül oluşturmada). Modül tablosu oluşturulmadan önce module package.loaded'ın halihazırda bu modül için tablo içerip içermediğini veya verilen ada sahip bir değişkenin mevcut olup olmadığını  kontrol eder. Bu yerlerden birinde bir tablo bulursa module bu tabloyu modül için yeniden kullanır; bu, hazırda oluşturulmuş bir modülün yeniden açılması için module'i kullanabileceğimiz anlamına gelir. Modül henüz mevcut değilse o zaman module modül  tablosunu oluşturur. Bundan sonra tabloyu önceden tanımlanmış bazı değişkenlerle doldurur: _M, modül tablosunun kendisini içerir (_G 'nin eşdeğeri); _NAME modül adını içerir (module'e geçilen ilk argüman); ve _PACKAGE paket adını içerir (son bileşensiz ad; sonraki bölüme bakın).

15.5 Alt modüller ve paketler

Lua, isim seviyelerini ayırmak üzere nokta kullanarak modül adlarının hiyerarşik olmasına  imkan verir. Örneğin, mod.sub adlı bir modül mod'un bir alt modülüdür. Buna göre, mod.sub modülü mod.sub tablosu içinde tüm değerlerini tanımlayacağını varsayabilirsiniz yani mod tablosundaki sub anahtarı ile depolanan tablo içinde. Bir paket modüllerin tam bir ağacıdır; Lua'da dağıtım birimidir.

mod.sub adlı bir modülü require ettiğinizde require önce package.loaded tablosunu sorgular ve ardından package.preload tablosu anahtar olarak "mod.sub" isimli orijinal modülü kullanır; nokta'nın bu aramada hiçbir önemi yoktur.

Bununla birlikte, bu alt modülün tanımlandığı dosyayı ararken require noktayı başka bir karaktere, genellikle sistemin dizin ayırıcısına (örneğin, ‘/’ Unıx veya Windows için‘\’) çevirir. Çeviriden sonra require ortaya çıkan adı başka herhangi bir ad gibi arar. Örneğin, path 'i şöyle varsayarak

./?.lua;/usr/local/lua/?.lua;/usr/local/lua/?/init.lua

ve '/'dizin ayırıcı olarak, require "a.b" çağrısı aşağıdaki dosyaları açmaya çalışacaktır:

./a/b.lua
/usr/local/lua/a/b.lua
/usr/local/lua/a/b/init.lua

Bu davranış, a paketinin tüm modüllerinin tek bir dizinde yaşamasına olanak verir. Örneğin, a paketinin p, p.a ve p.b modülleri varsa,  p diziniyle aynı yerde ilgili dosyalar  p/init.lua, p/a.lua, ve p/b.lua olarak adlandırılabilir.

Lua tarafından kullanılan dizin ayırıcı, derleme zamanında yapılandırılır ve herhangi bir string olabilir (Lua dizinler hakkında hiçbir şey bilmiyor hatırlayın). Örneğin, hiyerarşik dizinsiz sistemler “dizin” ayırıcı olarak ‘_’ kullanabilir bu şekilde require "a.b" a_b.lua dosyasını arayacak.

C-fonksiyon adları noktalar içeremez bu nedenle a.b alt modülü için bir C kütüphanesi luaopen_a.b fonksiyonunu ihraç edemez. Burada require noktayı başka bir karaktere, alt çizgiye çevirir. Bu yüzden, a.b adlı bir C kütüphanesi,  ilkleme fonksiyonunu luaopen_a_b olarak adlandırmalıdır. Bazı ince sonuçlarla burada da tire hilesini kullanabiliriz. Örneğin, bir a C kütüphanemiz varsa ve bunu bir mod'un alt modülü yapmak istiyorsak dosyayı mod/-a olarak yeniden adlandırabiliriz. Ne zaman require "mod.-a" yazmamız gerekse require doğru şekilde hem yeni dosya mod/-a hemde onun içinde luaopen_a fonksiyonunu bulur .

Ek bir imkan olarak, require, C alt modülleri yüklemek için bir seçeneğe daha sahiptir. bir alt modül için bir C dosyası veya bir Lua dosyası bulanamadığında yine C path'i arar ancak bu sefer paket adına bakar. Örneğin, program bir alt modül a.b.c 'i require ediyor ve require a/b/c ararken bir dosya bulamıyorsa, bu son arama a'ı arayacak. Bu ada sahip bir C kütüphanesi bulursa o zaman require uygun bir open fonksiyon için , bu örnekte luaopen_a_b_c, bu kütüphane içine bakar. Bu imkan bir dağıtımın, her biri kendi open fonksiyonu olan çeşitli altmodülleri tek C kütüphanesinde bir araya getirilmesine izin verir.

module fonksiyonu da alt modüller için açık destek sunar. module("a.b.c") gibi bir çağrı ile bir alt modül oluşturduğumuzda module ortam tablosunu a.b.c değişkenine koyar, yani a tablosunun b alanındaki a tablosunun c alanına. Bu arada, tablolardan herhangi biri yoksa module bunları oluşturur.
Aksi takdirde onları yeniden kullanır. 

Lua bakış açısından, aynı paketteki alt modüllerin, ortam tablolarının iç içe geçiyor olması dışında açık bir ilişkisi yoktur. require edilen bir a modülü, alt modüllerinden herhangi birini otomatik olarak yüklemez; benzer şekilde, a.b için require , a 'ı otomatik olarak yüklemez. Tabii ki, paket uygulayıcısı istediği takdirde bu bağlantıları oluşturmada serbesttir. Örneğin, belirli bir a modülü açıkça bir veya tüm alt modüllerini require ile başlayabilir.

Hiç yorum yok:

Yorum Gönder