Lua'da tablolar veri yapılarından BİRİ değil; VERİ YAPISIDIR. Diğer dillerin sunduğu tüm yapılar —diziler, kayıtlar, listeler, kuyruklar, kümeler— (array, record, list, queue, set) Lua tablolarıyla temsil edilebilir. Daha da önemlisi Lua tabloları tüm bu yapıları verimli bir şekilde uygular.
C ve Pascal gibi geleneksel dillerde diziler ve listelerle (listeler = kayıtlar + işaretçiler) çoğu veri yapısını uygularız. Lua tabloları kullanarak diziler ve listeleri uygulayabilsek de (ve bazen bunu yaparız) tablolar, diziler ve listelerden daha güçlüdür; birçok algoritma tabloların kullanımı ile önemli derecede basitleşir. Örneğin Lua'da nadiren bir arama yazarız çünkü tablolar herhangi tipte direkt erişim sunar.
Tabloları verimli bir şekilde nasıl kullanılacağını öğrenmek biraz zaman alır. Burada tipik veri yapılarını tablolarla nasıl uygulayacağımızı göstereceğim ve kullanımlarına ilişkin bazı örnekler sunacağım. Diziler ve listelerle başlayacağız çünkü diğer yapılar için onlara ihtiyacımız yok ama çoğu programcı zaten onlara aşina. Dille ilgili bölümlerde bu materyalin temellerini zaten gördük ancak tamlık için burada tekrarlayacağım.
11.1 Diziler
tabloları tam sayılarla indeksleyerek Lua'da dizileri basitçe uygularız. Velhasıl, dizilerin sabit bir boyu yoktur ama ihtiyaç kadar büyür. Genellikle, diziyi ilklediğimizde boyunu dolaylı olarak tanımlarız. Örneğin aşağıdaki koddan sonra 1-1000 aralığı dışındaki bir alana erişmek için herhangi bir girişim sıfır yerine nil döndürecek:
a = {} -- yeni dizi for i = 1, 1000 do a[i] = 0 end
Bir dizinin boyunu bulmak için uzunluk operatörünün ('#') şu imkanı kullanılır:
print(#a) --> 1000
bir dizi indeksini 0, 1 veya başka herhangi bir değerle başlatabilirsiniz:
-- -5den 5'e indeksli bir dizi yarat a = {} for i = -5, 5 do a[i] = 0 end
Bununla birlikte Lua'da dizileri 1 indeksiyle başlatmak gelenekseldir. Lua kütüphaneleri bu düzene uymaktadır; uzunluk operatörü de öyle. Dizileriniz 1 ile başlamazsa bu imkanları kullanamazsınız.
Tek bir ifadede dizileri yaratmak ve ilklemek için bir oluşturucu kullanabiliriz:
tamKare = {1, 4, 9, 16, 25, 36, 49, 64, 81}
Bu tür kurucular ihtiyacınız olduğu kadar büyük olabilir (birkaç milyon öğeye kadar).
11.2 Matrisler ve çok boyutlu diziler
Lua'da matrisleri temsil etmenin iki ana yöntemi vardır. Birincisi, bir dizilerin dizisi yani her öğenin başka bir tablo olduğu bir tablo kullanmak. Örneğin aşağıdaki kodla M N boyutlu sıfır matrisi oluşturabilirsiniz:
mt = {} -- matris yarat for i = 1, N do mt[i] = {} -- yeni bir satır yarat for j = 1, M do mt[i][j] = 0 end end
Tablolar Lua'da nesneler olduğundan, bir matris oluşturmak için açıkça her satırı oluşturmanız gerekir. Bir taraftan, bu, C veya Pascal'da yaptığınız gibi sadece bir matris deklare edilmesinden kesinlikle daha ayrıntılı. Öteki taraftan, size daha fazla esneklik sağlar. Örneğin, önceki örnekteki for j=1,i do...end 'i , for j=1,M do...end döngüsüne çevirerek üçgen matris oluşturabilirsiniz. Bu kodla Üçgen matris orjinalinin sadece yarısı kadar bellek kullanır.
Lua'da bir matrisi temsil etmenin ikinci yöntemi, iki indeksi tek bir indeksle oluşturmaktır. İki indeks tamsayı ise, birincisini uygun bir sabitle çarpabilir ve ikinci indekse ekleme yapabilirsiniz. Bu yaklaşımla, aşağıdaki kod sıfır matrisimizi M N boyutlarına göre oluşturacaktır:
mt = {} -- matris yarat for i = 1, N do for j = 1, M do mt[(i - 1) * M + j] = 0 end end
indeksler string ise, onları ayırmak için aralarında bir karakterle her iki indeksi birleştiren tek bir indeks oluşturabilirsiniz. Örneğin, m[s..":"..t] kodu ile t ve s string indeksleri ile bir m matrisini indeksleyebilirsiniz, hem s hem de t'nin : içermemesi koşuluyla; aksi takdirde, (“a:”,“b”) ve (“a”,“:b”) gibi çiftleri tek bir indekse getirme “a::b” suya düşer. Şüpheniz olduğunda, indeksleri ayırmak için ‘\0’ gibi bir kontrol karakteri kullanabilirsiniz.
Çoğu zaman, uygulamalar bir matris olarak çoğu öğesi 0 veya nil olan seyrek(sparse) matris kullanır. Örneğin, bir graf'ı(graph) komşu(adjacency) matris ile temsil edebilirsiniz, m ve n düğümlerinin cost x ile ilişkilendirildiğinde; bu düğümler bağlı olmadığında, m, n konumdaki değer nildir. Her düğümün yaklaşık beş komşusu olan on bin düğüm içeren bir graf'ı temsil etmek için, yüz milyon girişi olan bir matrise (10000 sütunlu ve 10000 satırlı bir kare matris ) ihtiyacınız olacak, ancak yaklaşık elli bin tanesi nil olmayacak (her bir düğümün beş komşusuna karşılık gelen her satır için beş nil olmayan sütun). Veri yapılarına ilişkin birçok kitap, 400 Megabayt belleği boşa harcamadan bu tür seyrek matrislerin nasıl uygulanacağını uzun uzuna tartışır, ancak Lua'da programlama yaparken bu tekniklere ihtiyacınız yoktur. Diziler tablolar ile temsil edildiğinden, doğal olarak seyrektir. İlk temsilimiz (tabloların tabloları) ile, her biri yaklaşık beş elemana sahip on bin tabloya, toplamda elli bin girdiye ihtiyacınız olacak. İkinci temsil ile, içinde elli bin giriş bulunan tek bir tabloya sahip olacaksınız. Temsil ne olursa olsun, yalnızca nil olmayan öğeler için alana ihtiyacınız var.
Aktif girişler arasındaki delikler(nil değerleri) nedeniyle, uzunluk operatörünü seyrek matrisler üzerinde kullanamayız. Bu büyük bir kayıp değil; yapabilsek bile yapmamalıyız. Çoğu işlem için, tüm bu boş girişleri dolaşmak oldukça verimsiz olurdu. Bunun yerine, yalnızca nil olmayan öğeleri dolaşmak için pairs'i kullanabiliriz. Örneğin, bir satırı bir sabitle çarpmak için aşağıdaki kodu kullanabiliriz:
function mult(a, rowindex, k) local row = a[rowindex] for i, v in pairs(row) do row[i] = v * k end end
Tabi, anahtarların tabloda herhangi içsel sıralamaya sahip olmadığına dikkat, bu nedenle pairs'le iterasyon, sütunları artan düzende ziyaret etmemizi sağlamaz. Bazı görevler için (önceki örneğimiz gibi), bu bir problem değil. Diğer görevler için, alternatif bir yaklaşıma ihtiyacınız olabilir, misal bağlı listeler .
11.3 Bağlı Listeler
Tablolar dinamik öğeler olduğundan, Lua'da bağlı listelerin uygulanması kolaydır. Her düğüm bir tabloyla temsil edilir ve bağlantılar diğer tablolara referanslar içeren tablo alanlarıdır. Örneğin, her düğümün iki alana, sonraki ve deger'e sahip olduğu temel bir listeyi uygulamak için, liste kökü olarak bir değişken oluştururuz:
list = nil
Listenin başına bir öğe eklemek için, deger v ile, şunu yaparız:
list = {sonraki = list, deger = v}
Listeyi dolaşmak için şunu yazarız:
local l = list while l do <l.deger ziyareti> l = l.sonraki end
Çift bağlı listeler veya çember listeler gibi diğer listeler de kolayca uygulanır. Tabi, nadiren Lua'da bu yapılara gereksinirsiniz çünkü genellikle bağlı listeleri kullanmadan verilerinizi temsil etmenin daha basit yolu var. Örneğin, (sınırsız) bir diziyle bir yığını(stack) temsil edebiliriz
11.4 Kuyruklar ve Çift Kuyruklar
Lua 'da kuyrukları uygulamanın basit yolu insert ve remove fonksiyonlarıyladır (table kütüphanesinden) . Bu fonksiyonlar, bir dizinin herhangi bir pozisyonuna öğe ekler ve çıkarır, bu işleme mütakip diğer öğeleri taşır. Bununla birlikte, bu taşıma büyük yapılar için masraflı olabilir.
Daha verimli bir uygulama, biri ilk öğe için, diğeri sonuncu için, iki indeks kullanmaktır:
function ListNew() return {first = 0, last = -1} end
genel alanı kirletmekten kaçınmak için, List olarak adlandırılan bir tablonun içinde tüm liste işlemlerini tanımlayacağız (yani, bir modül oluşturacağız). Bu nedenle, son örneğimizi şu şekilde yeniden yazıyoruz:
List = {} function List.new() return {first = 0, last = -1} end
Artık, vefalı bir sürede her iki uca da bir eleman yerleştirebilir veya kaldırabiliriz:
function List.pushfirst(list, value) local first = list.first - 1 list.first = first list[first] = value end function List.pushlast(list, value) local last = list.last + 1 list.last = last list[last] = value end function List.popfirst(list) local first = list.first if first > list.last then error("liste boş") end local value = list[first] list[first] = nil -- çöp toplamaya imkan vermek için list.first = first + 1 return value end function List.poplast(list) local last = list.last if list.first > last then error("liste boş") end local value = list[last] list[last] = nil -- çöp toplamaya imkan vermek için list.last = last - 1 return value end
Bu yapıyı sıkı bir kuyruk disiplininde kullanırsanız, yalnızca pushlast ve popfirst çağırarak, hem first hem de last sürekli artacak. Ancak, Lua'da dizileri tablolarla temsil ettiğimizden bunları 1 ila 20 veya 16777216 ile 16777236 arasında indeksleyebilirsiniz. Lua sayıları temsil etmek için çift hassasiyet kullandığından programınız iki yüz yıl çalışabilir ve taşmalarla ilgili sorunlar yaşamadan saniyede bir milyon ekleme yapabilir.
11.5 Kümeler ve Torbalar(Bag)
Bir programın kaynak kodunda kullanılan tüm tanımlayıcıları listelemek istediğinizi varsayalım;
bir şekilde reserve kelimeleri listenizden filtrelemeniz gerekiyor. Bazı C programcılarına, reserve sözcük kümesini bir stringler dizisi olarak temsil etmek ve daha sonra belli bir kelimenin kümede olup olmadığını öğrenmek için bu diziyi aramak cazip gelebilir. Aramayı hızlandırmak için kümeyi temsil etmek üzere ikili ağaç(binary tree) bile kullanabilirler.
Lua'da bu tür kümeleri temsil etmenin verimli ve basit yolu, bir tabloya indeksler olarak kümenin öğelerini koymaktır. Ardından belirli bir öğe için tabloyu aramak yerine sadece tabloyu indeksleyip sonucun nil olup olmadığını test edersiniz. Örneğimizde, sıradaki kodu şöyle yazabiliriz:
reserved = { ["while"] = true, ["end"] = true, ["function"] = true, ["local"] = true, } for w in allwords() do if not reserved[w] then <'w' ile birseyler yap> -- ’w’ reserve bir kelime değil end end
(Bu kelimeler Lua'da reserve olduğu için tanımlayıcılar olarak kullanamazsınız; örneğin, while=true yazamayız. Bunun yerine, ["while"]=true notasyonunu kullanıyoruz.)
kümeyi oluşturmak için yardımcı bir fonksiyon kullanarak daha net bir ilkleme olabilir:
function Set(list) local set = {} for _, l in ipairs(list) do set[l] = true end return set end
reserved = Set{"while", "end", "function", "local", }
Multiset(çoklu küme) olarak da adlandırılan torbalar, her bir öğenin birden çok kez görünebileceği normal kümelerden farklılığıdır. Lua'da torbalar için kolay temsil, kümeler için önceki temsile benzer, ama her bir anahtar ile bir sayaç ilişkilendiririz. Bir öğe eklemek için sayacını arttırıyoruz:
function insert(bag, element) bag[element] = (bag[element] or 0) + 1 end
Bir öğeyi kaldırmak için sayacını azaltıyoruz:
function remove(bag, element) local count = bag[element] bag[element] = (count and count > 1) and count - 1 or nil end
hali hazırda var ve hala sıfırdan büyükse sayacı olduğu gibi tutuyoruz.
11.6 String Tamponları
Parça parça bir string oluşturduğunuzu varsayalım, örneğin satır satır bir dosya okuyorsunuz. Tipik kodunuz şöyle görünür:
local buff = "" for line in io.lines() do buff = buff .. line .. "\n" end
Masum görünümüne rağmen, Lua'daki bu kod büyük dosyalar için büyük bir performans sıkıntısına neden olabilir: örneğin, 350 Kilobaytlık bir dosyayı okumak neredeyse bir dakika sürer.
Niye? Olanı anlamak için, okuma döngüsünün ortasında olduğumuzu varsayalım; her satır 20 bayt ve hazırda 2500 satırlık okuma yaptın, bu yüzden buff 50 kilobaytlık bir string oldu. Lua, buff..line.."\n" birleştirmesini yaptığında 50020 baytlık yeni bir string oluşturur ve buff'den 50000 bayt bu yeni stringe kopyalar. Yani, her yeni satır için Lua 50 kilobaytlık belleğe ulaşır, ve büyür de büyür. 100 yeni satır okuduktan sonra (sadece 2 kilobaytlık), Lua hazırda 5 megabayt bellekten fazlasına ulaşmış. Daha da önemlisi, algoritma kuadratik. Lua 350 kilobaytlık okumayı bitirdiğinde 50 gigabayt'dan fazlasına ulaşmış olur.
Bu sorun Lua'a özgü değil: stringlerin değişmez değerli olduğu diğer diller de, Java en ünlü örnek, benzer bir davranış sunar.
Devam etmeden önce, söylediğim her şeye rağmen, bu durumun genel bir sorun olmadığını belirtmeliyiz. Küçük stringler için yukarıdaki döngü iyidir. Tüm dosyayı okumak için, Lua, bikerede dosyayı okuyan, io.read("*all") seçeneğini sağlar. Bununla birlikte, bazen bu problemle yüzleşmeliyiz. Java, sorunu iyileştirmek için StringBuffer yapısını sunar. Lua'da, string tamponu olarak tablo kullanabiliriz. Bu yaklaşımın anahtarı, verilen bir listenin tüm stringlerini birleştirimişini döndüren , table.concat fonksiyonudur. concat'ı kullanarak aşağıdaki gibi önceki döngüyü yazabilirsiniz:
local t = {} for line in io.lines() do t[#t + 1] = line .. "\n" end local s = table.concat(t)
Bu algoritma, orijinal kodla okuması neredeyse bir dakika süren dosya için 0,5 saniyeden daha az sürede okuma yapar. (Tabii ki, bütün bir dosyayı okumak için “*all” seçeneğiyle io.read'i kullanmak daha iyi.)
Daha iyisini yapabiliriz. concat fonksiyonu, stringler arasına eklenecek bir ayırıcı olacak isteğe bağlı bir ikinci argüman kabul eder. Bu ayırıcıyla, her satırdan sonra "\n" eklememiz gerekmez:
local t = {} for line in io.lines() do t[#t + 1] = line end s = table.concat(t, "\n") .. "\n"
concat fonksiyonu stringler arasına ayırıcı ekler ancak yine de son "\n" eklememiz gerekir.
Bu son birleştirme, oldukça uzun olabilen nihai stringi çoğaltır. Bu ekstra ayırıcıyı concat ile eklemek için bir seçenek yok ancak onu aldatabiliriz, t'e ekstra boş bir string ekleyebiliriz:
t[#t + 1] = "" s = table.concat(t, "\n")
İstediğimiz gibi, nihai stringinin sonundaki bu boş stringden önce concat ekstra "\n" ekler.
İçsel olarak, hem concat hem de io.read("*all") birçok küçük stringi birleştirmek için aynı algoritmayı kullanır. Standart kütüphanelerden diğer bazı fonksiyonlar da bu algoritmayı büyük stringler oluşturmak için kullanır. Nasıl çalıştığına bir göz atalım.
Orjinal döngümüz, sorunu çözmek için bir depo içinde tek tek küçük stringleri birleştirdiği, doğrusal bir yaklaşım almıştı. Bu yeni algoritma bir ikili yaklaşımı kullanarak sıkıntıları önler. içlerindeki küçük stringleri birleştirir ve ortaya çıkan büyük stringleri diğer büyüklerle birleştirir. Algoritmanın kalbi olan yığın, alt kısmında hazırda oluşturulmuş büyük stringleri tutar ; küçük stringler üstten girer. Bu yığının ana değişmezi, popüler(programcılar arasında en azından) Hanoi kulesine benzer:
yığındaki bir string asla daha kısa bir string üzerinde bulunamaz. Yeni bir string daha kısa bir string üzerine konulduğunda, algoritma ikisini birleştirir(sadece o zaman). Bu birleştirme, artık, önceki kattaki komşusundan daha büyük olabilecek daha büyük bir string oluşturur. Bu olursa, onlar da birleştirilir. Bu birleştirmeler, döngü daha büyük bir stringe veya yığının alt kısmına ulaşıncaya kadar yığının altına doğru iner.
function addString(stack, s) stack[#stack + 1] = s -- yığına ’s’ 'i koy for i = #stack - 1, 1, -1 do if #stack[i] > #stack[i + 1] then break end stack[i] = stack[i] .. stack[i + 1] stack[i + 1] = nil end end
Tamponun nihai içeriğini elde etmek için tüm stringleri aşağıya doğru birleştiriyoruz.
11.7 Graph
Herhangi bir makul dil gibi, Lua graf'lar için, her biri belli algoritmalara daha iyi adapte olan, birden fazla uyarlamaya imkan verir. Burada, düğümleri nesneler (aslında tablolar elbette) ve düğümler arasında referanslar olarak yayları temsil ettiğimiz basit bir nesne yönelimli uyarlama göreceğiz.
Liste 11.1. bir dosyadan graf okuma:
function readgraph() local graph = {} for line in io.lines() do -- satırı iki isme böl local namefrom, nameto = string.match(line, "(%S+)%s+(%S+)") -- uyan düğümleri bul local from = name2node(graph, namefrom) local to = name2node(graph, nameto) -- ’from’ 'nın bitişik kümesine 'to' 'u ekle from.adj[to] = true end return graph end
Her bir düğümü iki alana sahip bir tablo olarak temsil edeceğiz: name, düğümün adı; ve adj, buna bitişik düğümler kümesi. Bir metin dosyasından graf'ı okuyacağımız için, adı verilen bir düğümü bulmak için bir yola ihtiyacımız var. Mamafi, düğümlerle isimleri eşlemek için ekstra bir tablo kullanacağız. verilen bir isimle(name), name2node fonksiyonu karşılık gelen düğümü döndürür:
local function name2node(graph, name) if not graph[name] then -- düğüm yok; yeni bir tane yarat graph[name] = {name = name, adj = {}} end return graph[name] end
Liste 11.1 bir graf oluşturan fonksiyonu gösterir. bir dosya okur, her satır , ilk düğümden ikincisine bir yay olduğu anlamına gelen iki düğüm adına sahiptir. Her satır için, satırı iki ada bölmek için string.match kullanır, bu adlara karşılık gelen düğümleri bulur (gerekirse düğümleri oluşturur) ve düğümleri bağlar.
Liste 11.2 bu tür graf'ı kullanan bir algoritma göstermektedir. findpath fonksiyonu, Depth-first search (DFS - derin arama) kullanarak iki düğüm arasında bir yol arar. İlk parametresi geçerli düğümdür; ikincisi hedeftir; üçüncü parametre orijinden geçerli düğüme yolu tutar; son parametre, hazırda ziyaret edilen (döngülerden kaçınmak için) tüm düğümlerin bir kümesidir. Algoritmanın, adlarını kullanmadan düğümleri doğrudan nasıl manipüle ettiğine dikkat. Örneğin, visited , düğümlerin kümesi, düğümlerin adları değil. Benzer şekilde, path düğümlerin listesidir.
Liste 11.2. iki node arasında yolu bulma:
function findpath(curr, to, path, visited) path = path or {} visited = visited or {} if visited[curr] then -- hazırda düğüm ziyaret edildi mi? return nil -- burada yol yok end visited[curr] = true -- düğümü ziyaret edildi olarak işaretle path[#path + 1] = curr -- yola onu ekle if curr == to then -- final düğüm? return path end -- tüm bitişik düğümleri dene for node in pairs(curr.adj) do local p = findpath(node, to, path, visited) if p then return p end end path[#path] = nil -- yoldan düğümü çıkar end
Bu kodu test etmek üzere, path'i(yolu) yazdıracak bir fonksiyon ekliyor, tüm çalışmaları bir araya getiriyoruz:
function printpath(path) for i = 1, #path do print(path[i].name) end end g = readgraph() a = name2node(g, "a") b = name2node(g, "b") p = findpath(a, b) if p then printpath(p) end
Selam.. konu hala aktifmi bilmiyorum, zira bir sorum var;
YanıtlaSilAşağıdaki kodu, küçükten büyüğe doğru nasıl sıralayabilirim?
Kod:
local pn1=[[item_aaaaaaaaaaa
item_bbbbbbbbbbbbbbbbbbbbbbbb
item_ccccccccccccccccc
item_aaaaaaaaaaaaaa
item_bbbbbbbbbbbbbbbbbbbbbbbbbb
item_cccccccccccccccccccc
item_aaaaaaa
item_bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
item_ccccccccccccccc
item_aaaaaaaaaaaaaaaaa
item_bbbbbbbbbbbbbbbbbbbbb
item_ccccccccccccccccc
item_aaaaaaaaaaaaaaaaaaaaa
item_bbbbbbbbbbbbbbbbbbbbbbbb
item_ccccccccccccccccccccccccccc
item_aaaaaaaaa]]
function filter()
local SL = createStringlist()
SL.Text=pn1
for i=0, strings_getCount(SL)-1 do
local item1 = SL[i]
local item2 = SL[i]:sub(1, 6)
--print(item2)
local itemLeng = # (item1)
if item2=="item_a" then
print(itemLeng.." - "..item1)
end
end
SL.destroy()
end
filter()
sonuç;
16 - item_aaaaaaaaaaa
19 - item_aaaaaaaaaaaaaa
12 - item_aaaaaaa
22 - item_aaaaaaaaaaaaaaaaa
26 - item_aaaaaaaaaaaaaaaaaaaaa
14 - item_aaaaaaaaa
bana gereken;
12 - item_aaaaaaa
14 - item_aaaaaaaaa
16 - item_aaaaaaaaaaa
19 - item_aaaaaaaaaaaaaa
22 - item_aaaaaaaaaaaaaaaaa
26 - item_aaaaaaaaaaaaaaaaaaaaa
cevap için şimdiden teşekkürler.
table.sort(pn1, function(a, b) return #a < #b end)
YanıtlaSilpn1 isimli anahtar-değer tipli bir tablon olduğunu , değerlerinde string tipinde olduğunu ve stringlerin uzunluğu bazlı sıralama yapmak istediğini varsaydım.
Sil