Hlavní navigace

LuaJIT – Just in Time překladač pro programovací jazyk Lua (5 – tabulky a pole)

11. 11. 2014
Doba čtení: 21 minut

Sdílet

V páté části článku o Just in Time překladači nazvaném LuaJIT se budeme zabývat způsobem překladu programů pracujících s tabulkami. Ukážeme si způsob tvorby jednorozměrných i dvourozměrných tabulek a taktéž přístup k prvkům tabulek, a to jak s využitím celočíselných indexů, tak i s použitím klíčů.

Obsah

1. LuaJIT – Just in Time překladač pro programovací jazyk Lua (5 – tabulky a pole)

2. Demonstrační příklad test24.lua – vytvoření prázdné tabulky, zjištění počtu prvků tabulky, přístup k prvkům tabulky

  2.1 Zdrojový kód příkladu test24.lua

  2.2 Překlad příkladu test24.lua do mezijazyka LuaJITu

3. Demonstrační příklad test25.lua – vytvoření a inicializace neprázdné tabulky

  3.1 Zdrojový kód příkladu test25.lua

  3.2 Překlad příkladu test25.lua do mezijazyka LuaJITu

4. Demonstrační příklad test26.lua – přidání prvků do tabulky s využitím (konstantních) indexů

  4.1 Zdrojový kód příkladu test26.lua

  4.2 Překlad příkladu test26.lua do mezijazyka LuaJITu

5. Demonstrační příklad test27.lua – přidání prvků do tabulky s využitím (konstantních) klíčů

  5.1 Zdrojový kód příkladu test27.lua

  5.2 Překlad příkladu test27.lua do mezijazyka LuaJITu

6. Demonstrační příklad test28.lua – přidání prvků do tabulky s využitím proměnných namísto konstantních indexů či klíčů

  6.1 Zdrojový kód příkladu test28.lua

  6.2 Překlad příkladu test28.lua do mezijazyka LuaJITu

7. Demonstrační příklad test29.lua – práce s dvourozměrnými tabulkami (maticemi)

  7.1 Zdrojový kód příkladu test29.lua

  7.2 Překlad příkladu test29.lua do mezijazyka LuaJITu

8. Repositář se zdrojovými kódy všech šesti dnešních demonstračních příkladů

9. Odkazy na Internetu

1. LuaJIT – Just in Time překladač pro programovací jazyk Lua (5 – tabulky a pole)

V programovacím jazyce Lua jsou nejdůležitějším strukturovaným datovým typem tabulky (table). Tento datový typ lze použít jak pro implementaci pole (array) s prvky adresovanými s využitím celočíselných indexů, tak i pro implementaci asociativního pole, kdy jsou namísto celočíselných indexů použity klíče, které jsou typicky reprezentovány řetězci (není to však nutné). V jazyku Lua je interní struktura tabulek implementována takovým způsobem, aby byla práce s tabulkami efektivní jak při použití celočíselných indexů, tak i při použití klíčů. Není tedy divu, že jsou tabulky použity v prakticky všech aplikacích a navíc i samotná Lua tabulky interně poměrně často využívá – příkladem může být například známá globální tabulka _G. Potřeba práce s tabulkami je samozřejmě reflektována i v mezijazyku (IR) LuaJITu. Všechny instrukce, které nějakým způsobem s tabulkami pracují, lze rozdělit do čtyř skupin:

# Funkce Seznam instrukcí
1 vytvoření nové tabulky TNEW, TDUP
2 práce s globální tabulkou _G GGET, GSET
3 čtení prvků z tabulky TGETV, TGETS, TGETB
4 zápis prvku do tabulky TSETV, TSETS, TSETB, TSETM

V tabulce zobrazené pod tímto odstavcem jsou všechny základní instrukce IR, které se týkají práce s tabulkami, vypsány společně s podrobnějším popisem i s použitými operandy:

# Instrukce Operandy (A,B,C/D) Popis
1 TNEW dst, lit vytvoření nové tabulky o velikosti specifikované v D (lit), reference na vytvořenou tabulku se uloží do A (dst)
2 TDUP dst, tab vytvoření nové tabulky na základě šablony specifikované v D (tab), reference na vytvořenou tabulku se uloží do A (dst)
       
3 GGET dst, str přečtení prvku z globální tabulky _G, v D (str) je uložen klíč (řetězec)
4 GSET var, str zápis prvku do globální tabulky _G, v D (str) je uložen klíč (řetězec)
       
5 TGETV dst, var, var čtení prvku z tabulky specifikované v B, klíč je uložen v proměnné
6 TGETS dst, var, str čtení prvku z tabulky specifikované v B, klíčem je řetězec
7 TGETB dst, var, lit čtení prvku z tabulky specifikované v B, klíčem je literál (konstanta, typicky celé číslo)
       
8 TSETV var, var, var zápis prvku do tabulky specifikované v B, klíč je uložen v proměnné
9 TSETS var, var, str zápis prvku do tabulky specifikované v B, klíčem je řetězec
10 TSETB var, var, lit zápis prvku do tabulky specifikované v B, klíčem je literál (konstanta, typicky celé číslo)
       
11 TSETM base, num* nastavení většího množství prvků dle vztahu: (A-1)[D], (A-1)[D+1], … = A, A+1, …

S instrukcemi GGETGSET jsme se již v několika demonstračních příkladech setkali, protože se s využitím těchto instrukcí mj. pracuje i s „globálními“ funkcemi, tj. funkcemi, které nejsou anonymní a nejsou uloženy do uživatelsky definované tabulky. Většinu dalších instrukcí s výjimkou poněkud speciální instrukce TSETM si popíšeme v navazujících kapitolách.

2. Demonstrační příklad test24.lua – vytvoření prázdné tabulky, zjištění počtu prvků tabulky, přístup k prvkům tabulky

V dnešním prvním demonstračním příkladu nazvaném test24.lua jsou ukázány čtyři základní jazykové konstrukce podporované programovacím jazykem Lua, které se používají při práci s tabulkami. Jedná se o konstrukci pro vytvoření prázdné tabulky (jednorozměrného pole – vektoru), dále o tisk informace o celé tabulce (vytiskne se hodnota reference), tisk počtu prvků v tabulce (platí jen pro klasická pole, nikoli pro asociativní pole) a samozřejmě též i o konstrukci sloužící pro získání hodnoty konkrétního prvku v tabulce na základě jeho indexu. Připomeňme si, že v Lue se prvky číslují od jedničky a nikoli od nuly:

2.1 Zdrojový kód příkladu test24.lua

--
-- LuaJIT: demonstrační příklad číslo 24
--
-- Práce s tabulkami:
--    * vytvoření prázdné tabulky
--    * tisk poctu prvku tabulky
--    * přístup k prvkům tabulky
--
 
 
 
-- vytvoření prázdné tabulky
local tbl = {}
 
-- tisk informace o tabulce (reference...)
print(tbl)
 
-- tisk poctu prvku v tabulce
print(#tbl)
 
-- tisk hodnoty prvního prvku v tabulce
print(tbl[1])
 
-- tisk hodnoty desátého prvku v tabulce
print(tbl[10])
 
 
 
-- finito

2.2 Překlad příkladu test24.lua do mezijazyka LuaJITu

Při pohledu na IR získaný překladem demonstračního příkladu test24.lua do mezijazyka LuaJITu můžeme získat základní představu o tom, jak se v IR s tabulkami pracuje. Pro vytvoření prázdné tabulky se používá instrukce TNEW, které se předají rozměry tabulky a index slotu, do kterého se uloží reference na vytvořenou tabulku. Pro výpočet počtu prvků tabulky se používá instrukce LEN a pro přečtení hodnoty konkrétního prvku uloženého na určitém indexu lze použít instrukci TGETB. Posledním operandem této instrukce je index čteného prvku:

; Překlad demonstračního příkladu test24.lua
; do IR využívaného virtuálním strojem a JIT
; překladačem LuaJIT.
 
-- BYTECODE -- test24.lua:0-31
 
; vytvoření prázdné tabulky
; local tbl = {}
0001    TNEW     0   0        ; vytvoření nové prázdné tabulky
 
; tisk informace o tabulce (reference...)
; print(tbl)
0002    GGET     1   0        ; získání reference na funkci se jménem "print"
0003    MOV      2   0        ; bude se tisknout reference na tabulku
0004    CALL     1   1   2    ; volání funkce print()
 
; tisk počtu prvků v tabulce
; print(#tbl)
0005    GGET     1   0        ; získání reference na funkci se jménem "print"
0006    LEN      2   0        ; získání počtu prvků tabulky (resp. pole, které je součástí tabulky)
0007    CALL     1   1   2    ; volání funkce print()
 
; tisk hodnoty prvního prvku v tabulce
; print(tbl[1])
0008    GGET     1   0        ; získání reference na funkci se jménem "print"
0009    TGETB    2   0   1    ; přečtení hodnoty prvního prvku tabulky
0010    CALL     1   1   2    ; volání funkce print()
 
; tisk hodnoty desátého prvku v tabulce
; print(tbl[10])
0011    GGET     1   0        ; získání reference na funkci se jménem "print"
0012    TGETB    2   0  10    ; přečtení hodnoty desátého prvku tabulky
0013    CALL     1   1   2    ; volání funkce print()
 
; každý program je automaticky ukončen následující instrukcí
0014    RET0     0   1
 
; konec

3. Demonstrační příklad test25.lua – vytvoření a inicializace neprázdné tabulky

Kromě vytvoření prázdné tabulky je možné v programovacím jazyku Lua vytvořit novou tabulku a současně provést i inicializaci jejích prvků. Pro tento účel se v nejjednodušším případě používá konstrukce local tbl = {hodnota1, hodnota2, hodnota3, …}. Vzhledem k tomu, že se jedná o velmi často používanou jazykovou konstrukci, lze očekávat, že bude nějakým způsobem podporována i v IR LuaJITu, takže nezbývá, než si vše otestovat:

3.1 Zdrojový kód příkladu test25.lua

--
-- LuaJIT: demonstrační příklad číslo 25
--
-- Práce s tabulkami:
--    * vytvoření a inicializace neprázdné tabulky
--    * tisk poctu prvku tabulky
--    * přístup k prvkům tabulky
--
 
 
 
-- vytvoření desetiprvkové tabulky
local tbl = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}
 
-- tisk informace o tabulce (reference...)
print(tbl)
 
-- tisk poctu prvku v tabulce
print(#tbl)
 
-- tisk hodnoty prvního prvku v tabulce
print(tbl[1])
 
-- tisk hodnoty desátého prvku v tabulce
print(tbl[10])
 
 
 
-- finito

3.2 Překlad příkladu test25.lua do mezijazyka LuaJITu

Pohled na IR demonstračního příkladu test25.lua přeloženého do mezijazyka LuaJITu nám ukáže, že se IR prakticky nijak zásadně neliší od IR předchozího příkladu nazvaného test24.lua. Jediným rozdílem je zde použití instrukce TDUP namísto instrukce TNEW. Zatímco TNEW slouží pro vytvoření prázdné tabulky zadané velikosti, je TDUP interně mnohem komplikovanější, protože vytvoří a inicializuje novou tabulku na základě zadané šablony, která je uložena v obdobě constant poolu. Díky existenci této instrukce je tedy možné IR značným způsobem zkrátit, především v porovnání s některými populárními bajtkódy, které mnohdy inicializaci tabulek neřeší optimálním způsobem:

; Překlad demonstračního příkladu test25.lua
; do IR využívaného virtuálním strojem a JIT
; překladačem LuaJIT.
 
-- BYTECODE -- test25.lua:0-31
 
; vytvoření a inicializace desetiprvkové tabulky
; local tbl = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}
0001    TDUP     0   0        ; pozor - rozdíl oproti předchozímu příkladu
 
; tisk informace o tabulce (reference...)
; print(tbl)
0002    GGET     1   1        ; získání reference na funkci se jménem "print"
0003    MOV      2   0        ; bude se tisknout reference na tabulku
0004    CALL     1   1   2    ; volání funkce print()
 
; tisk počtu prvků v tabulce
; print(#tbl)
0005    GGET     1   1        ; získání reference na funkci se jménem "print"
0006    LEN      2   0        ; získání počtu prvků tabulky (resp. pole, které je součástí tabulky)
0007    CALL     1   1   2    ; volání funkce print()
 
; tisk hodnoty prvního prvku v tabulce
; print(tbl[1])
0008    GGET     1   1        ; získání reference na funkci se jménem "print"
0009    TGETB    2   0   1    ; přečtení hodnoty prvního prvku tabulky
0010    CALL     1   1   2    ; volání funkce print()
 
; tisk hodnoty desátého prvku v tabulce
; print(tbl[10])
0011    GGET     1   1        ; získání reference na funkci se jménem "print"
0012    TGETB    2   0  10    ; přečtení hodnoty desátého prvku tabulky
0013    CALL     1   1   2    ; volání funkce print()
 
; každý program je automaticky ukončen následující instrukcí
0014    RET0     0   1
 
; konec

4. Demonstrační příklad test26.lua – přidání prvků do tabulky s využitím (konstantních) indexů

V dnešním třetím demonstračním příkladu si vyzkoušíme, jakým způsobem se v mezijazyku LuaJITu řeší situace, kdy je nutné změnit či nastavit hodnotu prvku uloženého na určitém indexu. Opět se jedná o velmi často používanou jazykovou konstrukci, přičemž se velmi často setkáme s použitím relativně malých tabulek s indexy prvků od jedničky do 256. A právě k prvkům takto malých tabulek lze přistupovat velmi efektivním způsobem, což bude patrné z výpisu IR:

4.1 Zdrojový kód příkladu test26.lua

--
-- LuaJIT: demonstrační příklad číslo 26
--
-- Práce s tabulkami:
--    * vytvoření a inicializace prázdné tabulky
--    * přidání prvku do tabulky s použitím indexu
--    * tisk poctu prvku tabulky
--    * přístup k prvkům tabulky
--
 
 
 
-- vytvoření prázdné tabulky
local tbl = {}
 
-- nastavit hodnotu prvního prvku tabulky
tbl[1] = 777
 
-- nastavit hodnotu druhého prvku tabulky
tbl[2] = 999
 
-- nastavit hodnotu desátého prvku tabulky
tbl[10] = 1000
 
-- tisk informace o tabulce (reference...)
print(tbl)
 
-- tisk poctu prvku v tabulce
print(#tbl)
 
-- tisk hodnoty prvního prvku v tabulce
print(tbl[1])
 
-- tisk hodnoty druhého prvku v tabulce
print(tbl[2])
 
-- tisk hodnoty desátého prvku v tabulce
print(tbl[10])
 
 
 
-- finito

4.2 Překlad příkladu test26.lua do mezijazyka LuaJITu

Po překladu demonstračního příkladu test26.lua do mezijazyka LuaJITu je patrné, jakým způsobem se zápis prvků do tabulky přeložil. Můžeme zde vidět dvojici instrukcí KSHORT+TSETB. První z těchto instrukcí uloží celočíselnou konstantu do vybraného slotu, druhá instrukce tuto konstantu zapíše do tabulky na určený index. Povšimněte si toho, že poslední operand instrukce TSETB přímo obsahuje index zapisovaného prvku, druhý operand obsahuje referenci na tabulku a operand první pak index slotu, v němž je uložena zapisovaná hodnota:

; Překlad demonstračního příkladu test26.lua
; do IR využívaného virtuálním strojem a JIT
; překladačem LuaJIT.
 
-- BYTECODE -- test26.lua:0-44
 
; vytvoření prázdné tabulky
; local tbl = {}
0001    TNEW     0   0
 
; nastavit hodnotu prvního prvku tabulky
; tbl[1] = 777
0002    KSHORT   1 777        ; ukládaná konstanta
0003    TSETB    1   0   1    ; nastavení prvního prvku tabulky
 
; nastavit hodnotu druhého prvku tabulky
; tbl[2] = 999
0004    KSHORT   1 999        ; ukládaná konstanta
0005    TSETB    1   0   2    ; nastavení druhého prvku tabulky
 
; nastavit hodnotu desátého prvku tabulky
; tbl[10] = 1000
0006    KSHORT   1 1000       ; ukládaná konstanta
0007    TSETB    1   0  10    ; nastavení desátého prvku tabulky
 
; tisk informace o tabulce (reference...)
; print(tbl)
0008    GGET     1   0        ; získání reference na funkci se jménem "print"
0009    MOV      2   0        ; bude se tisknout reference na tabulku
0010    CALL     1   1   2    ; volání funkce print()
 
; tisk počtu prvků v tabulce
; print(#tbl)
0011    GGET     1   0        ; získání reference na funkci se jménem "print"
0012    LEN      2   0        ; získání počtu prvků tabulky (resp. pole, které je součástí tabulky)
0013    CALL     1   1   2    ; volání funkce print()
 
; tisk hodnoty prvního prvku v tabulce
; print(tbl[1])
0014    GGET     1   0        ; získání reference na funkci se jménem "print"
0015    TGETB    2   0   1    ; přečtení hodnoty prvního prvku tabulky
0016    CALL     1   1   2    ; volání funkce print()
 
; tisk hodnoty druhého prvku v tabulce
; print(tbl[2])
0017    GGET     1   0        ; získání reference na funkci se jménem "print"
0018    TGETB    2   0   2    ; přečtení hodnoty druhého prvku tabulky
0019    CALL     1   1   2    ; volání funkce print()
 
; tisk hodnoty desátého prvku v tabulce
; print(tbl[10])
0020    GGET     1   0        ; získání reference na funkci se jménem "print"
0021    TGETB    2   0  10    ; přečtení hodnoty desátého prvku tabulky
0022    CALL     1   1   2    ; volání funkce print()
 
; každý program je automaticky ukončen následující instrukcí
0023    RET0     0   1
 
; konec

5. Demonstrační příklad test27.lua – přidání prvků do tabulky s využitím (konstantních) klíčů

Kromě celočíselných indexů lze pro adresování ukládaných či čtených prvků použít i klíče jiného typu. Velmi často se v programovacím jazyku Lua používají klíče typu řetězec (string), takže není divu, že přímo v mezijazyku existují instrukce umožňující manipulaci s prvky tabulky určenými právě s využitím řetězců, přesněji řečeno s využitím řetězcových literálů (konstant). Podívejme se na jednoduchý příklad:

5.1 Zdrojový kód příkladu test27.lua

--
-- LuaJIT: demonstrační příklad číslo 27
--
-- Práce s tabulkami:
--    * vytvoření a inicializace prázdné tabulky
--    * přidání prvku do tabulky s použitím klíčů
--    * tisk poctu prvku tabulky
--    * přístup k prvkům tabulky
--
 
 
 
-- vytvoření prázdné tabulky
local tbl = {}
 
-- nastavit hodnotu prvku tabulky
tbl["first"] = 777
 
-- nastavit hodnotu prvku tabulky
tbl["second"] = 999
 
-- nastavit hodnotu prvku tabulky
tbl["tenth"] = 1000
 
-- tisk informace o tabulce (reference...)
print(tbl)
 
-- tisk poctu prvku v tabulce
print(#tbl)
 
-- tisk hodnoty prvního prvku v tabulce
print(tbl["first"])
 
-- tisk hodnoty druhého prvku v tabulce
print(tbl["second"])
 
-- tisk hodnoty desátého prvku v tabulce
print(tbl["tenth"])
 
 
 
-- finito

5.2 Překlad příkladu test27.lua do mezijazyka LuaJITu

Ve zdrojovém kódu demonstračního příkladu test27.lua jsme viděli použití jazykové konstrukce pro zápis prvku do tabulky i pro čtení prvku z tabulky, přičemž pro adresování prvků byl použit řetězcový literál. Tyto dvě konstrukce se do mezijazyka LuaJITu přeloží s využitím instrukcí TSETSTGETS, kde S na konci jména těchto instrukcí samozřejmě znamená „string“. Tyto instrukce tedy namísto přímého indexu prvku obsahují index do tabulky konstantních řetězců, což znamená, že mnoho operací s asociativními poli je přeloženo velmi efektivním způsobem:

; Překlad demonstračního příkladu test27.lua
; do IR využívaného virtuálním strojem a JIT
; překladačem LuaJIT.
 
-- BYTECODE -- test27.lua:0-44
 
; vytvoření prázdné tabulky
; local tbl = {}
0001    TNEW     0   0
 
; nastavit hodnotu prvního prvku tabulky
; tbl["first"] = 777
0002    KSHORT   1 777        ; ukládaná konstanta
0003    TSETS    1   0   0    ; uložení hodnoty pod klíčem "first"
 
; nastavit hodnotu druhého prvku tabulky
; tbl["second"] = 999
0004    KSHORT   1 999        ; ukládaná konstanta
0005    TSETS    1   0   1    ; uložení hodnoty pod klíčem "second"
 
; nastavit hodnotu desátého prvku tabulky
; tbl["tenth"] = 1000
0006    KSHORT   1 1000       ; ukládaná konstanta
0007    TSETS    1   0   2    ; uložení hodnoty pod klíčem "tenth"
 
; tisk informace o tabulce (reference...)
; print(tbl)
0008    GGET     1   3        ; získání reference na funkci se jménem "print"
0009    MOV      2   0        ; bude se tisknout reference na tabulku
0010    CALL     1   1   2    ; volání funkce print()
 
; tisk počtu prvků v tabulce
; print(#tbl)
0011    GGET     1   3        ; získání reference na funkci se jménem "print"
0012    LEN      2   0        ; získání počtu prvků tabulky (resp. pole, které je součástí tabulky)
0013    CALL     1   1   2    ; volání funkce print()
 
; tisk hodnoty prvního prvku v tabulce
; print(tbl["first"])
0014    GGET     1   3        ; získání reference na funkci se jménem "print"
0015    TGETS    2   0   0    ; přečtení prvku uloženého pod klíčem "first"
0016    CALL     1   1   2    ; volání funkce print()
 
; tisk hodnoty druhého prvku v tabulce
; print(tbl["second"])
0017    GGET     1   3        ; získání reference na funkci se jménem "print"
0018    TGETS    2   0   1    ; přečtení prvku uloženého pod klíčem "second"
0019    CALL     1   1   2    ; volání funkce print()
 
; tisk hodnoty desátého prvku v tabulce
; print(tbl["tenth"])
0020    GGET     1   3        ; získání reference na funkci se jménem "print"
0021    TGETS    2   0   2    ; přečtení prvku uloženého pod klíčem "tenth"
0022    CALL     1   1   2    ; volání funkce print()
 
; každý program je automaticky ukončen následující instrukcí
0023    RET0     0   1
 
; konec

6. Demonstrační příklad test28.lua – přidání prvků do tabulky s využitím proměnných namísto konstantních indexů či klíčů

Existuje ještě jeden způsob adresování prvků uložených v tabulkách – namísto celočíselných konstant či řetězcových literálů se totiž může použít hodnota uložená do proměnné, ať již se jedná o celočíselnou hodnotu či o řetězec či hodnotu jiného typu (kromě nil). I tuto jazykovou konstrukci musí být LuaJIT schopen přeložit do mezijazyka, ovšem nemůže přitom využít ani dvojici TSETB+TGETB ani dvojici TSETS+TGETS:

6.1 Zdrojový kód příkladu test28.lua

--
-- LuaJIT: demonstrační příklad číslo 28
--
-- Práce s tabulkami:
--    * vytvoření a inicializace prázdné tabulky
--    * přidání prvku do tabulky s použitím proměnných namísto klíčů
--    * tisk poctu prvku tabulky
--    * přístup k prvkům tabulky
--
 
 
 
-- vytvoření prázdné tabulky
local tbl = {}
 
-- inicializace proměnných použitých jako klice
local key1 = "first"
local key2 = 2
local key3 = "tenth"
 
-- nastavit hodnotu prvku tabulky
-- s využitím indexu či klice uloženého v proměnné
tbl[key1] = 777
 
-- nastavit hodnotu prvku tabulky
-- s využitím indexu či klice uloženého v proměnné
tbl[key2] = 999
 
-- nastavit hodnotu prvku tabulky
-- s využitím indexu či klice uloženého v proměnné
tbl[key3] = 1000
 
-- tisk informace o tabulce (reference...)
print(tbl)
 
-- tisk poctu prvku v tabulce
print(#tbl)
 
-- tisk hodnoty prvku v tabulce
-- s využitím indexu či klice uloženého v proměnné
print(tbl[key1])
 
-- tisk hodnoty prvku v tabulce
-- s využitím indexu či klice uloženého v proměnné
print(tbl[key2])
 
-- tisk hodnoty prvku v tabulce
-- s využitím indexu či klice uloženého v proměnné
print(tbl[key3])
 
 
 
-- finito

6.2 Překlad příkladu test28.lua do mezijazyka LuaJITu

Podívejme se nyní na překlad demonstračního příkladu test28.lua do mezijazyka LuaJITu. Můžeme zde vidět novou dvojici instrukcí TSETVTGETV, kde V na konci jmen těchto instrukcí samozřejmě značí „variable“. Tyto dvě instrukce se použijí při zápisu resp. při čtení hodnoty libovolného prvku tabulky v případě, kdy není možné pro adresaci použít ani celé číslo, ani řetězcový literál. Operandy obou instrukcí jsou tři – index čteného/zapisovaného slotu, index slotu obsahujícího referenci na tabulku a konečně index slotu obsahujícího hodnotu použitou jako index či jako klíč:

; Překlad demonstračního příkladu test28.lua
; do IR využívaného virtuálním strojem a JIT
; překladačem LuaJIT.
 
-- BYTECODE -- test28.lua:0-55
 
; vytvoření prázdné tabulky
; local tbl = {}
0001    TNEW     0   0
 
; inicializace proměnných použitých jako klíče
; local key1 = "first"
0002    KSTR     1   0        ; řetězec "first"
 
; local key2 = 2
0003    KSHORT   2   2        ; ukládaná konstanta
 
; local key3 = "tenth"
0004    KSTR     3   1        ; řetězec "tenth"
 
; nastavit hodnotu prvku tabulky
; s využitím indexu či klíče uloženého v proměnné
; tbl[key1] = 777
0005    KSHORT   4 777        ; ukládaná konstanta
0006    TSETV    4   0   1    ; uložení hodnoty do tabulky
 
; nastavit hodnotu prvku tabulky
; s využitím indexu či klíče uloženého v proměnné
; tbl[key2] = 999
0007    KSHORT   4 999        ; ukládaná konstanta
0008    TSETV    4   0   2    ; uložení hodnoty do tabulky
 
; nastavit hodnotu prvku tabulky
; s využitím indexu či klíče uloženého v proměnné
; tbl[key3] = 1000
0009    KSHORT   4 1000       ; ukládaná konstanta
0010    TSETV    4   0   3    ; uložení hodnoty do tabulky
 
; tisk informace o tabulce (reference...)
; print(tbl)
0011    GGET     4   2        ; získání reference na funkci se jménem "print"
0012    MOV      5   0        ; bude se tisknout reference na tabulku
0013    CALL     4   1   2    ; volání funkce print()
 
; tisk poctu prvku v tabulce
; print(#tbl)
0014    GGET     4   2        ; získání reference na funkci se jménem "print"
0015    LEN      5   0        ; získání počtu prvků tabulky (resp. pole, které je součástí tabulky)
0016    CALL     4   1   2    ; volání funkce print()
 
; tisk hodnoty prvku v tabulce
; s využitím indexu či klíče uloženého v proměnné
; print(tbl[key1])
0017    GGET     4   2        ; získání reference na funkci se jménem "print"
0018    TGETV    5   0   1    ; přečtení prvku z tabulky
0019    CALL     4   1   2    ; volání funkce print()
 
; tisk hodnoty prvku v tabulce
; s využitím indexu či klíče uloženého v proměnné
; print(tbl[key2])
0020    GGET     4   2        ; získání reference na funkci se jménem "print"
0021    TGETV    5   0   2    ; přečtení prvku z tabulky
0022    CALL     4   1   2    ; volání funkce print()
 
; tisk hodnoty prvku v tabulce
; s využitím indexu či klíče uloženého v proměnné
; print(tbl[key3])
0023    GGET     4   2        ; získání reference na funkci se jménem "print"
0024    TGETV    5   0   3    ; přečtení prvku z tabulky
0025    CALL     4   1   2    ; volání funkce print()
 
; každý program je automaticky ukončen následující instrukcí
0026    RET0     0   1
 
; konec

7. Demonstrační příklad test29.lua – práce s dvourozměrnými tabulkami (maticemi)

Kromě jednorozměrných tabulek je v programovacím jazyce Lua samozřejmě možné pracovat i s tabulkami, které mají vetší počet dimenzí. Velmi časté bývá použití dvourozměrných tabulek neboli matic. Z hlediska programovacího jazyka Lua je práce s vícerozměrnými tabulkami snadná, ovšem zajímavé bude zjistit, jakým způsobem se příkazy pro čtení a zápis prvků přeloží do mezijazyka LuaJITu. K otestování základních vlastností IR nám bude sloužit poměrně jednoduchý demonstrační příklad, v němž je nejdříve vytvořena matice a následně se přistupuje jak k jednotlivým řádkům této matice, tak i k prvkům uloženým v matici (přesněji řečeno na vybraném řádku).

7.1 Zdrojový kód příkladu test29.lua

--
-- LuaJIT: demonstrační příklad číslo 29
--
-- Práce s tabulkami:
--    * vytvoření a inicializace dvourozměrné tabulky
--    * vložení podtabulek do "hlavni" tabulky
--    * přístup k prvkům matice
--
 
 
 
-- vytvoření a inicializace dvourozměrné
local tbl = {
    { 1,  2,  3,  4},
    { 5,  6,  7,  8},
    { 9, 10, 11, 12}
}
 
-- tisk informace o tabulce (reference...)
print(tbl)
 
-- tisk poctu prvku v tabulce (nejvyšší dimenze)
print(#tbl)
 
-- tisk poctu prvku v prvním řádku tabulky (nižší dimenze)
print(#tbl[1])
 
-- tisk poctu prvku ve druhem řádku tabulky (nižší dimenze)
print(#tbl[2])
 
-- tisk poctu prvku ve třetím řádku tabulky (nižší dimenze)
print(#tbl[3])
 
-- tisk hodnoty vybraného prvku v dvourozměrné tabulce
print(tbl[1][1])
 
-- tisk hodnoty vybraného prvku v dvourozměrné tabulce
print(tbl[3][4])
 
-- finito

7.2 Překlad příkladu test29.lua do mezijazyka LuaJITu

V IR přeloženého příkladu test29.lua můžeme vidět hned několik nových technik. První z nich se týká vytvoření a inicializace dvourozměrné tabulky, což je již operace, kterou nelze vykonat jedinou instrukcí TNEW, ale je nutné ještě explicitně vytvořit jednotlivé řádky matice:

; Překlad demonstračního příkladu test29.lua
; do IR využívaného virtuálním strojem a JIT
; překladačem LuaJIT.
 
-- BYTECODE -- test29.lua:0-42
 
; vytvoření a inicializace dvourozměrné tabulky
; local tbl = {
0001    TNEW     0   4
;     { 1,  2,  3,  4},
0002    TDUP     1   0
0003    TSETB    1   0   1
;     { 5,  6,  7,  8},
0004    TDUP     1   1
0005    TSETB    1   0   2
;     { 9, 10, 11, 12}
0006    TDUP     1   2
0007    TSETB    1   0   3
; }

Tisk souhrnných informací o tabulce zůstává stejný, nezávisle na tom, zda se jedná o jednorozměrný vektor či o dvourozměrnou matici. Stejný je i způsob zjištění počtu prvků v tabulce, což zde znamená počet řádků matice:

CS24_early

; tisk informace o tabulce (reference...)
; print(tbl)
0008    GGET     1   3        ; získání reference na funkci se jménem "print"
0009    MOV      2   0
0010    CALL     1   1   2    ; volání funkce print()
 
; tisk počtu prvků v tabulce (nejvyšší dimenze)
; print(#tbl)
0011    GGET     1   3        ; získání reference na funkci se jménem "print"
0012    LEN      2   0        ; výpočet velikosti tabulky (nejvyšší dimenze)
0013    CALL     1   1   2    ; volání funkce print()

Zajímavější je již přístup k jednotlivým řádkům matice, protože každý řádek je vlastně prvkem nadřazené tabulky. Totéž platí i při přístupu ke konkrétním prvkům matice, kde je nutné použít dvojici instrukcí TGETB – první pro získání reference na řádek, druhou pro přečtení prvku z tohoto řádku:

; tisk počtu prvků v prvním řádku tabulky (nižší dimenze)
; print(#tbl[1])
0014    GGET     1   3        ; získání reference na funkci se jménem "print"
0015    TGETB    2   0   1    ; načtení celého řádku tabulky
0016    LEN      2   2        ; výpočet délky jednoho řádku tabulky
0017    CALL     1   1   2    ; volání funkce print()
 
; tisk počtu prvků ve druhem řádku tabulky (nižší dimenze)
; print(#tbl[2])
0018    GGET     1   3        ; získání reference na funkci se jménem "print"
0019    TGETB    2   0   2    ; načtení celého řádku tabulky
0020    LEN      2   2        ; výpočet délky jednoho řádku tabulky
0021    CALL     1   1   2    ; volání funkce print()
 
; tisk počtu prvků ve třetím řádku tabulky (nižší dimenze)
; print(#tbl[3])
0022    GGET     1   3        ; získání reference na funkci se jménem "print"
0023    TGETB    2   0   3    ; načtení celého řádku tabulky
0024    LEN      2   2        ; výpočet délky jednoho řádku tabulky
0025    CALL     1   1   2    ; volání funkce print()
 
; tisk hodnoty vybraného prvku v dvourozměrné tabulce
; print(tbl[1][1])
0026    GGET     1   3        ; získání reference na funkci se jménem "print"
0027    TGETB    2   0   1    ; načtení celého řádku tabulky
0028    TGETB    2   2   1    ; načtení vybraného prvku z řádku
0029    CALL     1   1   2    ; volání funkce print()
 
; tisk hodnoty vybraného prvku v dvourozměrné tabulce
; print(tbl[3][4])
0030    GGET     1   3        ; získání reference na funkci se jménem "print"
0031    TGETB    2   0   3    ; načtení celého řádku tabulky
0032    TGETB    2   2   4    ; načtení vybraného prvku z řádku
0033    CALL     1   1   2    ; volání funkce print()
 
; každý program je automaticky ukončen následující instrukcí
0034    RET0     0   1
 
; konec

8. Repositář se zdrojovými kódy všech šesti dnešních demonstračních příkladů

Všech šest dnes popsaných a taktéž „disasemblovaných“ demonstračních příkladů bylo uloženo do Git repositáře umístěného na adrese https://github.com/tisnik/luajit-examples. Odkazy na prozatím poslední verze těchto šesti příkladů naleznete v tabulce umístěné pod tímto odstavcem:

9. Odkazy na Internetu

  1. Wikipedia: Mezijazyk
    http://cs.wikipedia.org/wi­ki/Mezijazyk
  2. The LuaJIT Project
    http://luajit.org/index.html
  3. LuaJIT FAQ
    http://luajit.org/faq.html
  4. LuaJIT Performance Comparison
    http://luajit.org/performance.html
  5. LuaJIT 2.0 intellectual property disclosure and research opportunities
    http://article.gmane.org/gma­ne.comp.lang.lua.general/58908
  6. LuaJIT Wiki
    http://wiki.luajit.org/Home
  7. LuaJIT 2.0 Bytecode Instructions
    http://wiki.luajit.org/Bytecode-2.0
  8. Programming in Lua 9.1 – Coroutine Basics,
    http://www.lua.org/pil/9.1.html
  9. Programming in Lua (first edition)
    http://www.lua.org/pil/contents.html
  10. Programming in Lua: 6 – More about Functions
    http://www.lua.org/pil/6.html
  11. Lua Lanes
    http://kotisivu.dnainternet­.net/askok/bin/lanes/
  12. Programming in Lua: 6.1 – Closures
    http://www.lua.org/pil/6.1.html
  13. Programming in Lua: 9.1 – Coroutine Basics
    http://www.lua.org/pil/9.1.html
  14. Programming in Lua: Numeric for
    http://www.lua.org/pil/4.3.4.html
  15. Programming in Lua: break and return
    http://www.lua.org/pil/4.4.html
  16. Programming in Lua: Tables
    http://www.lua.org/pil/2.5.html
  17. Programming in Lua: Table Constructors
    http://www.lua.org/pil/3.6.html
  18. Programovací jazyk Lua
    http://palmknihy.cz/web/kni­ha/programovaci-jazyk-lua-12651.htm
  19. Lua: Tables Tutorial
    http://lua-users.org/wiki/TablesTutorial
  20. Lua: Control Structure Tutorial
    http://lua-users.org/wiki/ControlStruc­tureTutorial
  21. Lua Types Tutorial
    http://lua-users.org/wiki/LuaTypesTutorial
  22. Goto Statement in Lua
    http://lua-users.org/wiki/GotoStatement
  23. Lua 5.2 sources
    http://www.lua.org/source/5.2/
  24. Lua 5.2 sources – lopcodes.h
    http://www.lua.org/source/5­.2/lopcodes.h.html
  25. Lua 5.2 sources – lopcodes.c
    http://www.lua.org/source/5­.2/lopcodes.c.html
  26. Lambda the Ultimate: Coroutines in Lua,
    http://lambda-the-ultimate.org/node/438
  27. Coroutines Tutorial,
    http://lua-users.org/wiki/CoroutinesTutorial
  28. Lua Coroutines Versus Python Generators,
    http://lua-users.org/wiki/LuaCorouti­nesVersusPythonGenerators

Byl pro vás článek přínosný?

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.