Bir performans sorununu araştırırken Redis loglarında garip bir satır gördüm, orada bir script çalışıyordu ama Python değil, JavaScript de değildi. Sorduğumda cevap kısaydı: “O Lua, atomik işlem için kullanıyoruz.” O ana kadar Lua’yı sadece World of Warcraft eklentilerinden bilirdim, meğer aynı dil bambaşka bir dünyada, veritabanının içinde de sessizce çalışıyormuş.

Bu yazıda Lua’nın nereden çıktığını, onu bu kadar küçük ve her yere sığabilir yapan özellikleri, table denen tek ama güçlü veri yapısını, değişkenlerin global/lokal ayrımını ve coroutine ile metatable gibi Lua’ya has kavramları anlatacağım. Sonunda basit bir animasyon örneğiyle bunların hepsini bir arada göreceğiz.

Lua nedir, nereden çıktı?

Lua’nın hikayesi 1993’e, Rio de Janeiro’daki PUC-Rio Üniversitesi’ne dayanıyor. O dönem Brezilya’da yabancı yazılım ithal etmek hem zor hem pahalıydı. Üniversitedeki bir ekip, elde var olan uygulamaları genişletmek ve yapılandırmak için kendi gömülebilir script dillerini yazmaya karar verdi. Ortaya çıkan isim Lua, Portekizcede “ay” anlamına geliyor.

Sahada da benzer bir refleks sık görülür: elindeki araç yetmiyorsa bazı ekipler dışarıdan çözüm arar, bazıları oturup kendi aracını yazar. Lua da tam olarak böyle bir kısıtlamadan doğdu, basitliği ve C diliyle sorunsuz entegrasyonu sayesinde kısa sürede oyun endüstrisinin vazgeçilmez dillerinden biri oldu.

Lua’yı bu kadar yaygın yapan şey: her yere sığması

Lua programlama dilinin C ve C++ dilleriyle entegrasyonunu gösteren şema

Lua’nın kaynak kodu 300 KB’tan küçük, derlenmiş hali de sadece birkaç yüz KB yer kaplıyor. Bu ufaklık, onu mikro denetleyicilerden büyük sunuculara kadar hemen her platforma taşınabilir kılıyor. C ve C++ ile entegrasyonu da aynı derecede sorunsuz: birkaç satır C koduyla Lua motoru neredeyse her uygulamaya gömülebiliyor.

Bu yüzden Lua kodlarını beklemediğiniz yerlerde bulabilirsiniz: World of Warcraft’ın eklenti sisteminde, Angry Birds’ün oyun motorunda, hatta Redis’in atomik işlem scriptlerinde. Üstüne otomatik bellek yönetimi de var, garbage collector arka planda sessizce çalışıp yazılımcıyı bellek sızıntısı derdinden kurtarıyor.

Değişkenler: global mi, lokal mi?

Lua'da global ve local değişken tanımlamasını gösteren hello.lua kod ekran görüntüsü

Python ve Go gibi dillerin aksine, Lua’da bir değişkeni tanımlarken hiçbir şey belirtmezseniz o değişken varsayılan olarak global olur. Yerel bir değişken istiyorsanız local anahtar kelimesini eklemeniz gerekiyor. Bu ufak fark, Lua koduna ilk bakan birinin genelde ilk şaşırdığı yer.

İki pratik not: birincisi, virgülle ayırarak aynı satırda birden fazla değişkene değer atayabilirsiniz. İkincisi, iki değişkenin değerini takas etmek için geçici bir üçüncü değişkene ihtiyacınız yok, atama sırasında yerlerini değiştirmeniz yeterli.

Table: Lua’nın tek ama her işi gören veri yapısı

Lua’nın en karakteristik özelliği, diğer dillerde ayrı ayrı duran array, dictionary ve object kavramlarının hepsini tek bir yapıda toplaması: table. Python’daki liste ve sözlük, JavaScript’teki array ve object, Lua’da hepsi table’a karşılık geliyor.

Şöyle bir benzetme yapayım: table, hem bir raf hem bir dosya dolabı gibi düşünülebilir; içine sırayla dizi de koyabilirsiniz, anahtar-değer çiftleri de. Süslü parantezle tanımlanır ve tanımlandıktan sonra dilediğiniz kadar eleman ekleyip çıkarabilirsiniz. Tek dikkat edilmesi gereken yer şu: Lua’da dizi indeksleri sıfırdan değil, birden başlar. Alışkanlıktan sıfırla çağırıp hata almak, neredeyse her yeni Lua yazarının geçtiği bir eşik.

Bu arada ufak bir gözlem: Lua’da print() ile ekrana yazı basmak bile başka dillere göre daha az tören gerektiriyor, import yok, kütüphane çağırmaya gerek yok. Bu sadelik, Lua’yı öğrenmesi kolay ama elden bırakmayan bir dil yapıyor.

Metatable ve coroutine: Lua’nın gizli güçleri

Lua'da coroutine.create, coroutine.yield ve coroutine.resume kullanımını gösteren kod ekran görüntüsü

Table’lar tek başına sınıf ya da nesne gibi davranmaz, burada devreye metatable giriyor. Metatable, bir table’a özel davranışlar tanımlamanızı sağlıyor; mesela iki table’ı toplama işlemine sokmak istiyorsanız bu davranışı metatable üzerinden tarif edersiniz. Lua’nın nesne yönelimli programlamaya yaklaşımı işte bu yapı üzerine kurulu.

Coroutine ise Lua’nın eş zamanlılık çözümü, ama gerçek paralel işlem değil. Bir coroutine’i istediğiniz noktada durdurup daha sonra kaldığı yerden devam ettirebilirsiniz; coroutine.yield() duraklatır, coroutine.resume() kaldığı yerden sürdürür. Asenkron kod yazmak ya da bir işlemi adım adım ilerletmek gerektiğinde tam da bu esneklik işe yarıyor.

Uygulamalı: küçük bir animasyon örneği

Lua ile yazılmış, açı ve mesafe hesabıyla bir gezegenin çember üzerinde döndürüldüğü animasyon kodu

Az önce anlattığımız her şeyin bir arada nasıl durduğunu görmek için basit bir örneğe bakalım. Yukarıdaki kodda planet_pos bir table, içinde x ve y koordinatlarını tutuyor. update() fonksiyonu her karede açıyı biraz artırıp gezegenin konumunu trigonometriyle yeniden hesaplıyor, draw() fonksiyonu ise ekranı temizleyip gezegeni o konuma çiziyor.

Kısacık bir kod ama Lua’nın neyi iyi yaptığını özetliyor: az satırla, az kaynakla, oyun motorunun içine sorunsuz gömülerek çalışan bir mantık. WoW eklentilerinde de birebir aynı desen var, çoğu eklenti tam olarak böyle bir update() döngüsü üzerine kurulu.

Aklınızda kalsın diye

  • Lua, 1993’te Brezilya’da yazılım ithalat kısıtlamalarına bir çözüm olarak PUC-Rio’da doğdu.
  • Küçük boyutu ve C/C++ ile sorunsuz entegrasyonu sayesinde oyun motorlarından Redis’e kadar geniş bir yelpazede kullanılıyor.
  • Table, array/dictionary/object ayrımını ortadan kaldıran tek ve esnek veri yapısı.
  • Metatable nesne yönelimli davranışlar, coroutine ise eş zamanlılık için kullanılıyor.

Bir dahaki sefere bir oyunda ya da Redis scriptinde tuhaf bir satırla karşılaşırsanız, artık kim olduğunu biliyorsunuz. Sorularınız olursa yorumlarda buluşalım.

Yorum Bırakın

Bu site istenmeyenleri azaltmak için Akismet kullanır. Yorum verilerinizin nasıl işlendiğini öğrenin.