لوآ (زبان برنامه‌نویسی)

لوآ یک زبان برنامه‌سازی سبک‌وزن، بازتابنده و امری-تابعی است. زبان لوا با ساختار پردازه‌نویسی (برنامه‌نویسی اسکریپتی) و با هدف داشتن قابلیت‌های گسترش‌پذیر طراحی شده‌است.
فلسفله اولیه ساخت این زبان، استفاده از آن به عنوان یک زبان عمومی و ساده پردازه‌نویسی بود. جامعه هدف لوا شامل کاربران نیمه حرفه‌ای هم می‌شود.
در پردازه‌نویسی برای انجام توصیفات پیچیده از یک زبان نهفته شده در یک زبان دیگر استفاده می‌شود. به خاطر سبک بودن کتابخانه لوا، می‌توان لوا را با تمام قابلیت‌ها به عنوان یک زبان نهفته در سی استفاده نمود. حجم مفسر کامپایل شده حدود ۱۵۰KB است.
در طراحی لوا سعی شده‌است از فرا ساز و کارها بیشتر استفاده شود تا در عین کوچکی، انعطاف‌پذیری زبان افزایش یابد. داده‌ساختار جدول، داده ساختار اصلی مورد استفاده در این زبان است.
لوا یک مفسر اصلی دارد که توسط طراحان اصلی زبان پیاده‌سازی شده‌است. این مفسر دارای واسط سی نسبتا سبک و ساده‌ای است.
هر دوی لوا و جاوااسکریپت از اشیاء نمونه‌پایه استفاده می‌کنند. آن‌ها هر دو از شما الگو گرفته‌اند. بسیاری از مفاهیم مشترک میان آن‌ها وجود دارد، گرچه تفاوت‌های اساسی در نحو دارند. در طراحی لوا شبیه به آیکون است، شاید به این علت که هر دو از اسنوبال تاثیر پذیرفته‌اند.
زبان لوا در طول زمان تغییر می‌کند، قابلیت‌هایی از آن کاسته و قابلیت‌هایی بر آن افزوده می‌شوند. اکنون این زبان در نسخه ۵٫۳٫۲ قرار دارد (آذر ۱۳۹۴)
لوا در صنعت بازی‌های ویدئویی محبوبیت زیادی دارد. به جز بازی‌ها لوا در کاربردهای بسیاری هم تجاری و هم غیرتجاری استفاده شده‌است. نام این زبان از کلمه پرتغالی lua به معنای «ماه» می‌آید.
تاریخچه
لوا در سال ۱۳۷۱ توسط روبرتو یروسالیمچی، لوییز هنریک دی فیگوئردو و والدمر سلس، اعضای تکگراف - گروه تکنولوژی گرافیک کامپیوتر (TecGraph) در PUC-Rio، دانشگاه مسیحی ریو د ژانیرو در برزیل ساخته شد.
از سال ۱۳۵۵ تا ۱۳۷۰ در برزیل قوانین سخت تجارت خارجی روی سخت‌افزار و نرم‌افزار وجود داشت. این قوانین از انگیزه ملی توانایی ساخت نرم‌افزار و سخت‌افزار در داخل برزیل نشات گرفته بود. این قوانین موجب شد که تکگراف نه از نظر اقتصادی و نه سیاسی امکان داشته باشد که بتواند نرم‌افزار آماده شده از خارج کشور خریداری کند. به جز این مسئله، جدایی جغرافیای برزیل از بقیه مراکز تحقیقات و توسعه، موجب شد که تکگراف ابزارهایی که نیاز داشت را از هیچ تولید کند.
«پدر و مادر» تاریخی لوا زبان‌های توصیف داده/تنظیم (SOL (Simple Object Language و (DEL (Date Entry Language هستند. آن دو به‌طور مستقل در سال‌هال ۱۳۷۰–۱۳۷۱ در تکگراف تولید شده‌اند، تا دو پروژه متفاوت را منعطف‌تر کنند (هر دوی این پروژه‌ها برنامه‌های گرافیکی تعاملی برای کاربردهای مهندسی در شرکت پتروبرس بودند. در SOL و DEL ساختارهای کنترلی وجود نداشت و نیاز به یک زبان برنامه‌سازی کامل‌تر احساس می‌شد. «در ۱۳۷۱ تنها رقیب اصلی لوا تیکل بود، که به‌طور صریح برای استفاده نهفته در برنامه‌ها طراحی شده بود. اما تیکل نحوی ناآشنا داشت، پشتیبانی کافی از توصیف داده نداشت و فقط روی محیط یونیکس قابل استفاده بود. ما لیسپ و شما را استفاده نکردیم چون نحو زشتی داشتند. پایتون هنوز در نوزادی به سر می‌برد. در جو آزاد و مستقل تکگراف تقریبا عادی بود که ما باید زبان پردازه‌نویسی خودمان را اختراع می‌کردیم… به این علت که خیلی از کاربران محتمل زیان برنامه‌نویسان حرفه‌ای نبودند، زبان باید از نحو و مفاهیم مرموز خودداری می‌کرد. پیاده‌سازی زبان جدید باید قابلیت اجرا روی محیط‌های مختلف را می‌داشت چون کاربران تکگراف انواع متمایزی رایانه در اختیار داشتند. در نهایت، از آنچه که ما انتظار داشتیم که محصولات دیگر تکگراف هم نیاز به نهفتن یک زبان پردازه‌نویسی داشتند، زبان جدید باید مثال SOL را ادامه می‌داد و به عنوان کتاب‌خانه‌ای همراه API سی ارائه می‌شد.»
در نتیجه لوا به دنیا آمد. سازنده‌های اشیا لوای ۱٫۰ که آن زمان کمی متفاوت از حالت سبک و انعطاف‌پذیر امروزی متفاوت بودند، نحو توصیف داده SOL را به کار گرفته بودند (ریشه نام لوا از همین‌جا می‌آید، sol در زبان پرتغالی یه معنای خورشید است و لوا به معنای ماه). ساختارهای کنترلی لوا (if,while,repeat/until) بیشتر از مدولا قرض گرفته شده بودند، اما از CLU هم تاثیر گرفته‌است (مقدار خروجی چندگانه از توابع و عملیات مقداردهی چندگانه
به عنوان گزینه‌ای ساده‌تر از پارامترهای ارجاعی یا اشاره‌گرهای صریح).
لوا همچنین از ++C (تعریف محدودتر حوزه متغیرها)، آوک و سنوبال (آرایه‌های انجمنی) هم تاثیر گرفته‌است.
در یک مقاله منتشر شده در مجله «Dr Dobbs Journal»، سازندگان لوا همچنین اذعان می‌کنند که زبان لیسپ و شما هم با داده ساختار تکین خود (لیست) تاثیر مهمی روی تصمیم سازندگان بر استفاده از جدول به عنوان داده ساختار اولیه داشتند.
مفاهیم فعلی لوا بیشتر از شما گرفته شده‌اند: «از نظر مفهوم لوا شباهت‌های زیادی به شما دارد، گرچه این شباهت‌ها مستقیما دیده نمی‌شوند، چون این دو زبان تفاوت‌های نحوی زیادی دارند. تاثیر شما روی لوا به تدریج در زمان تکامل لوا افزایش یافته‌است: در ابتدا شما فقط زبانی در پس‌زمینه بود اما بعدا به تدریج به عنوان منبعی برای ایده گرفتن مهم شد، به خصوص به خاطر معرفی توابع ناشناس و حوزه‌بندی کامل»
کاربردها
لوا در صنعت بازی‌های ویدئویی محبوبیت زیادی دارد.
در تعدادی از موتورهای بازی از زبان لوا برای توسعه و به عنوان اسکریپت استفاده می‌شود از جمله:
CryEngine
Leadwerks Engine
Corona SDK
از جمله بازی‌هایی که از افزون‌بسته‌های (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
این مثال به خوبی نشان می‌دهد که توابع کتابخانه در لوا متغیرهای عمومی (یا سطح اول) هستند.
سیستم تایپ
لوا پویندگی تایپ دارد، یعنی نوع متغیرها در لوا در زمان همگردانی مشخص نمی‌شود، بلکه هر متغیر در زمان اجرا نوعش تعیین می‌شود. در واقع در تعریف متغیر، نوع متغیر توسط برنامه‌نویس نوشته نمی‌شود. زمانی که برنامه‌نویس یک عمل مقداردهی به آن متغیر انجام می‌دهد، نوع متغیر بسته به چیزی که در متغیر ذخیره می‌شود تعیین می‌شود. نوع متغیرها می‌تواند در طول اجرا تغییر کند.
نوع داده‌های اولیه
در لوا ۸ نوع داده اولیه وجود دارد:
نوع صحت (Boolean): مقدار true یا false می‌تواند بگیرد.
عدد: عددی اعشاری با دقت دوگانی (double precision)
رشته: رشته‌ای از کاراکترهای یک بایتی. از تمام کاراکترهای ۰ تا ۲۵۵ می‌توان در رشته استفاده کرد.
تابع: با هر تابع هم مثل یک متغیر برخورد می‌شود. برای آن متغیر عملگر)(تعریف شده‌است.
جدول: جدول ساختارداده‌ای شبیه آرایه است، اما می‌توان از هر تایپی (عدد، رشته، …) برای اندیس‌گذاری در آن استفاده کرد.
ریسه: ساختار داده مربوط به همروندسازی اجرا
کاربرداده (userdata): برای پیوند میان لوا و 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 مانند مثال قبلی مقداردهی می‌شود.
داده‌ساختار جدول (از دید برنامه‌نویس) شبیه جدولی است که دو ستون دارد: در ستون اول تعدادی صفت مشخص شده‌است (مثلا رنگ، اندازه، …). در ستون دوم جلوی هر سطر مقدار متناظر آن صفت آمده‌است. مثلا روبروی سطر رنگ آمده‌است آبی.
برنامه‌نویس می‌تواند یک سطر به جدول اضافه کند، یک سطر را حذف کند یا مقدار ستون دوم یک سطر را تغییر دهد. همه این اعمال با این نحو انجام می‌شود:
tx = a
که در آن t یک جدول است و x نام ستون است.
اگر x مقداری بوده که قبلا هم در جدول بوده (در ستون اول)، آنگاه مقدار روبروی آن x می‌شود. در حالت خاصی که a برابر nil باشد آن ستون حذف می‌شود. اگر x قبلا در جدول نبوده آنگاه یک سطر جدید اضافه می‌شود که در ستون اول آن x و در ستون دوم a آمده‌است.
پس یک جدول یک رشته را به یک مقدار متناظر می‌کند.
از جدول برای پیاده‌سازی مفاهیم مختلفی استفاده می‌شود: آرایه، شی و فضای نام
جدول به عنوان آرایه
در مثال زیر:
colors1 = {"blue", "green"
colors21 = "blue"
colors22 = "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_oi = 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 = ai
if bi==nil then
return nil
end
v_p = bi
e, r = pcall(function() return v + v_p end)
if e then
reti = 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)
زبان‌های پردازه‌نویسی درون یک زبان بزرگتر اجرا می‌شوند. این زبان باید با زبان پردازه‌نویسی داخلی از طریق یک لایه ارتباط برقرار کند، که آن رابط ربن (برنامه‌نویسی نرم‌افزار) است.
در لوا از دو بخش هسته و کمکی تشکیل شده‌است. لایه ربن در لوا نسبتا ساده است، چون برخلاف پایتون مدیریت ارجاع در آن نیاز نیست. ربن لوا همچون خود زبان کمینه است: عملکرد پیشرفته‌تر توسط کتاب‌خانه کمکی پشتیبانی می‌شود، که شامل ماکروهای تعریف شده زیادی است که عملیات پیچیده جدولی را آسان می‌کند.