Fonksiyonlar, Lua'da ifade ve deyimleri soyutlamada ana mekanizmadır. Fonksiyonlar, belli bir görev (bazen diğer dillerde prosedür veya alt yordam olarak adlandırılırlar) veya hesaplama - değerler döndürme ikilisini yerine getirebilir. İlk durumda fonksiyon çağrısını bir deyim olarak kullanıyoruz; ikinci durumda bir ifade olarak kullanıyoruz:
print(8*9, 9/8) a = math.sin(3) + math.cos(10) print(os.date())
Her iki durumda da parantez içinde argümanlar listesini yazıyoruz. Fonksiyon çağrısının argümanları yoksa çağrıyı belirtmek için yine de boş liste () yazmamız gerekir. Bu kural için özel bir durum: eğer fonksiyonun tek argümanı var ve bu argüman bir string öbeği ya da bir tablo kurucusuysa o zaman parantezler isteğe bağlıdır :
print "Merhaba Dunya" <--> print("Merhaba Dunya") dofile 'a.lua' <--> dofile ('a.lua') print [[coksatirli <--> print([[coksatirli mesaj]] mesaj]]) f{x=10, y=20} <--> f({x=10, y=20}) type{} <--> type({})
Lua ayrıca nesne yönelimli çağrılar için özel bir sözdizimi sunar, : operatörü. Bu sadece o.foo(o,x) gibi bir ifadenin o:foo(x) şeklinde yazma yöntemidir. Bu, o.foo çağrısına ilk argüman olarak ekstra o'u ekler. 16. bölümde bu tür çağrıları (ve nesne yönelimli programlamayı) daha ayrıntılı olarak ele alacağız.
Bir Lua programı Lua ve C ikilisinde (veya ev sahibi uygulamaca kullanılan başka bir dilde) tanımlanmış fonksiyonları kullanabilir. Misal standart Lua kütüphanesinden tüm fonksiyonlar C ile yazılmıştır. Tabi bu durum Lua programcılarını ilgilendirmez: bir fonksiyonu çağırırken Lua 'da tanımlanmış fonksiyonlar ile C'de tanımlanmış fonksiyonlar arasında bir fark yoktur.
Diğer örneklerde gördüğümüz üzere bir fonksiyon tanımlamasının devamdaki gibi genel bir sözdizimi vardır:
function add (a) local sum = 0 for i,v in ipairs(a) do sum = sum + v end return sum end
Bu sözdiziminde bir fonksiyon tanımlaması bir isime (önceki örnekte add), parametreler listesine ve deyimler listesi olan bir gövdeye sahiptir.
Parametreler, fonksiyon çağrısında geçilen argümanların değerleriyle ilklenen yerel değişkenler olarak çalışır tamamen. Parametrelerinin sayısından farklı argüman sayısıyla bir fonksiyonu çağırabilirsiniz. Lua çoklu atamada yaptığı gibi argümanların sayısını parametrelerin sayısına ayarlar: fazlalık argümanlar atılır; fazlalık parametreler nil alır. Örneğin devamdaki gibi bir fonksiyonumuz varsa
function f(a, b) return a or b end
argümanlardan parametrelere devamdaki eşlemeye sahip olacağız:
ÇAĞRI PARAMETRELER
f(3) a=3, b=nil
f(3, 4) a=3, b=4
f(3, 4, 5) a=3, b=4 (5 es geçilir)
Bu davranış programlama hatalarına (çalışma zamanında kolayca belli eden) yol açabilse de özellikle varsayılan argümanlar için de yararlıdır. Örneğin global bir sayacı artırmak için aşağıdaki fonksiyonu göz önünde bulundurun:
function sayacArttir (n) n = n or 1 sayac = sayac + n end
Bu fonksiyon varsayılan argümanı olarak 1'e sahiptir; diğer bir deyişle, argümansız sayacArttir() çağrısı sayac 'ı bir artırır. sayacArttir()'ı çağırdığınızda, Lua ilkin n' i nil ile ilkler; or ikinci argümanında sonuçlanır ve haliyle Lua n'e 1 varsayılanını atar.
5.1 Çoklu Sonuçlar
Lua 'nın alışılmadık ama oldukça kullanışlı bir özelliği, fonksiyonların birden fazla sonuç döndürebilmesidir. Lua'daki çeşitli öntanımlı fonksiyonlar çoklu değer döndürür. Bir örnek, stringde şablon arayan string.find fonksiyonudur. Bu fonksiyon şablonu bulduğunda iki indeks döndürür: şablonun eşleştiği karakter indeksinin başlangıç ve bitiş yeri. çoklu atama programa iki sonuç almaya imkan verir:
s, e = string.find("merhaba Lua kullanicilari", "Lua") print(s, e) --> 9 11
Lua'da yazılmış fonksiyonlar da return kelimesinden sonra hepsini listeleyerek birden fazla sonuç döndürebilir. Örneğin bir dizideki en büyük öğeyi bulmak için olan bir fonksiyon hem en büyük değeri hem de onun konumunu döndürebilir:
function maximum (a) local mi = 1 -- en büyük değerin indeksi local m = a[mi] -- en büyük değer for i,val in ipairs(a) do if val > m then mi = i; m = val end end return m, mi end print(maximum({8,10,23,12,5})) --> 23 3
Lua daima çağrının durumuna göre fonksiyon sonuç sayısına ayar çeker. Bir fonksiyonu bir deyim olarak çağırdığımızda Lua tüm sonuçları fonksiyondan atar. Bir ifade olarak çağrıyı kullandığınızda Lua sadece ilk sonucu tutar. Tüm sonuçları yalnızca çağrı, ifadeler listesindeki son (veya tek) ifade olduğunda alırız. Bu listeler Lua'da dört yapıda görülür: çoklu atamalar, fonksiyon çağrıları için argümanlar, tablo kurucuları ve return deyimleri. Tüm bu durumları göstermek üzere bir sonraki örnekler için aşağıdaki tanımlamalara sahip olduğumuzu varsayacağız:
function foo0 () end -- sonuç döndürmüyor function foo1 () return "a" end -- 1 sonuç döndürüyor function foo2 () return "a","b" end -- 2 sonuç döndürüyor
çoklu atamada son (veya tek) ifade olarak bir fonksiyon çağrısı, değişkenlerle eşleşmesi için gereken kadar sonuç üretir:
x,y = foo2() -- x="a", y="b" x = foo2() -- x="a", "b" es geçilir x,y,z = 10,foo2() -- x=10, y="a", z="b
Bir fonksiyonun sonucu yok veya ihtiyacımız olduğu kadar çok sonucu yoksa Lua eksik değerler için nil ler üretir:
x,y = foo0() -- x=nil, y=nil x,y = foo1() -- x="a", y=nil x,y,z = foo2() -- x="a", y="b", z=nil
Listede son öğe olmayan bir fonksiyon çağrısı daima bir sonuç üretir:
x,y = foo2(), 20 -- x="a", y=20 x,y = foo0(), 20, 30 -- x=nil, y=20, 30 es geçilir
Bir fonksiyon çağrısı başka bir çağrıda son (veya tek) argüman olduğunda ilk çağrıdan gelen tüm sonuçlar argüman olarak gider. print ile zaten bu yapının örneklerini gördük:
print(foo0()) --> print(foo1()) --> a print(foo2()) --> a b print(foo2(), 1) --> a 1 print(foo2() .. "x") --> ax (şimdi bakacağız)
foo2 çağrısı bir ifade içinde göründüğünde Lua sonuçların sayısını bire ayarlar; bu nedenle son satırdaki birleştirmede(..) yalnızca “a” kullanılır.
print fonksiyonu değişen sayıda argüman alabilir. f(g()) yazar ve f'nin sabit sayıda argümanı varsa Lua daha önce gördüğümüz üzere g'nin sonuçlarının sayısını f'nin parametreleri sayısına ayarlar.
Bir oluşturucu herhangi ayar çekilmeden bir çağrıdan tüm sonuçları toplar:
t = {foo0()} -- t = {} (boş tablo) t = {foo1()} -- t = {"a"} t = {foo2()} -- t = {"a", "b"}
Her zaman ki gibi bu davranış yalnızca çağrı listede sonuncu olduğunda gerçekleşir; başka bir konumdaki çağrılar tamamen bir sonuç üretir:
t = {foo0(), foo2(), 4} -- t[1] = nil, t[2] = "a", t[3] = 4
Son olarak, return f() gibi bir deyim f tarafından döndürülen tüm değerleri döndürür:
function foo (i) if i == 0 then return foo0() elseif i == 1 then return foo1() elseif i == 2 then return foo2() end end print(foo(1)) --> a print(foo(2)) --> a b print(foo(0)) -- (sonuç yok) print(foo(3)) -- (sonuç yok)
Bir çağrıyı fazladan bir parantez çifti içine alarak tamamen bir sonuç döndürmeye zorlayabilirsiniz:
print((foo0())) --> nil print((foo1())) --> a print((foo2())) --> a
Bir return deyiminin döndürülen değer etrafında parantezlere ihtiyaç duymadığına dikkat; oraya yerleştirilen herhangi bir parantez çifti ekstra bir çift olarak hesaba katılır. Bu nedenle, return(f(x)) gibi bir deyim f kaç değer döndürürse döndürsün her zaman tek bir değer döndürür. Belki istediğin bu, belki de değil.
çoklu dönüşlü özel bir fonksiyon da unpack'dir. Bir dizi alır ve indeks 1'den başlayarak dizideki tüm öğeleri sonuç olarak döndürür:
print(unpack{10,20,30}) --> 10 20 30 a,b = unpack{10,20,30} -- a=10, b=20, 30 es geçilir
unpack için önemli bir kullanım yeri, jenerik çağrı mekanizmasıdır. Jenerik çağrı mekanizması, dinamik olarak herhangi bir argümanla herhangi bir fonksiyonu çağırmanıza imkan verir. ANSI C'de, misal, jenerik çağrı için bir yöntem yoktur. Değişen sayıda argümanlar alan fonksiyon deklare edebilirsiniz (stdarg.h ile) ve fonksiyonlara işaretçiler kullanarak değişen fonksiyon çağırabilirsiniz. Ancak, değişen sayıda argümanlı bir fonksiyonu çağıramazsınız: C 'de yazdığınız her çağrının sabit argüman sayısı vardır, ve her bir argümanın sabit bir tipi vardır. Lua'da, bir a dizisindeki değişen argümanlarla değişen f fonksiyonu çağırmak istiyorsanız, sadece şunu yazarsınız:
f(unpack(a))
unpack çağrısı f'nin argümanları haline gelen a'nın tüm değerlerini döndürür. Örneğin şunu çalıştırırsak
f = string.find a = {"hello", "ll"}
o zaman f(unpack(a)) çağrısı 3 ve 4 'ü döndürür, string.find("hello","ll") statik çağrısıyla döndürülen aynı sonuçlar.
Lua'a tümleşik bulunan unpack fonksiyonu C de yazılmış olsa da kendi kendini çağırıp yinelemeyle Lua'da da yazabiliriz:
function unpack (t, i) i = i or 1 if t[i] then return t[i], unpack(t, i + 1) end end
fonksiyonu ilk kez tek argümanla çağırdığımızda i 1 alır. sonra fonksiyon t[1]'i ve devamında unpack(t,2)'den gelen tüm sonuçları döndürür, daha sonra t[2]'i ve devamında unpack(t,3)'den gelen tüm sonuçları döndürür ve bu şekilde nil olmayan son öğeye kadar devam eder.
5.2 Değişken Argüman Sayısı
Lua da bazı fonksiyonlar değişken sayıda argüman alır. Örneğin zaten bir, iki ve daha fazla argümanla print'i çağırmıştık. print, C'de tanımlanmış olsa da Lua'da da değişken sayıda argüman kabul eden fonksiyonlar tanımlayabiliriz.
Basit bir örnek olarak aşağıdaki fonksiyon tüm argümanlarının kümülatifini döndürür:
function add (...) local s = 0 for i, v in ipairs{...} do s = s + v end return s end print(add(3, 4, 10, 25, 12)) --> 54
parametre listesindeki üç nokta(...) fonksiyonun değişen sayıda argüman kabul ettiğini gösterir. Bu fonksiyon çağrıldığında tüm argümanları içsel olarak toparlanır; bu toparlanan argümanlara fonksiyonun varargs'ı (varargs-variable arguments-değişken argümanlar) olarak adlandırıyoruz. fonksiyon artık bir ifade olarak üç noktayı kullanarak vararg'larına erişebilir. Örneğimizde {...} ifadesi toparlanan tüm argümanlarla bir dizi üretir. fonksiyon daha sonra öğelerini toplama işlemi için diziyi dolaşır.
... ifadesi mevcut fonksiyonun tüm varargs 'larını döndüren çoklu dönüşlü fonksiyon gibi davranır. Örneğin şu komut
local a, b = ...
ilk iki opsiyonel argümanın değerleriyle(veya argüman yoksa nil) iki lokal değişken yaratıyor.
function foo (a, b, c)
'den
function foo (...) local a, b, c = ...
'e dönüştürerek aslında Lua'nın normal parametre geçiş mekanizmasını taklit edebiliriz
Perl'nin parametre geçiş mekanizmasını sevenler bu ikinci forma bayılabilir.
Aşağıdaki gibi bir fonksiyon
function id (...) return ... end
basitçe çağıranına tüm parametrelerini döndürür. bu, çoklu-değer tanımlayıcı fonksiyondur.
Sonraki fonksiyon çağrıdan önce argümanlarıyla bir ileti yazdırması hariç başka bir foo fonksiyonu gibi davranır:
function foo1 (...) print("calling foo:", ...) return foo(...) end
Bu, belli bir fonksiyona olan çağrıları izlemek için yararlı bir hiledir.
Başka bir yararlı örnek görelim. Lua metni biçimlendirme için ayrı (string.format) ve metin yazmak için ayrı (io.write) fonksiyonlar sağlamıştır. Her iki fonksiyonu tek bir fonksiyonda birleştirmek kolay:
function fwrite (fmt, ...) return io.write(string.format(fmt, ...)) end
Noktalardan önce sabit bir parametre olan fmt 'nin varlığını dikkat. Vararg fonksiyonları vararg kısmından önce herhangi sayıda sabit parametreye sahip olabilir. Lua ilk argümanlarını bu parametrelere atar ve yalnızca ekstra argümanlar (varsa) varargs'a gider. Aşağıda, bazı çağrı örneklerini ve karşılık gelen parametre değerlerini görüyoruz:
ÇAĞRI PARAMETRELER
fwrite() fmt = nil, varargs yok
fwrite("a") fmt = "a", varargs yok
fwrite("%d%d", 4, 5) fmt = "%d%d", varargs = 4 ve 5
Değişken argümanları üzerinde iterasyon için add tanımlamımızda yaptığımız gibi bir fonksiyon hepsini bir tabloda toparlamak üzere {..} ifadesini kullanabilir. nadiren de olsa vararg listesi geçerli nil ler içerebileceği zaman da select fonksiyonunu kullanabiliriz. select çağrısı daima bir sabit argümana(seçici), artı değişken sayılı ekstra argümana sahiptir. Seçici n nolu ise select n'inci ekstra argümanını döndürür; Ya da seçici "#" string'i olmalıdır böylece select ekstra argümanların toplam sayısını döndürür. Aşağıdaki döngü bir fonksiyonun tüm vararg parametreleri üzerinde dolaşmak için select'i nasıl kullanabileceğimizi gösterir:
for i=1, select('#', ...) do local arg = select(i, ...) -- i'inci parametreyi al <döngü gövdesi> end
select("#",...) çağrısı nil ler dahil ekstra parametrelerin tam sayısını döndürür.
Lua 5.0 değişken sayılı argümanlar için farklı bir mekanizmaya sahipti. Bir vararg fonksiyonunun deklarasyon sözdizimi aynıydı, son parametre olarak üç noktayla. Ancak Lua 5.0 ... ifadesine sahip değildi. Bunun yerine bir vararg fonksiyonu varargs ile bir tablo alan arg adında gizli bir yerel değişkene sahipti. Bu tablo ayrıca toplam ekstra argüman sayısının olduğu n alanına sahipti. Bu eski davranışı şu şekilde simüle edebiliriz:
function foo (a, b, ...) local arg = {...}; arg.n = select("#", ...) <fonksiyon gövdesi> end
Eski mekanizmanın dezavantajı, program bir vararg fonksiyonunu her çağırdığında yeni bir tablo oluşturmasıdır. Yeni mekanizma ile yalnızca gerektiğinde varargs'ları toparlamak için tablo oluşturabiliriz.
5.3 İsimli Argümanlar
Lua'da parametre geçiş mekanizması pozisyoneldir: bir fonksiyonu çağırdığımızda argümanlar pozisyonlarıyla parametrelerle eşleştirilir. İlk argüman ilk parametreye değer verir ve böyle devam eder. Ancak bazen argümanları isimle belirtmek yararlıdır. Bunu göstermek için bir dosyayı yeniden isimlendiren os.rename(os kütüphanesinden) fonksiyonunu ele alalım. Çoğu zaman, eskisi hangisi yenisi hangisi, ilk önce hangisiydi unutuyoruz; bu nedenle bu fonksiyonu iki isimlendirilmiş argüman alması için yeniden tanımlamak isteyebiliriz:
-- geçersiz kod
rename(eskisi="temp.lua", yenisi="temp1.lua")
Lua'nın bu sözdizimi için direkt bir desteği yok ancak küçük bir sözdizimi değişikliği ile aynı nihai etkiye sahip olabiliriz. Buradaki olay, tüm argümanları bir tabloda paketlemek ve bu tabloyu fonksiyona tek argüman olarak kullanmaktır. Bu özel sözdizimi Lua 'ca argüman olarak sadece bir tablo oluşturuculu fonksiyon çağrıları için yardımcı hile olarak sağlanmıştır, :
rename{eskisi="temp.lua", yenisi="temp1.lua"}
Buna göre, yalnızca bir parametreyle rename'i tanımlarız ve bu parametreden gerçek argümanları alırız:
function rename(arg) return os.rename(arg.eskisi, arg.yenisi) end
w = Pencere{ xkonumu=0, ykonumu=0, genislik=300, yukseklik=200, baslik = "Lua", arkaplan="blue", cerceve = true }
Pencere fonksiyonu daha sonra zorunlu argümanları kontrol etme, varsayılan değerler ekleme ve benzerlerini yapma özgürlüğüne sahiptir. yeni pencereyi gerçekte oluşturan fonksiyonun ilkel _Pencere olduğunu varsayarak (ve tüm argümanlara uygun sırayla ihtiyaç duyan), Pencere'i devamdaki liste 5.1'deki gibi tanımlayabiliriz.
Liste 5.1 isimli isteğe bağlı parametrelerle bir fonksiyon
function Pencere (secenekler) -- zorunlu seçenekleri kontrol et if type(secenekler.baslik) ~= "string" then error("Baslik yok") elseif type(secenekler.genislik) ~= "number" then error("Genislik yok") elseif type(secenekler.yukseklik) ~= "number" then error("Yukseklik yok") end
-- isteğe bağlılar _Pencere(secenekler.baslik, secenekler.x or 0, -- varsayılan değer secenekler.y or 0, -- varsayılan değer secenekler.genislik, secenekler.yukseklik, secenekler.arkaplan or "white", -- varsayılan secenekler.cerceve -- varsayılan false (nil) ) end
we
YanıtlaSil