Skip to content

Instantly share code, notes, and snippets.

@met
Last active August 6, 2020 18:42
Show Gist options
  • Save met/6f1ff3fcd403742472c4e6e95da8d5d2 to your computer and use it in GitHub Desktop.
Save met/6f1ff3fcd403742472c4e6e95da8d5d2 to your computer and use it in GitHub Desktop.
Lua idiomy

Lua idiomy

Idiomy jsou konstrukce, které se neřeší logikou, ale naučením. Ty první číslované pochází z oficiální knihy Roberto Ierusalimschy: Programming in Lua, 4th edition.

1. Defaultní hodnota proměnné

x = x or value

Je obdobou zápisu if not x then x = value Pokud hodnota x nemá hodnotu, přiřadí jí výchozí hodnotu value. Nefunguje správně, pokud má x hodnotu false.

2. Ternární operátor

(a and b or c)

Případně ((a and b) or c) Je obdobou klasické konstrukce s terciálním operátorem a ? b : c který ale v Lua neexistuje. Nefunguje správně, pokud má b hodnotu false.

3. Seznam kódů pro znaky řetězce

{ string.byte(s, 1, -1) }

Vytvoří seznam (tabulku) kódů všech znaků v s. Funguje ale jen pro řetězce do cca 1MB, jinak dojde k přetečení zásobníku obsahujícího návratové hodnoty funkce. Definice je string,byte(s, start, end).

4. Přidání nové položky do sekvence

a[#a+1] = value

Funguje jen pro sekvence (sekvence = seznam položek 1..n bez děr, sequence = list 1..n without holes).

5. Assert a IO funkce

local f = assert(io.open(filename, mode))

Když IO funkce proběhne dobře, vrátí hodnotu a tu pošle dál i assert. Když IO funkce selže, vrátí dvě hodnoty: nil, a textový popis chyby. Funkce assert přijímá také dvě hodnoty, logickou hodnotu a text chyby. Při chybě nil není vyhodnocen jako pravda, tak assert vyvolá výjimku, jinak assert vrátí výsledek IO funkce.

6. Přiřazení do lokální proměnné stejného jména

local a = a

Proběhne inicializace lokální proměnné a hodnotou globální proměnné a. Lokální proměnná a začne být viditelná až po této deklaraci.

Následující jsou nečíslované, nejsou to již oficiálně proklamované idiomy, pouze konstrukce, které je nutné znát.

False v Lua

Pouze false a nil jsou v podmínkách vyhodnoceny jako false. Vše ostatní je vyhodnoceno jako true, včetně nuly a prázdného řetězce. Ovšem pozor, ačkoliv podmínka vyhodnotí nil jako false, tak porovnání nikoliv nil == false má hodnotu false.

Lokální proměnné bloku do..end

do
local x,y;
<do something>
end

Platnost dočasné proměnné omezíme blokem do .. end.

Inicializace hromadným přiřazením

local a,b = 1,2

Nelze použít jinde obvyklé: local a = 1, b = 2 -- CHYBA Lua totiž nezná jinde obvyklý operátor čárka.

Prohození proměnných dvojím přiřazením

a,b = b,a

Lua provede korektně, protože se pravá strana vyhodnotí dřív než levá.

Vkládání prvku na začátek seznamu/stromu

list = nil
list = { value = "something1", next = list } 
list = { value = "something2", next = list }  
...  

Nově vložený prvek se stane začátkem (hlavou) seznamu a odkazuje na další prvek, který byl předchozí hlavou seznamu. Funguje dobře, protože se pravá stana vyhodnotila dříve než levá.

Problém s lokální rekurzivní funkcí

local f = function() f() end -- CHYBA

Volání f() uvnitř funkce f() zde volá globální funkci f() (a pokud neexistuje globální f(), tak volá nil), protože deklarace lokální funkce f() během zpracovávání jejího těla ještě neexistuje.
Lze vyřešit takto:

local f;
f = function() f() end -- SPRÁVNĚ

Lua se snaží problém obejít přes syntactic sugar, následující definice proběhne v pořádku:

local function f() f() end -- SPRÁVNĚ

Lua totiž interně zápis local function rozvine a provede deklaraci jména funkce předem jako v příkladu výše.

I tak problém trvá, pokud se odkazujeme na lokální f() z jiné funkce, která je v kódu deklarovaná dříve než f(). Pokud nechceme skončit tak, že budeme deklarovat funkce povinně v pořadí, jak se na sebe odkazují, je snazší si napřed zadefinovat všechny jejich lokální identifikátory local f, g, h; a pak teprve funkce deklarovat v libovolném pořadí.

Volání funkce bez závorek

Volání funkce nemusí používat závorky, pokud má funkce jeden argument, kterým je řetězec nebo table constructor. print "a" je totéž jak print("a") type{} je totéž jak type({}) f{x=10} je totéž jak f({x=10}) Používá se k tvorbě pojmenovaných argumentů funkce foo{a=1, b=2} je volání funkce s konstruktorem tabulky jako argumentem, uvnitř funkce foo(arg) se používají zápisem arg.a, arg.b.

Může být matoucí, jelikož print "a" funguje, ale print 1 je chyba. A print "a", "b" znamená print("a"), "b", tedy vytiskne "a" a vrátí nil, "b" jako dva parametry.

Převod mezi řadou argumentů a sekvencí/tabulkou

Sekvenci vyrobíme z řady argumentů obecně:

tabulka = {...}

Opačný směr zajistí unpack, což použijeme, když chceme sekvenci před jako argument volání funkce:

print(unpack(tabulka))

Return doprostřed funkce lze vložit pomocí bloku

function f()
  return -- chyba, return musí být poslední příkaz bloku
  <do something>
end

Ale s blokem zafunguje:

function f()
  do return end
  <do something>
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment