لوآ (زبان برنامهنویسی)
لوآ (به انگلیسی: Lua، /ˈluːə/ LOO-ə) یک زبان برنامهسازی سبُکوزن، بازتابنده و امری-تابعی است. زبان لوآ با ساختار پردازهنویسی (برنامهنویسی اسکریپتی) و با هدف داشتن قابلیتهای گسترشپذیر طراحی شدهاست. واژه «لوآ» در زبان پرتغالی به معنی «ماه» است.
پارادایم برنامهنویسی | چند الگویی:اسکریپتی، دستوری (رویهای، بر پایه پیشنمونه، شیءگرا)، تابعی |
---|---|
طراحی شده توسط | روبرتو یروسالیمچی ولادیمیر سِلس لوییز هنریک دی فیگوئردو |
ظهوریافته در | ۱۹۹۳ |
انتشار پایدار | 5.4.4
۲۶ ژانویه ۲۰۲۲ |
پویا، نیرومند، اردکی | |
زبان پیادهسازی | C (خانواده ANSI) |
سیستمعامل | چندسکویی |
پروانه | پروانه امآیتی |
.lua | |
وبگاه | |
پیادهسازیهای بزرگ | |
Lua, LuaJIT, LLVM-Lua, Lua Alchemy | |
گویش | |
Metalua, Idle, GSL Shell | |
متأثر از | |
C++، سیالیو، Modula, اسکیم، اسنوبال | |
تأثیر گذاشته بر | |
آیاو، GameMonkey ،Squirrel، فالکن، MiniD |
فلسفه اولیه ساخت این زبان، استفاده از آن به عنوان یک زبان عمومی و ساده پردازهنویسی بود. جامعه هدف لوآ شامل کاربران نیمه حرفهای هم میشود. در پردازهنویسی برای انجام توصیفات پیچیده از یک زبان نهفته شده در یک زبان دیگر استفاده میشود. به خاطر سبک بودن کتابخانه لوآ، میتوان لوآ را با تمام قابلیتها به عنوان یک زبان نهفته در سی استفاده نمود. حجم مفسر کامپایل شده حدود ۱۵۰KB است.
در طراحی لوآ سعی شدهاست از فرا ساز و کارها بیشتر استفاده شود تا در عین کوچکی، انعطافپذیری زبان افزایش یابد. دادهساختار جدول، داده ساختار اصلی مورد استفاده در این زبان است.
لوآ یک مفسر اصلی دارد که توسط طراحان اصلی زبان پیادهسازی شدهاست. این مفسر دارای واسط سی نسبتاً سبک و سادهای است.
هر دوی لوآ و جاوااسکریپت از اشیاء نمونهپایه استفاده میکنند. آنها هر دو از شِما الگو گرفتهاند. بسیاری از مفاهیم مشترک میان آنها وجود دارد، گرچه تفاوتهای اساسی در نحو دارند. در طراحی لوآ شبیه به آیکون است، شاید به این علت که هر دو از اسنوبال تأثیر پذیرفتهاند.
زبان لوآ در طول زمان تغییر میکند، قابلیتهایی از آن کاسته و قابلیتهایی بر آن افزوده میشوند. اکنون این زبان در نسخه ۵٫۴٫۴ قرار دارد (۱۴۰۱)
لوآ در صنعت بازیهای ویدئویی محبوبیت زیادی دارد. به جز بازیها لوآ در کاربردهای بسیاری هم تجاری و هم غیرتجاری استفاده شدهاست.
تاریخچه
لوآ در سال ۱۹۹۳ توسط روبرتو یروسالیمچی، لوییز هنریک دی فیگوئردو و والدمر سلس، اعضای تِکگِراف - گروه تکنولوژی گرافیک کامپیوتر (TecGraph) در PUC-Rio، دانشگاه مسیحی ریو د ژانیرو در برزیل ساخته شد.
از سال ۱۹۷۷ تا ۱۹۹۲ در برزیل قوانین سخت تجارت خارجی روی سختافزار و نرمافزار وجود داشت. این قوانین از انگیزه ملی توانایی ساخت نرمافزار و سختافزار در داخل برزیل نشات گرفته بود. این قوانین موجب شد که تکگراف نه از نظر اقتصادی و نه سیاسی امکان داشته باشد که بتواند نرمافزار آماده شده از خارج کشور خریداری کند. به جز این مسئله، جدایی جغرافیای برزیل از بقیه مراکز تحقیقات و توسعه، موجب شد که تکگراف ابزارهایی که نیاز داشت را از هیچ تولید کند.
اجداد لوآ، زبان شیء ساده (Simple Object Language یا SOL) و زبان مدخل داده (Date Entry Language یا DEL) هستند. این دو زبان بهطور مستقل در سالهال ۱۳۷۰–۱۳۷۱ در تکگراف توسعه یافتند تا این دو پروژهٔ متفاوت را منعطفتر کنند (هر دوی این پروژهها برنامههای گرافیکی تعاملی برای کاربردهای مهندسی در شرکت پتروبرس بودند. در SOL و DEL ساختارهای کنترلی وجود نداشت و نیاز به یک زبان برنامهسازی کاملتر احساس میشد. «در ۱۳۷۱ تنها رقیب اصلی لوآ تیکل بود، که بهطور صریح برای استفاده نهفته در برنامهها طراحی شده بود. اما تیکل نحوی ناآشنا داشت، پشتیبانی کافی از توصیف داده نداشت و فقط روی محیط یونیکس قابل استفاده بود. ما لیسپ و شما را استفاده نکردیم چون نحو زشتی داشتند. پایتون هنوز در نوزادی به سر میبرد. در جو آزاد و مستقل تکگراف تقریباً عادی بود که ما باید زبان پردازهنویسی خودمان را اختراع میکردیم… به این علت که خیلی از کاربران محتمل زیان برنامهنویسان حرفهای نبودند، زبان باید از نحو و مفاهیم مرموز خودداری میکرد. پیادهسازی زبان جدید باید قابلیت اجرا روی محیطهای مختلف را میداشت چون کاربران تکگراف انواع متمایزی رایانه در اختیار داشتند. در نهایت، از آنچه که ما انتظار داشتیم که محصولات دیگر تکگراف هم نیاز به نهفتن یک زبان پردازهنویسی داشتند، زبان جدید باید مثال SOL را ادامه میداد و به عنوان کتابخانهای همراه API سی ارائه میشد.»
در نتیجه لوآ به دنیا آمد. سازندههای اشیا لوآی ۱٫۰ که آن زمان کمی متفاوت از حالت سبُک و انعطافپذیر امروزی متفاوت بودند، نحو توصیف دادهٔ SOL را به کار گرفته بودند (ریشه نام لوآ از همینجا میآید، sol در زبان پرتغالی یه معنای خورشید است و لوآ به معنای ماه). ساختارهای کنترلی لوآ (if,while,repeat/until) بیشتر از مدولا قرض گرفته شده بودند، اما از CLU هم تأثیر گرفتهاست (مقدار خروجی چندگانه از توابع و عملیات مقداردهی چندگانه به عنوان گزینهای سادهتر از پارامترهای ارجاعی یا اشارهگرهای صریح). لوآ همچنین از ++C (تعریف محدودتر حوزه متغیرها)، آوک و سنوبال (آرایههای انجمنی) هم تأثیر گرفتهاست. در یک مقاله منتشر شده در مجله «Dr Dobb's Journal»، سازندگان لوآ همچنین اذعان میکنند که زبان لیسپ و شما هم با داده ساختار تکین خود (لیست) تأثیر مهمی روی تصمیم سازندگان بر استفاده از جدول به عنوان داده ساختار اولیه داشتند. مفاهیم فعلی لوآ بیشتر از شما گرفته شدهاند: «از نظر مفهوم لوآ شباهتهای زیادی به شما دارد، گرچه این شباهتها مستقیماً دیده نمیشوند، چون این دو زبان تفاوتهای نحوی زیادی دارند. تأثیر شما روی لوآ به تدریج در زمان تکامل لوآ افزایش یافتهاست: در ابتدا شما فقط زبانی در پسزمینه بود اما بعداً به تدریج به عنوان منبعی برای ایده گرفتن مهم شد، به خصوص به خاطر معرفی توابع ناشناس و حوزهبندی کامل»
کاربردها
لوآ در صنعت بازیهای ویدئویی محبوبیت زیادی دارد. در تعدادی از موتورهای بازی از زبان لوآ برای توسعه و به عنوان اسکریپت استفاده میشود از جمله:
از جملهٔ بازیهایی که از افزونبستههای (Plugin) لوآ پشتیبانی میکنند میتوان به موارد زیر اشاره کرد:
- Crysis
- BZFlag
- PlayStation Home
- Dawn of War
- World of Warcraft
- DeathStatue Game(بازی تندیس مرگ)
- cs2d
به جز بازیها لوآ در کاربردهای بسیاری هم تجاری و هم غیرتجاری استفاده شدهاست از جمله:
- Adobe Photoshop Lightroom
- Apache HTTP Server
- Damn Small Linux
- LuaTex
- nmap
- WireShark
ساختار کلی
دستورها لوآ خط به خط توسط مفسر اجرا میشوند. در لوآ همچون زبانهای پردازهنویسی دیگر تابع اصلی (main) وجود ندارد (میتوان اینطور تصور کرد که کل برنامه یک تابع اصلی است). هر بلوک کد لوآ یا مربوط به مقداردهی متغیر است یا اینکه اجرای یک تابع خاص میباشد. ارتباط با دنیای بیرون از طریق رابط برنامهنویسی نرمافزار انجام میشود. متغیرهایی که تعریف میشوند به دو دسته عمومی و محلی تقسیم میشوند. متغیرهای محلی متغیرهایی هستند که حوزه آنها یک تابع خاص است. در این زبان پیشپردازنده وجود ندارد. در لوآ مستقیماً قابلیتهای زیادی وجود ندارد، بلکه هر برنامهنویس با فرا ساز و کارهای تعبیه شده میتواند قابلیت لازم خود را اضافه کند. این فرا ساز و کارها عبارتند از: آرایههای انجمنی پویا (Dynamic Associative Arrays)، تسهیلات انعکاسی (Reflexive Facilities) و فراجدولها (Metatables)
کد نمونه
کدهای این قسمت همگی کامل هستند و بدون هیچ تغییری تفسیر میشوند.
برنامهٔ نوشتن عبارت "Hello World" بر روی صفحه:
print('Hello World') -- this is a comment
کامنتها با -- مشخص میشوند.
جمع کردن دو عدد در ورودی:
a = io.read()
b = io.read()
print(a+b)
توصیف یک پنجره گرافیکی:
d = dialog {
hbox {
button{ label = "ok" },
button{ label = "cancel" }
}
}
در این مثال dialog با یک جدول توصیف میشود.
تابع فاکتوریل نمونهای از یک تابع بازگشتی در لوآ است، که به دو روش پیادهسازی شدهاست:
function factorial(n)
if n == 0 then
return 1
else
return n * factorial(n - 1)
end
end
function factorial2(n) -- Shorter equivalent of the above
return n == 0 and 1 or n * factorial2(n - 1)
end
در تابع دوم از خصوصیت محاسبه کمینه عبارت بولی از چپ در زبان لوآ استفاده شد. یعنی اینکه فقط اگر شرط n==0
برقرار نباشد محاسبه factorial2(n-1)
انجام میشود.
عوض کردن تابع print تعریف شده در لوآ به گونهای که رفتار آن فقط به ازای حالت خاص ورودی "foo" تفاوت کند.
do
local oldprint = print -- Store current print function as oldprint
function print(s) -- Redefine print function
if s == "foo" then
oldprint("bar")
else
oldprint(s)
end
end
end
این مثال به خوبی نشان میدهد که توابع کتابخانه در لوآ متغیرهای عمومی (یا سطح اول) هستند.
سیستم تایپ
لوآ دارای نوع پویا (Dynamic Type) است، یعنی نوع متغیرها در لوآ در زمان کامپایل مشخص نمیشود، بلکه هر متغیر در زمان اجرا نوعش تعیین میشود. در واقع در تعریف متغیر، نوع متغیر توسط برنامهنویس نوشته نمیشود. زمانی که برنامهنویس به آن متغیر مقداردهی کند، نوع متغیر بسته به چیزی که در آن ذخیره میگردد تعیین میشود. نوع متغیرها ممکن است در طول اجرا تغییر کنند.
نوع دادههای اولیه
در لوآ ۸ نوع داده اولیه وجود دارد:
- نوع بولین: میتواند مقدار true یا false بگیرد.
- عدد: عددی اعشاری با دقت مضاعف (double precision)
- رشته: رشتهای از کاراکترهای یک بایتی. از تمام کاراکترهای ۰ تا ۲۵۵ میتوان در رشته استفاده کرد.
- تابع: با هر تابع هم مثل یک متغیر برخورد میشود. برای آن متغیر عملگر تعریف میگردد.
- جدول: جدول ساختمان دادهای شبیه آرایه است، اما میتوان از هر تایپی (عدد، رشته، ...) برای اندیسگذاری در آن استفاده کرد.
- ریسه: ساختار داده مربوط به همروندسازی اجرا.
- داده کاربر: برای پیوند میان لوآ و C باید اشارهگرها را بتوان در لوآ استفاده کرد. داده کاربر همین کار را میکند.
- Nil: یک نوع خاص که فقط یک مقدار هم دارد که آن مقدار با nil مشخص میشود.
جدولها
دادهساختار اولیه لوآ، جدول است. جدول ساختمان دادهای شبیه آرایه است، اما میتوان از هر تایپی (عدد، رشته، ...) برای اندیسگذاری در آن استفاده کرد. مثلاً در کد زیر:
t = {}
t["mohsen"] = 1
t["ali"]=2
x = t["mohsen"] + t["ali"]
print(x) -- prints 3
با جای اندیسهای عددی از اندیس "mohsen" و "ali" استفاده شدهاست. قبل از اینکه بتوان از عملگر [] استفاده کرد باید متغیر مربوط را مقدار دهی اولیه به مقداری جدولی کرد. سادهترین مقداردهی اولیه مقدار به {} دادن است. میتوان از عبارات زیر هم استفاده کرد:
t = {mohsen = 1, ali = 2}
در کد بالا t مانند مثال قبلی مقداردهی میشود.
دادهساختار جدول (از دید برنامهنویس) شبیه جدولی است که دو ستون دارد: در ستون اول تعدادی صفت مشخص شدهاست (مثلاً رنگ، اندازه، ...). در ستون دوم جلوی هر سطر مقدار متناظر آن صفت آمدهاست. مثلاً روبروی سطر رنگ آمدهاست آبی. برنامهنویس میتواند یک سطر به جدول اضافه کند، یک سطر را حذف کند یا مقدار ستون دوم یک سطر را تغییر دهد. همهٔ این اعمال با این نحو انجام میشود:
t[x] = a
که در آن t یک جدول است و x نام ستون است. اگر x مقداری بوده که قبلاً هم در جدول بوده (در ستون اول)، آنگاه مقدار روبروی آن x میشود. در حالت خاصی که a برابر nil باشد آن ستون حذف میشود. اگر x قبلاً در جدول نبوده آنگاه یک سطر جدید اضافه میشود که در ستون اول آن x و در ستون دوم a آمدهاست. پس یک جدول یک رشته را به یک مقدار متناظر میکند.
از جدول برای پیادهسازی مفاهیم مختلفی استفاده میشود: آرایه، شی و فضای نام
جدول به عنوان آرایه
در مثال زیر:
colors1 = {"blue", "green"}
colors2[1] = "blue"
colors2[2] = "green"
مقداردهی colors1 از نوع جدیدی است که با مقداردهی colors2 معادل است. به این ترتیب یک جدول که اندیسهایش اعداد صحیح ۱ تا n هستند میتواند بیانگر مفهوم یک آرایه یاشد.
جدول به عنوان داده ساختار شی
هر شی را میتوان با یک جدول نمایش داد. مثلاً اگر میخواهیم اتاقی را توصیف کنیم، میتوان از کد زیر استفاده کرد:
room = {color="blue",x=10,y=20}
در این مثال صفتهایی که برای توصیف به کار رفتهاند، عبارتند از طول و عرض اتاق (x و y) و رنگ (color).
لوآ از تایپدهی اردکی استفاده میکند. کلاسها در لوآ تعریف نمیشوند، فقط نمونهها از اشیا تعریف میشوند. در تعریف هر نمونه باید تمامی صفات را معین کرد. وقتی روی هر نمونهای عملیات انجام میشود، درجا امکان انجام عملیات بررسی میشود. اگر این امکان نباشد خطای زمان اجرا پیش میآید (البته میتوان با فراجدولها بعضی از این خطاها را مهار کرد) پس در صورتی که نمونهای خصوصیات کافی داشته باشد، از نظر مفهومی به عنوان یک نمونه از یک کلاس خاص محسوب میشود.
برای سادگی لوآ یک نحو ویژه برای اینگونه استفاده از جدول ایجاد کردهاست. با نوشتن room.x در مثال بالا به مقدار x دست مییابیم، دقیقاً مانند اینکه نوشته باشیم room["x"]. این نمایش مفهوم شیء را میرساند و به کسانی که با نحو object.attribute عادت دارند هم کمک میکنند.
سازوکار تولید مثل
برای سادهسازی فرایند نمونهسازی لوآ از سازوکار تولید مثل استفاده میکند. در این سازکار، یک نمونه ساخته میشود و سپس از تابعی کمکی استفاده میشود تا مانند آن نمونه به تعداد کافی ساخته شود. این تابع به صورت زیر قابل پیادهسازی است.
function clone(o)
local new_o = {}
local i,v = next(o,nil)
while i do
new_o[i] = v
i, v = next(o, i)
end
return new_o
end
در این مثال از ابزارهای انعطاف پذیر استفاده شدهاست.
جدول به عنوان نام فضای نام
با استفاده از یک جدول میتوان توابع مرتبط را در یک فضا نگهداری کرد:
Point = {}
Point.new = function (x, y)
return {x = x, y = y}
end
Point.set_x = function (point, x)
point.x = x
end
ابزارهای انعطافپذیر
در لوآ میتوان روی عناصر یک جدول حرکت کرد. برای این منظور از تابع next استفاده میشود. این تابع یک متغیر میگیرد و اولین عضو بعد از آن در جدول را برمیگرداند (توجه کنید که اعضا در جدول با ترتیبی تعریف نشده نگهداری میشوند). next(nil) اولین عضو جدول را برمیگرداند. این قابلیت به لوآ امکانات زیادی میدهد از جمله تولید مثل اشیا یا ذخیرهسازی و بازیابی تمام متغیرهای عمومی.
حوزه متغیرها
حوزه متغیرها در لوآ دو حالت دارد: عمومی و محلی. متغیرهای محلی با عبارت local در ابتدای آنها مشخص میشوند. متغیرهای عمومی در همه جا قابل دسترسی هستند اما متغیرهای محلی فقط در بدنه همان تابع یا توابع تعریف شده در درون آن تابع قابل دسترسی هستند. مثال زیر به خوبی حوزه متغیرها را نشان میدهد. در تابع print به جای مقادیری که در حوزه نیستند nil چاپ میشود.
y = 1
function f(x)
y = 2
local z = 3
local function g(r)
z = 4
end
g(z)
return z
end
print(f(x)) -- prints 4
print(y) -- prints 2
print(z) -- prints nil
فراجدولها
لوآ دارای سیستم تایپ پویا میباشد؛ بنابراین در زمان اجرا مشخص میشود که انجام یک عمل خاص ممکن است یا نه. مثلاً در کد زیر
a1 = {x=1,y=2}
a2 = {x=3,y=4}
c = a1 + a2
وقتی که کد مربوط به c=a+b تفسیر میشود امکانناپذیر بودن آن مشخص میشود. برای اینکه احاطه برنامهنویس بر روند اجرا بیشتر شود، قابلیت فراجدول به زبان اضافه شد. این قابلیت اجازه میدهد که به جای رخ دادن خطاهای زمان اجرا تابعی از پیش تعیین شده توسط کاربر اجرا شود. این تابع وضعیت خطا را بررسی میکند و میتواند هم فعالیتی را در این زمان انجام دهد (مثلاً ضبط این موضوع که خطا رخ داده در یک فایل) یا اینکه یک مفهوم جدید برای عمل تعریف نشده تعریف کند. در مثال بالا جمع دو جدول را اینگونه تعریف کرد که مقادیر متناظر نامهای یکسان را جمع میکنیم (اگر مقداری در جدول اول بود که متناظری در جدول دوم نداشت حاصل nil میشود). کد این فراجدول اینگونه است:
mt = { __add = function (a, b)
local ret={}
local i=next(a,nil)
while i do
v = a[i]
if b[i]==nil then
return nil
end
v_p = b[i]
e, r = pcall(function() return v + v_p end)
if e then
ret[i] = r
else
return nil
end
i = next(a,i)
end
return ret;
end
}
a1 = {x=1,y=2}
a2 = {x=3,y=4}
setmetatable(a1,mt)
c = a1 + a2
print(c.x,c.y)
این کد در lua نسخه ۵٫۱ به قبل قابل ترجمه نیست چون آن زمان به جای فراجدولها سازوکار مشابهی به نام عقبگردانها (Fallbacks) وجود داشت. عقبگردانها به علت مشکلاتی که در خوانایی و قابلیت نگهداری و استفاده دوباره کد ایجاد میکردند از نسخه ۳٫۰ به بعد از زبان حذف شدند.
برنامهنویسی شیءگرا
شیءگرایی در لوآ بهطور مستقیم وارد نشدهاست. شیءگرایی از طریق سازوکار فراجدولها قابل پیادهسازی است. نمونهای ساده از پیادهسازی شیءگرایی:
a={x=100, y=200, color="red"}
b={x=300, y=400}
setmetatable(b,{ __index = a })
print(b.color) --> red
رابط برنامهنویسی نرمافزار (API)
زبانهای پردازهنویسی درون یک زبان بزرگتر اجرا میشوند. این زبان باید با زبان پردازهنویسی داخلی از طریق یک لایه ارتباط برقرار کند، که آن رابط ربن (برنامهنویسی نرمافزار) است. در لوآ از دو بخش هسته و کمکی تشکیل شدهاست. لایه ربن در لوآ نسبتاً ساده است، چون برخلاف پایتون مدیریت ارجاع در آن نیاز نیست. ربن لوآ همچون خود زبان کمینه است: عملکرد پیشرفتهتر توسط کتابخانهٔ کمکی پشتیبانی میشود، که شامل ماکروهای تعریف شده زیادی است که عملیات پیچیده جدولی را آسان میکند.
یادداشتها
منابع
- ↑ "Lua 5.4.4 now available" (به زبان انگلیسی). 26 ژانویه 2022. Retrieved 28 January 2022.
- ↑ Ierusalimschy, R.; Figueiredo, L. H.; Celes, W. (2007). "The evolution of Lua" (PDF). Proc. of ACM HOPL III. pp. 2–1–2–26. doi:10.1145/1238844.1238846. ISBN 978-1-59593-766-7. S2CID 475143.
- ↑ "The evolution of an extension language: a history of Lua". 2001. Retrieved 2008-12-18.
- Ierusalimschy et al. , "Lua -- an extensible extension language", TeCGraf, Computer Science Department, PUC-Rio, 1995
- Ierusalimschy et al., "Lua 5.1 Reference Manual", 2006-2008 Lua.org, PUC-Rio
- "Metamethods Tutorial", lua-users wiki
- Ierusalimschy et al. , "The Evolution of Lua", Proceedings of the Third ACM SIGPLAN History of Programming Languages Conference, 2007