Scheme
Ez a szócikk nem tünteti fel a független forrásokat, amelyeket felhasználtak a készítése során. Emiatt nem tudjuk közvetlenül ellenőrizni, hogy a szócikkben szereplő állítások helytállóak-e. Segíts megbízható forrásokat találni az állításokhoz! Lásd még: A Wikipédia nem az első közlés helye. |
Scheme | |
Paradigma | többelvű: funktionális, procedurális, meta |
Jellemző kiterjesztés | .scm, .ss |
Megjelent | 1975 |
Tervező | Guy L. Steele, Gerald Jay Sussman |
Utolsó kiadás | R6RS (ratified standard) (2007) |
Típusosság | erősen típusos, dinamikus kötés |
Dialektusok | T |
Megvalósítások | sok, lásd még: Category:Scheme implementations |
Hatással volt rá | Lisp, ALGOL, MDL |
Befolyásolt nyelvek | Common Lisp, Dylan, EuLisp, Haskell, Hop, JavaScript, Kernel, Lua, R, Ruby, Clojure , Racket |
Weboldal |
A Scheme programozási nyelv a Lisp nyelvcsalád egyik tagja, illetve a Lisp egyik nyelvjárása (dialektusa). A Scheme tervezői arra törekedtek, hogy a Lispből minden fölösleges tulajdonságot kigyomláljanak, és egy egyszerű, kevés szabállyal leírható, de erőteljes nyelvet hozzanak létre. Például a Scheme-ben egyetlen egységes névtérben találhatók a függvények és a változók. A nyelv precíz leírása és egyszerűsége miatt az oktatásban jól használható; alkalmazását valós problémák megoldására viszont némileg akadályozza, hogy kevés a szabványos eljárás, nincsenek kiterjedt szabványos könyvtárak. Ezt a hiányosságot azonban az utóbbi években igyekeznek pótolni (SRFI = Scheme Request for Implementation) – ma már a legtöbb Scheme implementáció tartalmaz több-kevesebb SRFI-megvalósítást.
A nyelv nagyon rövid története
[szerkesztés]A Scheme nyelv első leírását Gerald Jay Sussman és Guy Lewis Steele Jr. alkotta meg 1975-ben. A nyelvet azóta a javított jelentések (Revised Reports) írják le, melyekből az első 1978, a legutóbbi, az R5RS 1998-ban jelent meg. Az IEEE is szabványosította a nyelvet (IEEE Std 1178-1990). A legújabb szabvány, azaz az R6RS létrehozása éppen folyamatban van. A nyelvet 1981-ben kezdték oktatásra használni az MIT-n, a Yale-en és az Indiana University-n. 1984-ben jelent meg a SICP Archiválva 2005. március 2-i dátummal a Wayback Machine-ben (Structure and Interpretation of Computer Programs) első kiadása. Ez egy meghatározó jelentőségű számítástechnikai tankönyv amely a Scheme nyelvet használja.
A Scheme nyelv jellemzői
[szerkesztés]Az alábbi leírás nem teljes, leginkább az eredeti Lisptől való eltéréseket hangsúlyozza.
A Scheme-ben is a Lispben megszokott teljesen zárójelezett prefix kifejezésforma használatos, például az 1 + (2 * 3)
(matematikai, vagy más nyelvekben szokásos) kifejezést a
(+ 1 (* 2 3))
formában írhatjuk le.
Az adatok között megjelenik a #t
és #f
logikai érték, amely az igaz és a hamis értéket jelöli. Fontos, hogy az üres lista (nil
) nem számít hamis értéknek, mint más Lispeknél.
Változó létrehozása:
(define d 5)
(define z '(1 2))
Értékadás:
(set! z #t)
Általában a mellékhatással rendelkező eljárások neve felkiáltójellel végződik.
Eljárások létrehozása:
(define (f x)
(+ x d))
A Scheme-ben a Lisp más nyelvjárásaiban szokásos függvény elnevezés helyett az eljárás szót használják.
A Scheme-ben a Lisp történetében szokatlan módon a lexikális hatókör szabálya érvényesül (mint az Algolban vagy A Pascalban. Ez azt jelenti, hogy az eljárások hívásakor a definíciójukkor érvényes környezetben keresik a változó értékét, nem pedig a hívási környezetben. (A környezet nem más, mint változó-érték kötések halmaza.) A lexikális hatókörre mintapélda (az előző példákat is figyelembe véve):
(let ((d 1))
(f d))
Ez a kifejezés a 6
értéket adja vissza, mivel az f
eljárás definíciós környezetében a d
változó értéke 5
.
Ha a dinamikus hatókör lenne érvényben (mint például a Common Lispben a globális változók esetén) akkor a let
kifejezés értéke 2
lenne.
A Scheme-ben az adatok típusa nem a változókhoz, hanem az értékekhez van kötve, azaz a Scheme dinamikus típusokat használó nyelv (mint a többi Lisp):
(define v 1)
Itt az v
változó értéke 1, amely egész típusú, de a
(set! v '(1 2))
értékadással már az (1 2)
lista lesz az értéke.
A Scheme-ben létrehozott objektumok (értékek) élettartama nem korlátozott, tehát ha egy eljárás lokális változóját valahogy el tudjuk érni, akkor annak értéke megmarad a hívás után is:
(define (osszeado z)
(lambda (x) (+ z x)))
(define plusz2 (osszeado 2))
Az osszeado
eljárással egy tetszőleges számmal növelő eljárást tudunk létrehozni. Annak ellenére, hogy a program futása – a kifejezések kiértékelése – közben újabb és újabb tárterületeket használ el ez általában nem jelenti azt, hogy gyorsan elfogy a rendszerünk összes tárkapacitása: ha egy érték többé már nem elérhető a rendszer az általa elfoglalt helyet újrahasznosítja. Ez a folyamat a szemétgyűjtés (ilyet más nyelvek is használnak, mint például a Perl, Python vagy a Java).
Az eljárások hívásánál a paraméterek átadása mindig érték szerint történik (call-by-value) (mint általában a Lispben, a C-ben, Pascalban, de például nem úgy, mint a Haskellben, ahol ún. szükség szerinti átadásról beszélünk (call-by-need)). Ezt a paraméterátadási stratégiát mohó kiértékelésnek is nevezzük, ahol a paraméterként megadott kifejezések az eljárás hívása előtt kiértékelődnek – ellentétben a lusta kiértékeléssel, ahol a paraméterként átadott kifejezés csak akkor értékelődik ki, amikor szükség van erre az értékre. Példa:
(define (moho x y) x)
(moho 1 (/ 1 0))
A moho
függvény hívásakor hibajelzést kapunk, holott az y
paraméter értékét nem használjuk a kifejezésünkben – de az érték szerinti paraméterátadás szabálya szerint a ki kell értékelni a (/ 1 0)
kifejezés értékét a moho
hívása előtt, és ez hibajelzéshez vezet.
A Scheme nyelvben az eljárások is első osztályú polgárai a nyelvnek, azaz eljárást át lehet adni paraméterként, használható visszatérési értékként (mint azt az osszeado
eljárásnál láttuk), adatstruktúra része lehet. Névtelen függvényeket a lambda
szimbólummal definiálhatunk:
(lambda (x y) (+ x y 1))
Ez az eljárás összeadja a két paraméterét és még egyet ad hozzá, például a
((lambda (x y) (+ x y 1)) 2 3)
kifejezés a 6
értéket adja vissza.
Más Lisp dialektusokkal ellentétben az eljárás- (vagy függvény)hívás első tagja egy tetszőleges kifejezés, amely értéke eljárás kell, hogy legyen:
(define (buta-pelda x y)
((if (> x 0) + *) x y))
Ez az eljárás összeadja két paraméterét ha x
nagyobb mint 0
, egyébként összeszorozza őket. (A Common Lisppel ellentétben itt nincs szükség a function
speciális formára, vagy a funcall
függvényre, mivel egy névtérben laknak az eljárás-értékek az egyéb adatértékekkel.)
A Scheme nyelv talán egyik legérdekesebb fogalma a folytatás (continuation). A folytatás egy adott számítás menetének jövőjét jelenti, azaz egy program kifejezése kiértékelésének egy adott pillanatától számított további működését. A folytatás a más nyelvekben megtalálható goto, exit, return, catch névvel illetett utasítások, illetve mechanizmusok általánosítása. A folytatás megvalósítására a call-with-current-continuation
(szokásos rövid neve: call/cc
) eljárás szolgál.
A call-with-current-continuation
eljárást egy egyparaméteres eljárásparaméterrel (általában lambda-kifejezéssel) kell meghívni. Az átadott eljárásnak a paramétere egy szökési eljárás, amellyel az átadott eljárásból kiléphetünk.
Ebben a példában a call-with-current-continuation
eljárás egy egyszerű használatát mutatjuk be:
(call-with-current-continuation
(lambda (exit)
(for-each (lambda (x)
(if (negative? x)
(exit x)))
'(54 0 37 -3 245 19))
#t))
Ezen kifejezés értéke a lista első negatív eleme lesz, azaz -3
.
(A for-each
eljárás az első paraméterként adott eljárást hívja meg sorban a második paraméterében adott lista minden elemére.) Itt egyszerűen egy strukturált kilépésként használtuk a szökési eljárást. Mivel ez a folytatás egyenrangú adatnak számít a Scheme-ben, ezért visszatérési értékként használhatjuk, eltárolhatjuk, esetleg később többször is meghívhatjuk mint bármelyik más függvényt: ezzel többféle, a szokástól eltérő vezérlési struktúrát hozhatunk létre.