IS-LISP

File-név: LISP.EXT
Program neve: IS-LISP 0.6
Intelligent Software - 1984
rendszerbővítő, LISP programozási nyelv

A LISP (LISt Processor) nyelvet az 1950-es években alkották meg elsősorban listafeldolgozás és a mesterséges intelligencia kutatások elősegítésére. Bár azóta sok eszteredő eltelt, a LISP ma is Igen népszerű a programozók körében. Ezért (is) szeretnénk az IS-LISP-et bemutatni olvasóinknak.
A Lisp név az angol "List Processing" (lista feldolgozás) kifejezésre vezethető vissza. (Maga lisp szó angolul pöszét, pöszítést jelent.) A Lisp nyelvek fő adatstruktúrája ugyanis a láncolt lista. Az alapvető listakezelő műveletek az összes nyelvjárásban megegyeznek. Emellett közös sajátosság a gyenge, futásidejű típusosság, a funkcionális programozásra jellemző jegyek, és az, hogy a programkód adatként manipulálható.
Tetszőleges Lisp nyelven írt program azonnal felismerhető a jellegzetes szintaktikájáról. A programkód ui. nem más egymásba ágyazott listák, azaz zárójelezett, ún. S-kifejezések (S-expression, sexp) sorozata. Ennek a primitív szintaktikának köszönhetően a Lisp nyelveken írt programokhoz nagyon egyszerű elemzőt (parser-t) és kódot generáló (meta-) programokat írni, ugyanakkor emberi szemmel könnyű elveszni a nyitó- és csukózárójelek erdejében. A könnyű elemezhetőség volt az egyik oka a nyelv népszerűségének a 70-es években, amikor még nem volt elengendő számítási kapacitás összetett, többmenetes fordító- és értelmezőprogramok futtatásához.
Az 1958-ban készített első formális specifikációjával a Lisp a második legöregebb magasszintű programozási nyelv (a Fortran után). Megalkotása óta az elmúlt közel 50 évben a nyelv sokat változott, és számos nyelvjárással gazdagodott. Ma a legelterjedtebb változatai az általános célú Common Lisp és a Scheme nyelvek.
A LISP tanulmányozásához, valamint az alapok elsajátításához magyar nyelven Zimányi Magdolna - Kálmán László - Fadgyas Tibor: A LISP programozási nyelv (Műszaki Könyvkiadó, 1989) című könyvét ajánljuk.

Szintakszis
A Lisp kifejezésorientált nyelv. Ez annyit tesz, hogy a legtöbb nyelvvel ellentétben a Lispben nincs különbség "kifejezések" és "parancsok" között, minden adatot és utasítást kifejezések formájában írunk le. Amikor az értelmező kiértékel egy kifejezést, eredményként egy értéket (vagy értékek listáját) kapunk, amely aztán beágyazható újabb kifejezésekbe, vagy akár kifejezésként maga is kiértékelhető.
McCarthy 1958-as cikke két kifejezéstípust különböztetett meg: Az S-kifejezéseket (S-expression, sexp), vagyis szimbolikus kifejezéseket, és az M-kifejezéseket, vagyis meta kifejezéseket, amelyek S-kifejezéseket alakítanak át újabb S-kifejezésekké. Ez utóbbi típus azonban nem talált támogatókra, és a legtöbb mai Lisp változat S-kifejezéseket használ mind az adatok, mind a kód manipulálásához.
Sokan kritizálták a zárójelek túltengését az S-kifejezésekben (ezt tükrözi a Lisp mozaikszó ironikus kifejtése, a "Lots of Irritating Superfluous Parentheses", azaz "rengeteg irritáló és felesleges zárójel" is), de az igazság az, hogy az egyszerű, rendkívül szabályos szintaktika adja a Lisp legfőbb erejét: a lehetőséget, hogy a kódot adatként kezeljük.
Az, hogy mindent kifejezésekkel írunk le, nagy rugalmasságot biztosít a nyelvnek. Mivel a Lispben magukat a függvényeket is listákként definiáljuk, akként is tudjuk kezelni őket, és nem okoz technikai nehézséget programot generáló programok írása. (Ezt hívjuk metaprogramozásnak.) Számos Lisp nyelvjárás ki is aknázza ezt a tulajdonságot, és lehetővé teszi makrók készítését, amelyek tkp. függvényeket generáló, paraméterezhető függvények.
A Lisp-ben egy listát zárójelekkel határolunk, az elemeket szóközzel választjuk el egymástól:

(1 2 "izé")

Ezen lista elemei az 1, 2, és az "izé" értékek. Az értékek típusa implicite adott (azaz nem kell - nem is lehet - külön megadni a típusukat): az első kettő egész, a harmadik pedig egy füzér. Az üres listát a () kódsorozattal, vagy a nil kulcsszóval írhatjuk le.
A kifejezéseket szintén lista alakban írjuk, mégpedig prefix jelölést használva. A lista legelső eleme egy művelet (angolul form), azaz egy függvény, operátor, makró, stb. neve. A lista további elemei adják a művelet argumentumait. A list függvény például visszaadja az argumentumaiból alkotott listát, tehát a

(list 1 2 "izé")

kifejezés az (1 2 "izé") értékké egyszerűsödik. Függvény- vagy operátor-művelet esetén az argumentumok a művelet elvégzése előtt rekurzíve kiértékelődnek, értékük behelyettesítődik az eredeti kifejezésbe, amely csak ezután értékelődik ki, például a

(list 1 2 (list 3 4))

kifejezés értéke (1 2 (3 4)). (Az elemi, tovább már nem egyszerűsíthető értékek, mint az 1, 2, és az "izé" mindig önmagukat adják eredményül.) Vegyük észre, hogy a lista harmadik eleme egy másik lista, a listák tehát egymásba ágyazhatóak.
A számtani műveletek hasonlóan működnek (itt szokatlan lehet a prefix jelölés):

(+ 1 2 3 4)

értéke 10.
Bizonyos rendhagyó műveletek (special form-ok) vezérlési szerkezetként szolgálnak. Az if művelet például három argumentumot vár. Ha az első argumentum nem az üres listát (azaz nem a nil értéket) adja eredményül, akkor a második, ellenkező esetben a harmadik argumentumát értékeli ki. Így tehát a

(if nil
      (list 1 2 "izé")
   (list 3 4 "bigyó"))

eredménye (3 4 "bigyó"). Vegyük észre, hogy itt az argumentumok nem értékelődnek ki a művelet elvégzése előtt, mivel nem függvényhívásról vagy operátor alkalmazásáról van szó. Jelen esetben az sem okozna tehát gondot, ha az akkor ágon hibát okozó kifejezés állna.
Egy másik rendhagyó művelet, a defun függvények definiálására szolgál. Argumentumai rendre a definiálni kívánt függvény neve, a névleges argumentumainak listája, valamint a függvény törzsét alkotó kifejezés (vagy ezek listája), amelynek értéke egyúttal a függvény eredménye is:

(defun miez (a b)
      (list 1 a 3 b))

függvénydefiníció után a (miez 2 4) kifejezés értéke (1 2 3 4) lesz.

Párok és listák
A Lisp nyelvben a listák egyszeresen láncolt listák. A lista celláit cons celláknak (sokszor egyszerűen pároknak) nevezzük. Egy cons celle két mutatóból áll, amelyeket car-nak és cdr-nek hívunk. Ezen elnevezések eredete az első Lisp megvalósításig nyúlik vissza, az akkori IBM számítógépeken ugyanis így nevezték a Lisp értelmező által használt hivatkozásfeloldó műveleteket (amelyek elérték a mutatók átal megcímzett memóriaterületet). A cons cellákból tetszőleges adatszerkezet építhető, a leggyakoribb azonban a szabályos lista, amelynek car mutatója a lista első elemére, cdr mutatója pedig a farkára, tehát kivétel nélkül vagy a nil szimbólumra vagy egy másik, egy elemmel rövidebb szabályos listára mutat.
Mint látjuk, a listák nem atomi értékek, hanem cons cellák láncolt sorozatai. Egy listára hivatkozó változó nem más, mint a lista első cons cellájára hivatkozó mutató. Egy lista bejárása a cdr függvény sorozatos meghívásával lehetséges, amellyel tehát a listát alkotó cons cellákat látogatjuk sorra.
Egy zárójelezett S-kifejezés egy ilyen láncolt listát definiál. Ugyanazon lista ábrázolására S-kifejezésként számos lehetőség kínálkozik. Egy cons cellát felírhatunk az ún. pontozott pár jelöléssel (a . b) alakban, ahol a a car, míg b a cdr mutató. Ezt az alakot hívjuk kanonikus alaknak. Egy ebben az alakban felírt lista a (1 . (2 . (3 . (4 . nil)))) formát ölti. Ennek az egyszerűsített írásmódja a (1 2 3 4) forma. A két alak keverhető is, azaz ugyanezt a listát felírhatjuk (1 2 . (3 4)) vagy (1 2 3 4 . nil) alakban is. A pontozott pár alak arra is lehetőséget ad, hogy nem szabályos listát alkotó cons cellákat hozzunk létre: (1 2 . 3): itt az első cella cdr mezője ugyan a második cellára mutat, a másodiké viszont a 3 értékre.
A párok sokrétű felhasználhatóságának köszönhetően elterjedt, de téves vélekedés, hogy a Lisp-ben nincs is más adatstruktúra. Valójában azonban a legtöbb Lisp megvalósítás tartalmaz más struktúrákat is, mint pl. a vektorok (vagy tömbök), hash táblák, stb.

Megosztott struktúra
A Lisp listák, lévén hogy egyszerű láncolt listák, tartalmazhatnak közös, megosztott szakaszokat. Nevezetesen, két (vagy több) listának lehet ugyanaz a farka. Például az alábbi kód hatására

(setq ize (list 1 2 3))
(setq bigyo (cons 0 (cdr ize)))

az ize és bigyo változók rendre az (1 2 3) és a (0 2 3) értéket veszik fel. A (2 3) szakasz közös, ha ez valamilyen módon megváltoztatnánk, mindkét lista értéke megváltozna. Ez a megosztás nagyon kedvező hatással van a teljesítményre és a programok memóriaigényére, ugyanakkor könnyen válhat hibák forrásává, ha olyan függvényeket használunk, amelyek desktruktív módon manipulálják a listákat.
A tisztán funkcionális programozás hívei éppen ezért elkerülik a destruktív függvények használatát. A Scheme-ben, amely a Lisp funkcionális jellegére nagy hangsúlyt fektet, a desktruktív függvények neve egy-egy figyelemfelkeltő felkiáltójelben végződik, mint pl. set-car!, amely felülírja egy pár első tagját. A Common Lisp nyelvjárásban a desktruktív függvények jóval gyakoribbak, sőt, a nyelv definiál egy rendhagyó műveletet (a setf-et) is, amely desktruktív függvények létrehozását és használatát hivatott megkönnyíteni.

Önazonos kifejezések és az idézés
Amikor egy kifejezést kiértékelünk, egy másik, többnyire egyszerűbb kifejezést, értéket kapunk. A (+ 2 3) kifejezés például 5-té egyszerűsödik. Bizonyos kifejezések azonban saját magukká értékelődnek ki. Ilyenek a már említett egészek és a füzérek, de ilyen a nil kulcsszó is.
A Lisp-ben lehetőségünk van arra is, hogy egy értéket megvédjünk a kiértékeléstől. Erre szolgál az idézés, azaz a quote rendhagyó művelet, amelyet röviden egy felülvesszővel (´) is jelezhetünk. Ha például megpróbáljuk kiértékelni az ize kifejezést, akkor vagy az ilyen nevű változó értékét kapjuk, vagy hibát, ha nincs ilyen változó. Ha magát az ize szimbólumot akarjuk felírni, akkor idézetbe kell tennünk: (quote ize) vagy egyszerűen ´ize. Ezzel már könnyen tudunk a nil-hez hasonló önazonos neveket definiálni:

(setq ize ´ize)

Ezután akárhányszor értékeljük ki az ize kifejezést (vagy ennek eredményét), mindig önmagát fogjuk kapni.
A makrókat lehetővé tevő nyelvjárások ennél bonyolultabb idézőjeleket is definiálnak, mint pl. a részleges idézést (backqoute vagy quasiquote, jele a hátravessző, `). Ennek hatása majdnem azonos a hagyományos idézéssel, azt kivéve, hogy egy vesszővel lehetőség van visszaváltani idézett módból kiértékelendő módba egy-egy részkifejezés erejéig, ezzel kényelmesen változókat ágyazhatunk egy nagyobb idézett kifejezésbe.
Az önazonos és az idézett kifejezések alkotják a Lispben a konstansokat. (Bár ez az elnevezés nem pontos, mert a gyakorlatban lehetőség van ezek értékének megváltoztatására is, amely komoly félreértésekre és zavarokra adhat okot. Elképzelhető például, hogy az ´ize kifejezés értéke mégsem ize, hanem valami más lesz. Ez mindenképp kerülendő.)

A programkód listaszerkezete
Az eddigi példákban szereplő karaktersorozatok szigorúan véve nem Lisp programok, csak azok írott reprezentációi. Amikor beírjuk őket a Lisp értelmezőbe, az elemző (a Lisp esetében a read függvény) azonnal átalakítja őket láncolt listákká és fa struktúrákká. A Lisp makrók ezeken a struktúrákon, nem pedig az írott reprezentáción manipulálnak. A legtöbb más nyelvnél az elemző kimenete szigorúan belső, csak a fordító- vagy értelmezőprogram által elérhető adat. A C-ben ugyan vannak makrók, ám ezek a programszöveg előfeldolgozása (preprocesszálása) során aktiválódnak, mielőtt az elemző megkezdené a munkáját.
Egyszerű Lisp megvalósításokban az értelmező közvetlenül ezt a belső formát dolgozza fel a program futtatásához, azonban a legtöbb létező implementáció a hatékonyság érdekében futtatás előtt egy alacsonyabb szintű byte-kódra (adott esetben gépi kódra) fordítja a függvényeket.

Kiértékelés és a OKK (REP)-ciklus
A Lisp rendszereket gyakran egy interaktív parancssoron keresztül vezéreljük, amelyet adott esetben kiegészíthet egy integrált fejlesztői környezet (IDE). A felhasználó vagy közvetlenül a parancssorba írja be a kifejezéseket, vagy utasítja a környezetet, hogy tegye meg ezt helyette (mondjuk egy állományból). A Lisp értelmező először beolvassa az adott kifejezést, ezután kiértékeli azt, végül kiírja az eredményt. Ennek megfelelően a Lisp parancssori végrehajtást gyakran "olvasás-kiértékelés-kiírás" vagyis OKK-ciklusnak, angolul "read-eval-print-loop"-nak, REPL-nek hívjuk.
A ciklus három ütemének egy-egy alapvető Lisp függvényt lehet megfeleltetni.
A read függvény beolvassa egy S-kifejezés írott reprezentációját, eredményként annak belső ábrázolását adja. Ha például bemenetként a (+ 1 2) szöveget kapja, egy listát ad eredményül, amelynek első eleme a + szimbólum, második és harmadik eleme pedig rendre az 1 és 2 egészek. Ez történetesen érvényes programkód is, azaz kiértékelhető, mert az első helyen álló szimbólum egy operátor, amely alkalmazható a további elemekre, mint argumentumokra.
Az eval függvény kiértékeli az argumentumaként kapott listaszerkezetet, egy újabb struktúrát adva eredményül. Ez technikailag nem feltétlenül értelmezést jelent, mivel a Lisp megvalósítások programkódot először gyakran gépi kóddá fordítják, majd az így kapott kód futtatását a processzorra bízzák. A gyakorlatban azonban kényelmes értelmezésként gondolni rá: elsőként rekurzíve kiértékelődnek az argumentumok, majd ezek értékére alkalmazzuk a lista fejében hivatkozott függvényt vagy operátort, jelen esetben az összeadást. Az eredményként kapott érték (itt 3) a teljes kiértékelés végeredménye is egyben.
A print függvény feladata az argumentuma megjelenítése a felhasználónak. Egyszerű értékek esetén ez a feladat triviális, bonyolultabb struktúrák, listák esetében azonban a teljes struktúra bejárását igényli.
Ezen három függvény megléte esetén egy primitív OKK-ciklus írása nagyon egyszerű:

(loop (print (eval (read))))

ahol a loop rendhagyó művelet újra és újra kiértékeli az argumentumaként kapott kifejezést, a végtelenségig.

Az IS-LISP sajátosságai
A változók nevei betűkből, számjegyekből, valamint speciális karakterekből állhatnak (ezeket képes változó, ill. függvénynévként értelmezni). A szögletes zárójel (]) a "nagyzárójel" szerepét tölti be (az összes nyitott nyitózárójelet lezárja).
A szövegszerkesztő segítségével készíthetjük LISP "programjainkat" (amelyek gyakorlatilag függvények, melyek által szolgáltatott eredmények újabb függvények paramétereiként jelenhetnek meg). A már lefordított (értelmezett) függvények újraszerkesztését, alakítását a
(FEDIT név)
utasítás segítségével tehetjük meg, ahol név a függvény neve.
Az így szerkesztett programban azonban már a név helyén LAMBDA szó áll (ez a már korábban ismertté vált, már értelmezett utasítás belső jelzésére szolgál). A szerkesztés végét az ESC billentyűvel közölhetjük a programmal, amely a módosításnak megfelelően újrafordítja azt.

Programok kimentése, betöltése
A LISP-rendszer bővítése után kapott új állapotot (függvények, változók) elmentve, a későbbiek során ettől az adott szinttől folytathatjuk munkánkat. A mentést a
(SAVE "név")
utasítással, a visszatöltést pedig
(LOAD "név")
segítségével tehetjük meg.

IS-LISP függvények
A következőkben a beépített függvényeket soroljuk fel, megadva a hívási paramétereiket, valamint a típusukat. Ez a felsorolás lehetőség szerint az irodalomtól való eltérést mutatja.
A típusok a következőek lehetnek:
subr - olyan függvény amely kiértékeli az összes argumentumát (paraméterét).
fsubr - az összes argumentum kiértékelése nem kötelező.
id - speciális LISP azonosító.
var - a rendszerben használható (beépített) változó.

Fügvény
szolgáltatott érték
leírás
típus
(ABS szám1)szám1 szám1=abs(szám) abszolútérték
subr
(ADD1 szám1) szám2 szám2=szám1+1
subr
(APPEND lista1 lista2) kista lista=list1+lista2
subr
(AT sor oszlop) NIL a kurzort a sor, oszlop helyre mozgatja
(ATOM x) T vagy NIL T (igaz) értéket ad ha az x atom, nem lista
subr
(BALNK) space a space karaktert tárolja
id
(BAND2 szám1 szám2) szám szám=(szám1 AND szám2)
subr
(BEAM kifejezés) NIL
Amennyiben a kifejezés nem NIL, akkor a rajzolás az adott videocsatornán bekapcsolódik, egyébként kikapcsolt lesz
subr
(BNOT szám) szám bitenként negál
subr
(BOR2 szám1 szám2) szám bitenkénti VAGY művelet
subr
(BORDER szám) NIL a keretszínt állítja be
subr
(BXOR@ szám1 szám2) szám szám=szám1 XOR szám2
subr
(CAPTURE régi új) NIL átirányítja a régi csatornáról történő olvasási műveleteket az új csatornára
(CHARACTER szám) betű a számnak megfelelő ASCII karaktert adja
subr
(CHARP x) T vagy NIL T ha a paraméter változó, egyébként NIL
subr
(CHARS par) szám a par hosszát adja
subr
(CLEAR) NIL képernyőtörlés
subr
(CLOSE szám) szám
Ha a szám egy érvényes csatorna száma, amelyik megnyitott file, úgy a file lezárásra kerül
subr
(CODEP x) T vagy NIL T ha x kódpointer, egyébként NIL
subr
(COMMENT szöveg) NIL megjegyzés
fsubr
(CREATE csatorna név) szám név file megnyitása
subr
(CURSOR kif) NIL kurzor ki-be
subr
(DEFVIDEO mód gmód gszín) NIL
szöveges és grafikus paraméterek beállítása (mód text-hez 40 vagy 80; gmód = video mode; gszín=video color)
subr
(DEL csatorna) csatorna Az adott EXOS csatorna lezárása
subr
(DIGIT x) T vagy NIL T ha x neve számmal kezdődik, egyébként NIL
subr
(EDIT kif) a kif szerkesztése
subr
(EOF) T vagy NIL teszteli az aktuális input csatorna állapotát
subr
(ERROR szám) hibagenerálás
subr
(FKEY kszám szöveg) NIL funkcióbillentyű definiálása
subr
(FLATTEN struktúra) lista Eltávolítja a struktúrából a leágazásokat, így az eredménye egy lista
subr
(FLUSH csatorna) NIL Kiüríti az adott számú csatornát
subr
(GETCHAR) egy karakter beolvasása
subr
(GRAPHICS) NIL grafikus megjelenítés
subr
(GRS csatorna) szám A grafikus csatorna beállítása az előző érték visszaküldésével
subr
(IN ioport) szám portról olvas
subr
(INK szín) NIL Tintaszín a kiválasztott palettáról)
subr
(JOY szám) szám A botkormány leolvasása
subr
(LITER x) T vagy NIL T ha x neve betűvel kezdődik, NIL ha nem
subr
(MAX2 szám1 szám2) szám visszaadja a nagyobb számot
subr
(MEMBER x y) NIL vagy lista
A visszaadott érték NIL, ha x nem eleme y-nak, egyébként a kapott lista az x elemmel kezdődik
subr
(MESSOFF / MESSON szám) szám
A rendszer által adott üzenetek vezérlése. 1 - szemétgyűjtés eredménye ennyi byte-ot szabadított fel (OFF); 2 - szemétgyűjtések száma (OFF); 4 - hibaszám (ON); 8 - hibakövetés (ON); 64 - a QUOTE függvény vezérlése (ON)
subr
(OBLIST) lista a rendszernevek listában
subr
(OPEN szám file-név) szám file megnyotása
subr
(OUT érték ioport) érték portra ír
subr
(PAINT) NIL Kifestés
subr
(PALETTE c0 c1 ... c7) NIL A palettaszínek beállítása
subr
(PAPER szín) NIL A papír színének beállítása
subr
(PEEK cím) érték A (memória)cím tartalmát adja
subr
(PLIST azonosító) lista Az azonosító tulajdonságait tartalmazó listát adja vissza
subr
(PLOT x y) NIL Abszolút grafikus koordináta megadás
subr
(PLOTMODE szám) NIL Rajzolás vezérlése: 0 - egyszerű rajzolás; 1- OR; 2 - AND; 3 - XOR
subr
(PLOTR x y) NIL Relatív grafikus koordináta megadás
subr
(PLOTSTYLE szám) NIL A rajzolt vonal stílusa állítható be
subr
(POKE cím, érték) érték A (memória)címre az értéket írja
subr
(RANDOM szám) szám Véletlenszámok előállítása
subr
(RANDOMIZE sorszám) sorszám A véletlenszám-generálás vezérlése az adott sorszámtól kezdődik
subr
(RDS csatorna) csatorna Módosítja az adott sorszámú csatornát és visszaadja a megelőző csatornaszámot
subr
(RECLAIM) szám Tárolóhely visszavételt kezdeményez, a kapott számértéket öttel megszorozva a szabad memória mértékét kapjuk
subr
(REDIRECT régi új) NIL A kimeneti műveletek átirányítása a csatornák között
subr
(SETATTRIBUTES szám) NIL Grafikus attribútum beállítása)
subr
(SETCOLOUR szám palettaszín) NIL A palettaszín megadása
subr
(SET-TIME idő-string) NIL Az idő beállítása
subr
(SETVIDEO felbontás színek videox videoy NIL Videómód definiálása
subr
(SNDS csatorna) csatorna Adott csatorna átírása
subr
(SPRINT kifejezés) NIL Formázott kiírás
subr
(TEXT) NIL szöveges megjelenítés
subr
(TIME) szöveg az időt adja vissza
subr
(WRS csatorna) csatorna A kimeneti csatorna átirányítása
subr

Kapcsolat az EXOS-al
A LISP alapjában (így az IS-LISP is) magasszintű pofiramozási nyelv. Ez a tulajdonság azt jelenti, hogy más típusú gépeken futó LISP értelmezők forrásprogramjait is képes (kis számú módosítás után) végrehajtani. Így azonban az ENTERPRISE egyedi tulajdonságai (grafika, hang stb.) háttérbe szorulnak, hiszen más operációs rendszer nem biztos, hogy ugyanúgy kezel mindent, mint az EXOS. Az IS-LISP a EXOS-al való kapcsolat révén jobban kihasználhatóvá teszi a gépközeli (gépfüggő) dolgok felhasználását a programozási munkában. Az EXOS változók elérését az alábbi eljárások (subr típusú függvények) végzik:
(EXOS-READ <változó>), amely az adott EXOS-változó értékét adja vissza;
(EXOS-WRITE <változó> <érték>), amely az adott EXOS-változó átírása után, az új (megváltoztatott) értéket adja vissza;
(EXOSTOGGLE <változó>), amely az adott (lényegében két állapotot meghatározó) változó tartalmát billenti át.

LISP Programozási Segédlet

ENTERPRESS 1992/3, 1992/4)
Vissza