Jazyk Scheme

Ak si myslíte, že ste videli všetko, tak teraz sa držte! Spíšem krátky súhrn jazyka Scheme pre potreby skriptovania a úpravu obrázkov (pozn.: tento popis jazyka sa vzťahuje na interpreter SIOD).

Úvod

Toto: "( )" je funkcia. I keď to nemusí byť na prvý pohľad jasné je to tak. Jazyk Scheme ako dialekt jazyka Lisp stojí na dvoch pravidlách. Prvé je, že všetko sa uzatvára do okrúhlich zátvoriek , druhé pravidlo znie: názov funkcie je prvý v zátvorke a zbytok sú premenné danej funkcie.

Takže ak chceme sčítať dve čísla napíšeme (+ 5 2), kde "+" je názov funkcie. Pozor toto: (+5 6) by bola chyba, pretože nemáme funkciu s názvom "+5"! Takže pozor aby Vám nechýbali medzery. Takáto logika (operátor je pred operandami) sa tiež nazýva prefixová, zo života sme zvyknutý na infixovú a takisto exituje aj postfixová, kde operátor je až po operandoch (5 2 +), nazývaná tiež obrátená Poľská logika.

Ak by ste si chceli scheme vyskúšať interaktívne tak cez GIMP spustite Script-fu konzolu - Filters/Script-fu/Console. Stačí do príkazového riadku napísať funkciu (na jeden riadok) a výsledok sa objavý na obrazovke. Je to najlepší spôsob ako správne pochopiť jazyk.

Funkcie

Ako prvé uvediem do pozornosti funkcie, pretože program napísaný v jazyku scheme sa skladá z blokov - definícií jednotlivých funkcií. Nie je tu nejaká striktná štruktúra tak ako v Pascale. No a aby takýto program mohol aj fungovať, treba niekde definovať, ktorá funkcia sa spustí ako prvá, ako keby z vonku, ale k tomu sa dostaneme až potom. Všeobecná podoba funkcie pri volaní je:

(function_name variable_1 variable_2 ... variable_n)

Kde premenné sú oddelené len medzerou a nie čiarkou. Podobne i definovanie novej funkcie je pomocou volania inej funkcie:

(define (function_name variable_1 ... variable_n)
	; body
	return_value
)

Netreba pritom zabúdať, že celá konštrukcia (name variable ... ) vracia nejakú hodnotu (alebo pole či list) pretože ide vlastne o volanie funkcie píslušného mena. Táto hodnota je vyššie pomenovaná ako return_value.

Premenné

V jazyku Scheme podobne ako v Javaskripte sa typ premennej neudáva. Je to dynamicky typovaný jazyk. Každopádne, i tak môžme hovoriť o premenných uchovávajúcich reálne čísla (1.25; 0,2; ..), textové reťazce ("hallo"; "The GIMP"; apod.) a väčšie dátové štruktúry.

Podľa poľa pôsobnosti premenných ich delíme na globálne (platia v celom programe) a lokálne (ich platnosť je len v bloku, v ktorom sú definované). Lokálne premenné sa definujú volaním funkcie let* pričom platia len v bloku tejto funkcie. Všeobecná podoba použitia funkcie let je:

(let*
	(
	(variable_1 value)
	(variable_2 value)
	...
	)
	
	; block of validity of local variables
)

Takto vytvorené premenné sa dajú meniť - ich hodnoty - zavolaním funkcie priradenia set!, to je zároveň spôsob ako vytvoriť globálne premenné (definovaním pomocou set!), ale nieje dôvod ich bežne používať:

(set! variable_name new_value)

List

Prvým typom väčšej dátovej štruktúry je list. Vopred mi prepáčte za slovíčko list, v preklade to znamená zoznam, no nebudem používať preklad, takže si to nepomýlte s listami na strome alebo podobne. Upozorňujem, že nejde o pole. List definujem tak isto ako inú premennú ale jej hodnota nebute text alebo číslo, ale list: (set! variable '(a b c))

Je nutné pred zátvorkami napísať: " ' " , aby interpreter vedel, že to čo nasleduje nieje funkcia ale list. Obsahom listu môžu byť premenné ľubovolného typu, list môže obsahovať jeden element alebo môže byť aj prázdny. Teraz je dôležité povedať aká je skutočná štruktúra listu. List sa skladá z hlavičky a chvosta. Hlavička je prvý prvok listu a chvost je to ostatné, preto ak máme list: '(10 a "v"), tak v skutočnosti sa chová ako: '(10 (a ("v" ())))

Pridanie prvku do listu:
(cons 0 '(1 2 3)) ; returns '(0 1 2 3)
Vytvorenie listu poskladaním:
(list "a" "b" '(1 2)) ; returns '("a" "b" (1 2))
(list 'a 'b '(1 2))   ; returns '(a b (1 2))
(list p 2 3)          ; returns '(1 2 3) if p was 1
Vyňatie hlavičky listu:
(car '(1 2)) ; returns 1
Vyňatie chvostu listu
(cdr '(1 2)) ; returns '(1)

Pozor, funkcia cdr vráti chvost listu, ale ako list. Treba mať na pamäti, že zatiaľ čo "'(1)" je list, "1" je hodnota. Preto na vrátenie hodnoty druhého prvku listu '(1 2) treba použiť (car (cdr '(1 2))), alebo skrátene (cadr '(1 2)). Podobne existujú funkcie: cddr, caadr, cadar, apod. (a = car, d = cdr). Na skúšku môžete vyňať trojku z listu pomocou dvoch funkcií: '( (1 2 (3 4 5) 6) 7 8 (9 10) ).

Ďalšie funkcie pre prácu s listami nájdete v tabuľke nižšie.

Pole = array

Ak budete používať polia pri skriptovaní tak si vystačíte s jednorozmernými poliami a práve tie rozoberiem. Pole je typ premennej, ktorá v sebe uchováva usporiadané a oindexované hodnoty rovnakého typu. Pole vytvoríme podobne ako iné premenné ale namiesto hodnoty sa napíše:

(set! variable_name (cons-array number_of_items 'type))

'type nieje povinné udať, ak ho napíšeme, tak za 'typ sa dá dosadiť: 'double, 'string, 'byte (čísla od -128 po 127) alebo 'lisp (čo je typ list). Pravdaže počet prvkov poľa musí byť celočíselný.

Vrátenie i-teho prvku poľa sa deje pomocou funkcie aref (prvý prvok má index 0, druhý 1 atd.): (aref variable_name index)

Vloženie hodnoty do i-teho prvku poľa zas funkciou aset: (aset variable_name index new_value)

Vetvenie programu - if, cond

Všeobecná podoba if je:

(if (condition)
	() ; block evaluated if condition is true
	() ; else
)
	
; or
(if (condition) () value)

V Pascale to je case of v jave zas switch a tu cond. Jeho schéma použitia je:

(cond 
	( (condition_1) (command_1) )
	( (condition_2) (command_2) )
	...
	( (condition_n) (command_n) )
)

Cykli

Jediná možnosť vytvorenia cyklu je pomocou while s podmienkou na začiatku:

(while (condition)
	;body of cycle
) ; repeat while condition is true

Užitočné funkcie

Všeobecná podoba funkcie pri volaní bude: (function_name param_1 paramr_2 ... )

Logické
meno funkcie poznámka parametre návratová hodnota
< menší dve číselné hodnoty TRUE/FALSE
> väčší dve číselné hodnoty TRUE/FALSE
>= väčší rovný dve číselné hodnoty TRUE/FALSE
<= menší rovný dve číselné hodnoty TRUE/FALSE
= rovný dve číselné hodnoty TRUE/FALSE
eq? vráti TRUE ak x a y je ten istý objekt x y TRUE/FALSE
equal? vráti TRUE ak x a y sú rovnaké objekty (napr porovnávanie listov alobo stringov) x y TRUE/FALSE
eqv? vráti TRUE ak x a y je ten istý objekt alebo ak sú x a y numericky identické x y TRUE/FALSE
null? vráti TRUE ak x je prázdny list x TRUE/FALSE
number? vráti TRUE ak x je číslo x TRUE/FALSE
not logicky zneguje výraz výraz TRUE/FALSE
bit-and C & operátor dve čísla integer
bit-not C ~ operátor číslo intger
bit-or C | operátor dve čísla integer
bit-xor C ^ operátor dve čísla integer
Matemetické
meno funkcie poznámka parametre návratová hodnota
abs absolútna hodnota číslo |číslo|
sin uhol je v radiánoch uhol double
asin arcsin číslo od -1 po 1 uhol v rad
cos uhol double
acos arccos číslo od -1 po 1 uhol v rad
tan uhol double
atan arctan číslo uhol v rad
atan2 arctan(x/y) x y uhol v rad
pow x^y double - x y double
sqrt druhá odmocnina číslo double
exp e^x x double
log prirodzený logaritmus ln() číslo double
fmod delenie modulo dve celé čísla integer
trunc odrezanie desatinej časti čísla číslo integer
max max{x1;x2;...} x1 x2 ... číslo
min min{x1;x2;...} x1 x2 ... číslo
srand reset seedu pre rand, viz C funkcia seed '()
rand náhodné číslo od 0 po m-1 m integer
ash aritmetický posun hodnoty 'v' o 'b' bitov v b integer
Funkcie pre prácu s reťazcami
meno funkcie poznámka parametre návratová hodnota
array->hexstr konvertuje string alebo bytové pole do hexadecimálneho tvaru string string
number->string konvertuje číslo podľa 'základu', ktorý môže byť číslo 8,10 alebo 16, 'šírka' a 'presnosť' sú voliteľné x základ šírka presnosť
parse-number konvertuje string na číslo string číslo
read-from-string prečíta string string výraz
strbreakup vráti list s rozsekaným stringom podľa 'separátoru' string separátor list
strcmp vráti 0 ak str1 a str2 rovnaké, -1 ak str1 je kratší ako str2, inak 1 str1 str2 -1/0/1
strcspn vráti polohu prvého znaku v 'str' ktorý bol nájdený v indikátore - čo je string, ak nič nenájde vráti dĺžku stringu str indikátor integer
string-append spojenie reťazcov textové reťazce string
string-downcase "Hallo" > "hallo" string string
string-length počet znakov reťazca texový reťazec integer
string-lessp vráti TRUE ak str1 je kratší ako str2 str1 str2 TRUE/FALSE
string-search 'key' je string a funkcia vráti jeho polohu v stringu 'str', ak ho nenájde vráti () key str integer
string-trim oreže biele znaky z okrajov reťazca (existuje aj string-trim-left a string-trim-right) string string
string-upcase "Hallo" > "HALLO" string string
string? vráti TRUE ak x je string x TRUE/FALSE
strspn vráti pozíciu prvého znaku v 'str' ktorý nebol nájdený v 'indikátore' - čo je string, ak taký znak nenašiel vráti dĺžku 'str'. Príklad: (define (string-trim-left x) (substring x (strspn x " \t"))) str indikátor integer
substring výrez z reťazca 'str' podľa indexov 'start' a 'end' str start end string
substring-equal? (equal? str (substring str2 start end)) str str2 start end TRUE/FALSE
unbreakupstr opačne ako strbreakup list separátor string
length dĺžka objektu - list, string, array objekt integer
Funkcie pre prácu s listami
meno funkcie poznámka parametre návratová hodnota
append vráti list, ktorý vznikol rozšírením zo zadaných listov listy list
butlast odstráni z listu posledný prvok, ak je tým prvkom list tak ten list list list
delq vráti list v ktorom niesú elementy ekvivalentné 'elemntu' element list list
larg-default vráti prvok 'listu' podľa pozície v 'indexe', ak je list krátky tak vráti 'default-hodnotu', nepočíta elemnety listu - reťazce začínajúce sa na ":" list index default-hodnota
last vráti posledný element listu list element
make-list vráti list danej 'dĺžky' vyplnený daným 'elementom' dĺžka element list
nreverse zničí list, ktorý mu dáme ale vráti list s prvkami v opačnom poradí list list
nth vráti elemnt z listu na danej pozícií, prvý element má poziciu 0 index list element
qsort pomocou rekurzívneho utriedenia vráti list s elemntami usporiadanými pomocou predicate-fcn opierajúc sa o access-fcn, napr.:(qsort '(3 1 5) <)
=> '(1 3 5)
(qsort '((3 a) (2 b)) < car)
=> '((2 b)(3 a))
list predicate-fcn access-fcn list
quote (qoute x) je to isté ako 'x
reverse vráti list s prvkami v opačnom poradí list list
subset vráti podmnožinu listu zloženú z prvkov, ktoré vyhovujú podmienke, napr.: (subset string? '("a" 5))
=> '("a")
podmienka list
Vychytávky
meno funkcie poznámka parametre návratová hodnota
base64encode zkóduje string pomocou base64 string string
base64decode dekóduje string v base64 string string
eval vyhodnotí 'výraz' v kontexte 'prostredia' (velmi to nepoznám ale skúste napísať: (eval (read-from-string "(+ 2 1)")) výraz prostredie hodnota výrazu
realtime vráti čas v sekundách od 1.1. 1970 GMT - double
typeof vráti symbol opysujúci objekt x alebo jeho kód objekt_x symbol

Formy

Pod takýmto výrazom som našiel pár funkcií, ktoré majú ako svoje parametre výrazy, teda niečo ako (function_name x1 x2 ...) alebo iné formy. Veľa o tom neviem, takže na tomto poli sa zíde ak budete experimentovať Vy sami. K týmto formám patria aj while, if, define (napr. "(define variable value)" priradí hodnotu k premennej), ale tie som rozoberal už skôr.

Formy
meno funkcie poznámka parametre návratová hodnota
and vyhodnocuje výrazy zľava doprava pokiaľ výraz vracia "not-null" hodnotu výrazy hodnota posledného výrazu
begin vyhodnotí všetky výrazy hodnota posledného výrazu
cond výrazy, nazvime ich položky v cond majú špeciálny tvar: (logicky-výraz výraz1 výraz2 ...) akonáhle sa príde na položku, ktorej logický výraz je pravdivý, vykonajú sa výrazy v položke (posledný výraz určí návratovú hodnotu cond) a tam sa skončí vyhodnocovanie položiek hodnota posledného výrazu vo vykonanej položke
or vyhodnocuje výrazy v poradí pokiaľ nenarazí na výraz, ktorý má "non-null" hodnotu, vtedy skončí hodnota prvého výrazu s "non-null" hodnotou
prog1 vyhodnotí všetky výrazy hodnota prvého výrazu

Konštanty

Jazyk scheme pozná nasledovné konštanty, ich použitie je rovnaké ako ostatných premenných. Pozor, konštanty píšte tak ako sú v tabulke, jazyk scheme je casesensitive.

Konštanty
meno hodnota poznámka
*pi* 3.14159 ..
TRUE 1 používajte TRUE a nie 1,
napr.: (= log_variable TRUE)
FALSE niečo iné než 1 to isté

Záver

Ak ste si niečím neistý, dobre padne nájsť si skript, v ktorom je to čo hľadáte a vidieť to tak ako to funguje.

Patrilo by sa tu k záveru napísať aj bibliografiu. V stručnosti to zhrniem. Zdroje som čerpal z dokumentárneho projektu pre Gimp a podobných návodov, ľahko dosiahnuteľných prostredníctvom Google ;-). Každopádne mi veľmi pomohol súhrn všetkých funkcií SIOD, ktorý je od George J. Carrette na adrese http://people.delphi.com/gjc/siod.html, z neho som dosť čerpal, takže určite nájdete niektoré podobnosti.

posledná aktualizácia 10.1.2010