2 Tipler ve Değerler

2 Tipler ve Değerler

Lua dinamik tipli bir dildir. Dilde tip tanımlaması olmayıp; Her değer kendi tipini taşır.

Lua 'da sekiz temel tip vardır: nil (HİÇ), boolean (true false ikilisi), number (sayı), string (metin), userdata (kullanıcı verisi), function (fonksiyon), thread (iş parçacığı) ve table (tablo). type fonksiyonu verilen bir değerin tip adını döndürür:

print(type("Merhaba dünya"))    --> string
print(type(10.4 * 3))           --> number
print(type(print))              --> function
print(type(type))               --> function
print(type(true))               --> boolean
print(type(nil))                --> nil
print(type(type(X)))            --> string

X'in değeri ne olursa olsun son satır "string" sonucunu döndürür çünkü type fonksiyonun döndürdüğü sonuç daima stringdir.

Değişkenler öntanımlamalı tipe sahip değildir; her değişken herhangi bir tipte değer içerebilir:

print(type(a))          --> nil ('a' 'ya henüz bir değer ataması yapılmadı)
a = 10
print(type(a))          --> number
a = "bir metin dizesi!!"
print(type(a))          --> string
a = print               -- evet, bu geçerli!
a(type(a))              --> function

Son iki satıra dikkat: fonksiyonlar Lua 'da birinci-sınıf değerdir; Yani onları diğer herhangi bir değer gibi manipüle edebiliriz. (Bölüm 6'da bu konu hakkında daha fazlası olacak.)

Tek bir değişkeni farklı tiplerde kullanım genelde kirli kodlamayı doğurur. Ancak bazen bu mantıklı kullanımda yararlıdır örneğin anormal bir durumu bildik bir dönüş değeriyle ayırt etmek için nil kullanımı.

2.1 Nil

Nil tek değerli bir tiptir, HİÇ, ana özelliği herhangi diğer değerden farklılığıdır. Daha önce gördüğümüz üzere global değişken ilk atamadan önce varsayılanda nil değerine sahiptir ve onu silmek için global değişkene nil atayabilirsiniz. Lua , kullanışlı bir değerin yokluğunu temsil etmek için değersiz bir tip olarak nil'i kullanır.


2.2 Boolean

Boolean tipi iki değere sahiptir,klasik Boolean matematiği değerlerini temsil eden false ve true. Boolean 'lar koşul değerlerini tekelinde de tutmazlar: Lua 'da herhangi bir değer bir koşulda kullanılabilir. koşullarda (kontrol yapılarındakilerden) false ve nil ikilisi false diğer herşey true olarak dikkate alınır. Diğer bazı script dillerinin aksine Lua 'nın sıfır ve boş string ikilisini koşul testlerinde true olarak değerlendirdiğini unutmayın.


2.3 Number

Number tipi gerçek sayıları(double-çift duyarlıklı kayan noktalı sayıları)  temsil eder. Lua 'da ihtiyaç yokmuş gibi tamsayı tipi yoktur. Kayan noktalı aritmetiği hakkında yaygın yanlış düşünceler vardır; Bazı insanlar basit bir artışın bile kayan noktalı sayılarda  garipliklere sebep olacağından çekinir. Gerçek şu ki bir tam sayıyı temsil edecek bir double kullandığınızda hiçbir yuvarlama hatası olmaz (sayı 10 üstü 14'ten büyük değilse). Bilhassa bir Lua sayısı herhangi bir 32-bit tam sayıyı yuvarlama sorunu olmadan temsil edebilir. Dahası, en modern işlemciler kayan noktalı aritmetiğini tamsayılar kadar hızlı(hatta daha hızlı) yapar.

Yine de Lua 'nın derlenmesi sırasında  sayılar için tek duyarlıklı kayan(float) , longlar gibi diğer tiplerin kullanılması için ayarlamalar yapılabilir. Bu, özellikle kayan noktalı için donanım desteği olmayan platformlar için önemlidir. Ayrıntılı yönergeler için luaconf.h dosyasına bakın.

sayısal sabitleri isteğe bağlı ondalık bölümlü, artı isteğe bağlı ondalık üslü yazabiliriz. Geçerli sayısal sabit örnekleri şunlardır:

4 0.4 4.57e-3 0.3e12 5e+20

2.4 String

Lua 'da stingler geleneksel anlamdadır: karakterler dizisi. Lua,  8 bit temiz ve stringleri tümleşik sıfır dahil herhangi sayısal kodlu karakter içerebilir. Bunun anlamı bir string'de herhangi ikili-binary veriyi depolayabilirsiniz demektir .

Lua 'da stringler değişmez değerlerdir. C 'de yapabilirsiniz ama Lua 'da bir string içindeki karakteri değiştiremezsiniz; Bunun yerine sıradaki örnekte olduğu gibi istenen değişikliklerle yeni bir string oluşturursunuz:

a = "bir string"
b = string.gsub(a, "bir", "diger")  -- string 'in bir bölümünü değiştir
print(a)                            --> bir string
print(b)                            --> diger string


Lua 'da stringler diğer tüm Lua nesneleri (tablolar, fonksiyonlar vb) gibi otomatik bellek yönetimine tabidir. Bu, stringler için tahsisat ve tasfiye işlemleri hakkında endişelenmenize gerek olmadığı anlamına gelir; Lua bunu sizin için halleder. Bir string tek bir harf veya tüm bir kitabı içerebilir. Lua uzun stringleri oldukça verimli bir şekilde yönetir. 100K veya 1M karakterli stringleri işleyen programlar Lua 'da olağandışı değildir.

Eşleşecek şekilde tek veya çift tırnaklar arasına stringleri koyabiliriz:

a = "satır"
b = 'diger satır'

Stil meselesi olarak, string'in kendisi tırnak içermediği sürece her zaman aynı tür tırnak (tek veya çift) kulanılmalı bir programda; string'in kendi tırnak içeriyorsa diğerini veya kaçış sekansı(\) kullanın. Lua 'da stringler devam da ki gibi C tarzı kaçış sekansları içerebilir:

\a zil
\b geri al
\f sonraki sayfa
\n yeni satır
\r satır başı
\t yatay sekme
\v dikey sekme
\\ ters slash
\" çift tırnak
\' tırnak

Aşağıdaki örneklerde kullanımları gösterilmekte:

> print("ilk satır\nsonraki satır\n\"çift tırnak içinde\", 'tırnak içinde'")
ilk satır
sonraki satır
"çift tırnak içinde", 'tırnak içinde'

> print('tırnak içinde ters slash: \'\\\'')
tırnak içinde ters slash: '\'

> print("basit yoldan: '\\'")
basit yoldan: '\'

ddd 'nin üç basamağa kadar bir seri olduğu,  \ddd şeklinde bir kaçış sekansı ile bir string 'de bir karakteri sayısal değeriyle de belirtebiliriz. Biraz karışık gelebilir ama ASCII kullanılan bir sistemde devamdaki ikisi aynı; "alo\n123\""      '\97lo\10\04923"'   : 97 'a' 'nın , 10 yenisatır, 49 '1' sayısının ASCII kodu.(örnekte 49'u üç basamaklı 049 olarak yazdık çünkü onu başka sayı izliyor aksi halde Lua onu 492 olarak okuyacak)

Uzun yorumlarda yaptığımız gibi çift köşeli parantezler arasına uzun stringleri koyabiliriz. Bu formda çok satırlı string kaçış sekansları yorumlanmadan işleme alınır. Dahası bu form da stringin ilk karakteri yenisatır karakteri olduğunda bu ilk karakter yok sayılır. Bu form özellikle aşağıdaki örnekte olduğu gibi program parçaları içeren stringler yazmak için  uygundur:

sayfa= [[
<html>
<head>
<title>HTML Sayfasi</title>
</head>
<body>
<a href="https://lua-turkiye.blogspot.com/">Lua-Turkiye</a>
</body>
</html>
]]

write(sayfa)

Bazen, a=b[c[i]] gibi bir şey içeren kod parçası ( bu kodda ]] 'e dikkat) yada kod içeren açıklamalar kullanmak isteyebilirsiniz. Bu tür durumları yönetmek için(Bu özellik Lua 5.1'de yeni), iki açık köşeli parantez arasında herhangi sayıda eşittir işareti ekleyebilirsiniz, [===[ gibi. Bu değişiklikten sonra stringiniz en yakın iki kapalı köşeli parantez arasında aynı miktarda eşittir işaretinde yalnızca sonlanır, yani ]===]. Farklı sayıda eşittir işaretli köşeli parantez çiftleri göz ardı edilir. Uyan sayıda eşittir işaretleriyle, kaçışlar eklemeden herhangi string'i böylece kullanabilirsiniz.

Bu aynı imkan yorumlar için de geçerlidir. Örneğin eğer uzun yorumunuz --[=[ ile başlıyorsa en yakın  ]=] 'e kadar devam edebilir. Bu, hazırda bir yorumun parçası olan kod parçalarını yorum dışına atmayı da kolaştırır.

Lua yürütme esnasında sayılar ve stringler arasında otomatik dönüşüm sağlar. Bir string'e herhangi bir sayısal işlem uygulandığında stringi sayıya çevirmeye girişir:

print("10" + 1)         --> 11
print("10 + 1")         --> 10 + 1
print("-5.3e-10" * "2") --> -1.06e-09
print("merhaba" + 1)    -- HATA ("merhaba" çevrilemiyor)

Lua sadece aritmetik işlemlerde değil aynı zamanda bir sayı beklenen diğer yerlerde de bu tür zorlamayı uygular.

Bunun tersi olarak, Lua bir string beklenen yerde bir sayı bulursa bu sayıyı string'e dönüştürür:

print(10 .. 20)         --> 1020

(.. Lua'da string birleştirme operatörüdür. Rakamın sağına yazarken bir boşluk ile ayırmalısınız; Aksi takdirde Lua ilk noktanın bir ondalık nokta olduğunu düşünür.)

Bugün, bu otomatik dönüştürmenin Lua tasarımında iyi bir fikir olup olmadığından emin değiliz. Bir kural olarak, onlara güvenmemek daha iyidir. Birkaç yerde kullanışlıdır ancak dile ve bazen bunları kullanan programlara karmaşıklık ekler. Sonuçta, bu dönüşümlere rağmen stringler ve sayılar farklı şeylerdir. 10=="10" gibi bir karşılaştırma sonucu false 'dir çünkü 10 bir sayıdır “10” bir string. Bir string'i bir sayıya dönüştürmeniz gerekiyorsa string uygun bir sayı ifade etmediğinde nil döndüren tonumber fonksiyonunu kullanabilirsiniz.

line = io.read()    -- satırı oku
n = tonumber(line)  -- sayıya çevirmeyi dene

if n == nil then
    error(line .. " geçerli bir sayı değil")
else
    print(n * 2)
end


Bir sayıyı bir string'e dönüştürmek için tostring fonksiyonunu çağırabilir veya sayıyı  boş string ile birleştirebilirsiniz:

print(tostring(10) == "10") --> true
print(10 .. "" == "10")     --> true

Bu tür dönüşümler her zaman geçerlidir.

Lua 5.1'de, '#'(uzunluk operatörü olarak isimlendirilir) ön ekini kullanarak bir string'in uzunluğunu elde edebilirsiniz.

a = "merhaba"
print(#a)               --> 7
print(#"güle\0güle")    --> 9

2.5 Table

Tablo tipi, bir ilişkisel dizi(associative array- dizideki elemanların 2 kısmı vardır; key(anahtar) ve value(değer) ) uyarlamasıdır. İlişkisel dizide indekslemede yalnızca sayılarla değil aynı zamanda stringlerle veya nil hariç dildeki diğer değerlerde kullanılabilir. Ayrıca tablolar sabit boyutlu değildir; bir tabloya dinamik olarak istediğiniz kadar çok öğe ekleyebilirsiniz. Tablolar Lua 'da ana (aslında tek) ve güçlü bir veri yapılandırma mekanizmasıdır . Geleneksel dizileri(array), sembol tabloları, kümeler(set), kayıtlar(record), kuyruklar(queue) ve diğer veri yapılarını basit, tekdüze ve verimli bir şekilde temsil etmek için tabloları kullanırız. Lua aynı zamanda modülleri, paketleri ve nesneleri de temsil etmek için tabloları kullanır. io.read yazarak "io modülünden read fonksiyonu" olarak anlıyoruz. Lua için bunun anlamı key olarak read stringini kullanan io tablosu indeksi.


Lua tabloları ne değerdir ne de değişkendirler; Onlar nesnelerdir. Java veya Scheme 'deki diziler hakkında bilginiz varsa anlatmak istediğimden bir fikriniz oluşmuştur. Bir tabloyu dinamik olarak ayrılmış bir nesne olarak düşünebilirsiniz; programınız onları yalnızca referans (veya pointerlar) ile manipüle eder. Sahne arkasında yeni tabloların oluşturulması veya hiçbir gizli kopya yoktur. Ayrıca Lua 'da bir tabloyu deklare etmek zorunda değilsiniz; Aslında bunun için bir yol da yoktur. Bir kurucu ifade aracılığıyla tabloları oluşturursunuz, en basit formda {} olarak yazılır:

a = {}              -- bir tablo yarat ve referansını 'a' 'da tut.
k = "x"
a[k] = 10           -- yeni giriş,  key="x" ve value=10 ile
a[20] = "mükemmel"  -- yeni giriş, anahtar=20 ve deger="mükemmel" ile
print(a["x"])       --> 10
k = 20
print(a[k])         --> "mükemmel"
a["x"] = a["x"] + 1 -- "x" girişini arttır
print(a["x"])       --> 11

Bir tablo her zaman anonimdir. Tablonun kendisi ve tabloyu tutan değişken arasında sabit bir ilişki yoktur:

a = {}
a["x"] = 10
b = a           -- 'b' , 'a' ile aynı tabloya referanstır
print(b["x"])   --> 10
b["x"] = 20
print(a["x"])   --> 20
a = nil         -- artık sadece ’b’ tabloya referans
b = nil         -- tabloya referans kalmadı


Bir programın bir tabloya başvurusu kalmayınca Lua 'nın çöp toplayıcısı(garbage collector) nihayetinde tabloyu siler ve belleğini yeniden kullanılır.

Her tablo farklı tipte indekslerle değerler depolayabilir ve yeni girdiler barındırmak için gerektiği kadar büyür:

a = {}          -- boş tablo
-- 1000 yeni giriş oluştur
for i = 1, 1000 do
    a[i] = i * 2
end

print(a[9])     --> 18
a["x"] = 10
print(a["x"])   --> 10
print(a["y"])   --> nil


Son satıra dikkat: global değişkenler gibi ilkleme yapılmadığında tablo alanları nil olarak değerlendirilir. Ayrıca global değişkenler gibi  bir tablo alanını silmek için nil atayabilirsiniz. Bu bir tesadüf değil: Lua global değişkenleri sıradan bildik tablolarda depolar.Bu konuyu Bölüm 14 ' te daha fazla ele alacağız.

Kayıtları(record) temsil etmek için indeks olarak alan adı kullanırsınız. Lua, a["alanadi"] için sözdizimsel hoşluk(syntactic sugar) olarak a.alanadi şeklinde imkanı destekler. Velhasıl önceki örneğin son satırlarını aşağıdaki gibi daha temiz bir şekilde  yazabiliriz:


a.x = 10    -- a["x"] = 10 ile aynı
print(a.x)  -- print(a["x"]) ile aynı
print(a.y)  -- print(a["y"]) ile aynı

Lua için iki formda eşdeğer ve serbestçe mikslenebilir; bir insan evladı için iki form zihinde farklı ışıkları yakabilir. Noktalı kullanım, tabloyu, önceden tanımlanmış anahtarlı sabit bir sete sahip olduğumuz, bir kayıt olarak kullandığımızı temiz bir şekilde gösterir. stringli kullanım, tablonun a anahtarı olarak herhangi bir stringe sahip olabileceği ve bir sebeple bu özel anahtarı manipüle edebileceğimiz düşüncesini verebilir.

Yeni başlayanlar için yaygın bir hata a.x ile a[x] 'i karıştırmasıdır. İlk form a["x"] 'i temsil eder, yani tablo "x" stringi ile indekslenmiştir. İkinci formda tablo x değişkeninin değeriyle indekslenmiştir. Farkı görelim:

a = {}
x = "y"
a[x] = 10   -- "y" alanına 10 koy
print(a[x]) --> 10 -- "y" alanının değeri
print(a.x)  --> nil -- "x" alanının değeri(tanımsız)
print(a.y)  --> 10 -- "y" alanının değeri

Geleneksel bir diziyi(array) veya listeyi temsil etmek için tabloyu sadece tamsayı keylerle kullanırsınız. Ne bir usul ne de bir boyut deklaresine gerek yoktur; Yalnızca ihtiyacınız olan öğeleri ilklersiniz:

-- a tablosunda depolamak üzere 10 satır oku
a = {}

for i = 1, 10 do
    a[i] = io.read()
end

a tablosunu herhangi bir değerle indeksleyebileceğiniz için sizi memnun edecek herhangi bir sayı ile dizinin indekslerini başlatabilirsiniz. Ancak Lua 'da 1 ile (C 'deki gibi 0 değil) diziler başlamak gelenek olup birkaç tesis bu konvensiyona bağlıdır.

Lua 5.1'de uzunluk operatorü '#' bir dizinin veya listenin son indeksini(veya boyunu) döndürür. (Lua 5.0 uzunluk operatörünü desteklemez table.getn fonksiyonuyla aynı sonucu elde edebilirsiniz). Örneğin aşağıdaki kodla son örnekteki okunan satırları yazdırabilirsiniz:

-- satirlari yazdir
for i = 1, #a do
    print(a[i])
end


uzunluk operatörü birkaç yaygın Lua deyişi sağlar:

print(a[#a])    -- 'a' listesinin son değerini yazdır
a[#a] = nil     -- bu son değeri kaldır
a[#a + 1] = v   -- listenin sonuna 'v' ekle

Örnek olarak aşağıdaki kod bir dosyanın ilk 10 satırını okumak için alternatif bir yol gösterir:

a = {}

for i = 1, 10 do
    a[#a + 1] = io.read()
end

Bir dizi aslında bir tablo olduğundan "boyu" kavramı biraz bulanık olabilir. Örneğin aşağıdaki dizinin boyu ne olmalıdır?

a = {}
a[10000] = 1


İlklenmemiş herhangi indeks nil olarak sonuçlanır hatırlayalım; Lua bu değeri dizinin sonuna bulmak için bir işaret olarak kullanır. Dizi boş olduğunda — içindeki öğeler nil — uzunluk operatörü bu nil öğelerin herhangi birini bitiş işareti olarak alabilir. Tabii ki bu tahmin edilemezlik pek isteyebileceğiniz bir şey değildir. Bu nedenle uzunluk operatörünü delikler içerebilecek diziler üzerinde kullanmaktan kaçınmalısınız. Çoğu dizi delik içeremiyebilir (örneğin önceki örnekte bir dosya satırı nil olamaz) ve bu nedenle çoğu zaman uzunluk operatörü  kullanımı güvenlidir. Son indeksine kadar, delik içerebilecek dizileri yönetmek isterseniz tablonun en büyük pozitif sayısal indeksini döndürecek olan table.maxn  fonksiyonunu (Bu fonksiyon Lua 5.1'de yeni) kullanabilirsiniz:

a = {}
a[10000] = 1
print(table.maxn(a)) --> 10000

Herhangi bir tiple bir tabloyu indeksleyebilmemiz bir tarafa, bir tablo indekslenirken denklik esasında incelik gösterilmeli. 0 sayısı ile ve "0" string ile bir tabloyu indeksleyebiliriz ancak bu iki değer (denkliğe göre) farklıdır ve bu nedenle bir tabloda farklı  girdileri gösterir. Benzer şekilde, "+1", "01" ve "1" stringleri farklı girdileri gösterir. indekslerinizin gerçek tipi hakkında şüphede olduğunuzda emin olmak için dönüştürme kullanın:

i = 10
j = "10"
k = "+10"

a = {}
a[i] = "bir değer"
a[j] = "başka değer"
a[k] = "bambaşka değer"

print(a[j])             --> başka değer
print(a[k])             --> bambaşka değer
print(a[tonumber(j)])   --> bir değer
print(a[tonumber(k)])   --> bir değer

Bu noktaya dikkat etmemeniz programınızda ince hataları ortaya çıkarabilir.

2.6 Function

Fonksiyonlar, Lua 'da birinci-sınıf değerdirler. Bunun anlamı, fonksiyonlar değişkenlerde depolanabilir, diğer fonksiyonlara argüman olarak geçilebilir ve sonuç olarak döndürülebilir. Böyle imkanlar dile büyük esneklik verir: Bir program yeni işlevsellik eklemek üzere bir fonksiyonu yeniden tanımlayabilir veya güvenilmeyen kod parçası(ağdan alınan kodlarda mesela) çalıştırırken güvenli bir ortam oluşturmak için fonksiyonu basitçe silebilir. Ayrıca Lua uygun kapsamlarla iç içe geçen fonksiyonlar oluşturma imkanı sağladığından fonksiyonel programlama için güzel destek sunar; Bölüm 6'a kadar bekle. Son olarak Bölüm 16'da göreceğimiz üzere birinci-sınıf  fonksiyonlar Lua'nın nesne yönelimli tesisinde önemli rol oynar.


Lua, Lua da yazılmış fonksiyonları ve C de yazılmış fonksiyonları çağırabilir. Lua daki tüm standart kütüphaneler C de yazılmıştır.  string manipülasyonu, tablo manipülasyonu, Giriş/Çıkış,  temel işletim sistemi imkanlarına erişim, matematiksel fonksiyonlar ve hata ayıklama için fonksiyonlar içerir bu kütüphaneler. Uygulama programları C 'de başka fonksiyonlar tanımlayabilir.

Lua fonksiyonlarını Bölüm 5'de ve C fonksiyonlarını Bölüm 26 'da ele alacağız.

2.7 Userdata ve Thread

KullanıcıVerisi(Userdata) tipi zalim C verilerini Lua değişkenlerinde depolama imkanı verir. atama ve denklik testleri hariç Lua da hiçbir öntanımlı  işlem yoktur. Userdata,  C'de yazılmış bir kütüphane veya bir uygulama programı tarafından oluşturulan yeni tipleri temsil etmek için kullanılır; Örneğin, standart io kütüphanesi dosya temsillerinde onları kullanır. C API konusuna geçtiğimizde Userdata hakkında daha fazlasını ele alacağız.


eş yordamları(coroutine) ele aldığımız Bölüm 9 da iş parçacığı(thread) tipini açıklayacağım.

Hiç yorum yok:

Yorum Gönder