Művelet (programozás)
A programozási nyelvek rendszerint támogatnak műveleteket: olyan nyelvi konstrukciókat, amelyek általánosságban a függvényekhez hasonlóan viselkednek, de szintaktikailag vagy szemantikailag eltérnek a szokásos függvényektől. A legegyszerűbb általános példák között vannak az aritmetikai (az összeadásra +
, összehasonlításra >
) és logikai műveletek (mint AND
vagy más nyelveken &&
). Összetettebb példaként megemlíthető az értékadás (rendszerint =
vagy :=
), tag hivatkozása egy rekordban vagy egy objektumban (rendszerint .
), és a hatókör-feloldás művelet (leggyakrabban ::
). A programozási nyelvek általában tartalmaznak egy sor beépített műveletet, és néhány nyelv lehetővé teszi a felhasználó által definiált műveletek használatát is.
Szintaxis
[szerkesztés]Szintaktikailag a műveletek rendszerint különböznek a függvényektől. A legtöbb programnyelvben, a függvények egy speciális formátumú prefix műveletként értelmezhetőek, adott precedencia szinttel és asszociativitással, gyakran kötelező zárójelezést használva, pl. Func(a)
(vagy (Func a)
a LISP programozási nyelvben). A programozási nyelvek többsége támogatja a programozó által definiált függvények használatát, de ez önmagában nem teszi lehetővé a programozó által definiált műveletek használatát, ha azokhoz nem elegendő a prefix írásmód és az egyetlen (nyelvfüggő) precedencia szint. Szemantikailag a műveleteket úgy tekinthetjük, mint speciális formájú függvényeket, eltérő hívási írásmóddal és korlátozott számú paraméterrel (rendszerint 1 vagy 2).
A műveleti jel elhelyezkedése az operandusokhoz képest lehet prefix (Lengyel írásmód - operandusok előtti műveleti jel), infix (operandusok közötti műveleti jel) vagy posztfix (operandusok utáni műveleti jel), és egy műveleteket tartalmazó kifejezés szintaxisa függ a műveletek aritásától (az operandusok számától), a precedenciájuktól, és (ha értelmezhető) az asszociativitásuktól. A legtöbb programozási nyelv a kétváltozós (bináris) műveleteket és néhány egyváltozós (unáris) műveletet támogat, ritkán több operandusos műveletet is, mint például a ?:
művelet a C nyelvben, amely háromváltozós (ternáris). Az egyváltozós műveletek általában prefix írásmódúak, mint például az egyváltozós mínusz (negáció) -x
, esetenként posztfix írásmódúak, mint például az utólagos (változó kiértékelését követő) értéknövelés művelet x++
; a kétváltozós műveletek pedig infix írásmódúak, mint például az x + y
vagy az x = y
. A kettőnél több operandust használó infix írásmódú műveletek további szimbólumokat igényelnek, mint például a háromváltozós, infix ?:
művelet a C nyelvben (kóddal írva a ? b : c
) – valójában erre ez az egyetlen közismert példa, emiatt gyakran a háromváltozós infix műveletként is emlegetik. A prefix és a posztfix írásmódú műveletek viszont könnyen használhatóak tetszőleges operandus számmal, például 1 2 3 4 +
.
Néha[1][2] bizonyos nyelvek egyes elemeit „matchfix” vagy „cirkumfix”[3][4] műveletként is említik, hogy egyszerűsítsék a nyelv leírását vagy értelmezését. Egy cirkumfix műveleti jel, két vagy több részből áll (szimbólum párok), amik közrefogják az operandusokat. A cirkumfix műveleteknek a legmagasabb a precedenciája, a tartalmuk először kerül kiértékelésre, és ez az érték kerül felhasználásra a külső kifejezésben. Az említetteknek megfelelő legismertebb cirkumfix művelet a zárójelezés, amellyel azt jelezzük, hogy egy kifejezés mely részei értékelődjenek ki először. Egy másik példa a fizikából a Dirac féle Braket-jelölés, állapotvektorok skaláris szorzatának jelölésére. A cirkumfix műveleti jelek különösen hasznosak olyan műveletek jelölésére, amik sok, vagy változó számú operandust tartalmaznak.
Általában egy programozási nyelv specifikációja megadja a támogatott műveletek szintaxisát, míg az olyan nyelvek – mint például a Prolog – amik támogatják a programozó által definiált műveleteket, megkívánják, hogy a programozó definiálja a szintaxist.
Szemantika
[szerkesztés]A műveletek szemantikája elsősorban az értéktől (jobb vagy bal), a kiértékelés stratégiájától és az argumentumok (operandusok) átadási módjától (érték szerinti, referencia szerinti vagy név szerinti) függ. A nem szokványos kiértékelési stratégia egy példája, a logikai kifejezések rövidzár kiértékelése, ahol a kifejezés tagjaiból csak annyi kerül kiértékelésre, amivel a logikai kifejezés értéke már biztosan megállapítható. Egyszerűen, egy műveleteket tartalmazó kifejezés valamilyen módon kiértékelésre kerül, és aztán az eredmény vagy csupán egy érték lesz (egy jobb-érték), vagy egy értékadást lehetővé tevő objektum (bal-érték).
Egyszerű esetben, ez ugyanolyan mint a szokásos függvényhívás; például az összeadás x + y
általánosságban ekvivalens az összead(x, y)
függvényhívással, a kisebb-mint összehasonlítás x < y
pedig az kisebb_mint(x, y)
hívással, vagyis az argumentmok a szokásos módon kerülnek kiértékelésre, aztán valamilyen (függvény kódja szerinti) kiértékelés történik, aminek az eredménye lesz a visszaadott érték. Mindazonáltal a szemantika lehet jelentősen eltérő is. Például, az a = b
értékadásnál a „cél” a
tag nem kerül kiértékelésre, hanem ehelyett az elhelyezkedése (memóriacíme) kerül felhasználásra b
értékének tárolásához – a referencia szerinti paraméterátadás szemantikájának megfelelően. Továbbá, egy értékadás lehet egy utasítás is (explicit megadott érték nélkül), vagy egy kifejezés (értékkel), ahol az érték maga lehet jobb-érték (csak egy érték) vagy bal-érték (aminek értéket lehet adni). További példa lehet, a hatókör-feloldás művelet ::
és a tag hivatkozás művelet .
(kóddal Foo::Bar
vagy a.b
) amik nem értékekkel végeznek műveleteket, hanem nevekkel, lényegében a név szerinti paraméterátadás szemantikájával, és a kiértékelt eredményük is egy név lesz.
Bal-értékek értékként való használata műveleteket tartalmazó kifejezésekben különösen említésre méltó a tömbök indexelésénél és az unáris inkrementáló és dekrementáló műveleteknél. C nyelvben például a következő utasítások érvényesek és jól definiáltak, és azért használhatóak ebben a formában, mert ezek a műveletek bal-értéket adnak eredményül:
y = ++x++;
x = ++a[i];
Egy fontos felhasználás, amikor egy balra-asszociatív bináris (két operandusú) művelet módosítja a bal oldali operandust (vagy ott mellékhatásokat okoz) és aztán eredményként erre az operandusra értékelődik ki bal-értékként (vagy fordítva, egy jobbra-asszociatív művelet a jobb oldali operandusára vonatkozóan, habár ez ritkább). Ez megengedi műveletek egy olyan sorozatát, amelyek mindannyian hatással vannak az eredeti operandusra, lehetővé téve egy metódus-láncoláshoz hasonló folyó-interfész kialakítását. Ennek egy gyakori példája a <<
művelet a C++ iostream
könyvtárából, amely lehetővé teszi a folyó kimenetet, a következő példa szerint:
cout << "Hello" << " " << "world!" << endl;
Felhasználó által definiált műveletek
[szerkesztés]Egy programnyelv tartalmazhat adott számú beépített műveletet (pl. +, -, *, <, <=, !, =, stb. a C és C++ nyelvekben), vagy lehetővé teheti programozó által definiált műveletek létrehozását (pl. az F#, az OCaml vagy a Haskell). Néhány programnyelvben a műveleti jelek csak speciális karakterek lehetnek, mint például a + vagy a := míg mások megengednek neveket is, mint a div
(pl. a Pascal nyelvben).
A legtöbb nyelv rendelkezik beépített (a nyelv részét képező) műveletek egy csoportjával, de nem teszi lehetővé a felhasználó által definiált műveletek használatát, mivel ez jelentősen megnehezíti a forráskód feldolgozását. Egy új művelet bevezetése megváltoztatja a nyelv lexikális specifikációját, ami miatt módosul a lexikális elemzés. A művelet aritása (az operandusok száma) és precedenciája része lesz a nyelv kifejezés-szintaxisának, ezért megváltozik a kifejezés-szintű elemzés is. Például egy @
művelet hozzáadása, megkívánja ennek a karakternek a lexikai felismerését és tokenizálását, és a kifejezés-struktúra (szintaxis fa) függ majd ennek a műveletnek az aritásától és precedenciájától. A legtöbb nyelv a műveletek használatát csak a beépített adattípusokra engedi meg, de vannak amik megengedik a nyelvben létező műveletek használatát felhasználó által definiált típusokra is; ezt hívjuk művelet túlterhelésnek.
Néhány programnyelv lehetővé teszi új műveletek definiálását fordítási időben, de akár futásidőben is. Ez történhet magának a nyelvnek a használatával, vagy más esetekben meta-programozással (ahol a műveleteket egy külön nyelven specifikáljuk). Új műveletek definiálása (különösen futásidőben), gyakran lehetetlenné teszi a programok korrekt statikus elemzését, mivel a nyelv szintaxisa Turing-komplett lehet (egy Turing-gépen szimulálható) így még magának a szintaxis fának a létrehozása is a leállási probléma megoldását kívánná meg (annak eldöntése, hogy egy program egy adott inputtal véges időn belül leáll-e), ami lehetetlen (tetszőleges program-input párokra a megoldhatatlanságot Alan Turing 1936-ban bizonyította). Ez például előfordul a Perl programnyelv és a Lisp néhány dialektusa esetén.
Példák
[szerkesztés]A függvényektől szintaktikailag eltérő általános példák a matematikai, számokkal végzett műveletek - mint például a >
a „nagyobb mint” műveletre - ahol a műveleti jelek nem felelnek meg a nyelv függvény azonosítókra vonatkozó általános szabályainak és használatuk a függvények hívási szintaxisának. Függvényként a „nagyobb mint” általánosságban egy azonosítóval lenne elnevezve, mint például az nm
vagy a nagyobb_mint
és függvény szintaxissal lenne meghívva is: nagyobb_mint(x, y)
. Ehelyett, a művelet egy speciális karaktert >
használ (ami külön kerül tokenizálásra a lexikális elemzés alatt), és infix írásmódot (a műveleti jel az operandusok között van), a következő módon: x > y
.
Szemantikailag eltérő (a paraméter átadás módjában) általános példák a logikai műveletek, amikre gyakran jellemző a rövidzár kiértékelés: például egy rövidzárral kiértékelt X AND Y
konjunkció esetén csak akkor kerül kiértékelésre a második operandus, ha az első operandus értéke nem „hamis”, eltérően attól, ahogyan a nyelvben a függvényhívások érték-szerinti paraméterátadással történnek (pl. AND(X,Y)
függvényhívás esetén az érték-szerinti paraméterátadás miatt, mindenképpen kiértékelésre kerülne mindkét operandus). Az X AND Y
művelet viselkedése így inkább az if/then/else nyelvi konstrukcióhoz hasonló.
A következő kódpéldában IF ORDER_DATE > "12/31/2011" AND ORDER_DATE < "01/01/2013" THEN CONTINUE ELSE STOP
, a műveletek a >
(nagyobb mint), az AND
(logikai és), és a <
(kisebb mint).
Néhány kevésbé általános művelet:
Fordítás (compilation)
[szerkesztés]A fordító a műveleteket és a függvényeket szubrutin hívásokkal vagy sorosan kifejtett kóddal (ahol minden hívási helyre beillesztésre kerül a teljes hívott kód) valósítja meg a lefordított programban. Néhány a nyelv által támogatott beépített művelet közvetlenül lefordítható kisszámú, a CPU által ismert gépi kódú utasításra, míg másoknak (pl. a string összefűzésre használt +
műveletnek) sokkal összetettebb lehet a megvalósítása.
Művelet túlterhelés
[szerkesztés]Néhány programozási nyelvben egy művelet lehet ad hoc módon polimorfikus, vagyis lehet (különböző) értelmezése többféle adattípusra, (mint például a Java-ban, ahol a +
művelet egyaránt használatos számok összeadására és szövegek összefűzésére is). Az ilyen műveleteket nevezzük túlterheltnek. Az olyan nyelvekben, amik támogatják a programozó általi művelet túlterhelést (mint például a C++) de csak korlátozott számú beépített művelettel rendelkeznek, gyakorta alkalmazzák a művelet túlterhelést, a műveletek egyedi igények szerinti használata érdekében.
Operandus típuskényszerítés
[szerkesztés]Néhány programozási nyelv lehetővé teszi, hogy egy művelet operandusain implicit módon típuskonverzió, vagy típuskényszerítés történjen, olyan cél adattípusra, amely alkalmas az adott művelet elvégzéséhez. Például a Perl típuskényszerítési szabályai szerint a 12 + "3.14"
művelet eredménye 15.14
lesz. Vagyis a szöveges "3.14"
érték, a 3.14 számmá konvertálódik a művelet elvégzése előtt. Továbbá, mivel a 12
egy egész szám, a 3.14
pedig egy lebegőpontos vagy fixpontos ábrázolású szám (olyan szám, amiben vannak tizedesek), az egész szám is implicit konvertálásra kerül lebegőpontos vagy fixpontos ábrázolású számmá. Ezt követően a művelet két lebegőpontos vagy fixpontos ábrázolású szám összeadásaként kerül végrehajtásra, és az eredmény típusa is ilyen lesz.
A Javascript éppen ellenkező típuskonverziós szabályokat követ ugyanennél a kifejezésnél, itt a 12
egész szám a "12"
szöveges értékre konvertálódik, és aztán a művelet két szöveges érték összefűzéseként kerül végrehajtásra, aminek eredménye "123.14"
lesz.
Ha egy nyelvben jelen van a típuskényszerítés, a programozónak figyelemmel kell lennie az operandusok típusait és a műveletek eredmény-típusait érintő szabályokra, hogy megelőzze az ezekből fakadó szövevényes programozási hibákat.
Műveleti képességek programnyelvekben
[szerkesztés]Az alábbi táblázatban láthatóak néhány programnyelv műveletekkel kapcsolatos képességei, jellemzői:
Programozási nyelv | Nem alfanumerikus műveleti jelek | Alfanumerikus műveleti jelek | Prefix | Infix | Posztfix | Precedencia | Asszociativitás | Túlterhelés | Programozó által definiált túlterhelés | Programozó által definiált műveleti jelek |
---|---|---|---|---|---|---|---|---|---|---|
ALGOL 68 | +* ** * / % %* %× - + < <= >= > = /= & -:= +:= *:= /:= %:= %*:= +=: :=: :/=:
(A fentebbi műveleteknek van vastagon szedett alfanumerikus megfelelője a következő oszlopban. Néhánynak lentebb, nincs ASCII megfelelője.) ¬ +× ? ^ ˇ ? ? × ÷ ÷× ÷* ? ? ? ? ? ? ×:= ÷:= ÷×:= ÷*:= %×:= :?: |
not abs arg bin entier leng level odd repr round shorten i shl shr up down lwb upb lt le ge gt eq ne and or over mod elem minusab plusab timesab divab overab modab plusto is isnt | Igen | Igen | Nem | Igen (a prefix műveleteknek mindig 10-es prioritása van) | Az infix műveletek balra-asszociatívak, a prefix műveletek jobbra-asszociatívak | Igen | Igen | Igen |
C | () [] -> . ! ~ ++ -- + - * & / % << >> < <= > <= == != ^ | && || ?: = += -= *= /= %= &= ^= |= <<= >>= | sizeof | Igen | Igen | Igen | Igen | Igen | Igen | Nem | Nem |
C++ (bővebben) | sizeof typeid new delete throw decltype static cast dynamic cast reinterpret cast const cast | Igen | Igen | Igen | Igen | Igen | Igen | Igen | Nem | |
Java | new instanceof | Igen | Igen | Igen | Igen | Igen | Igen | Nem | Nem | |
Haskell | + - * / ^ ^^ ** == /= > < >= <= && || >>= >> $ $! . ++ !! : Sokkal több is van az általános könyvtárakban | A függvények nevét ` jelek közé kell tenni
|
Igen | Igen | Nem | Igen | Igen | Igen, Típus osztályok használatával | Igen | |
Pascal | * / + - = < > <> <= >= := | not div mod and or in | Igen | Igen | Nem | Igen | Igen | Igen | Nem | Nem |
Seed7 | {} [] -> ** ! + - * / << >> & >< | = <> > >= < <= <& := +:= -:= *:= /:= <<:= >>:= &:= @:= | conv varConv parse conj div rem mdiv mod times mult in not and or digits lpad rpad lpad0 | Igen | Igen | Igen | Igen | Igen | Igen | Igen | Igen |
Eiffel | [] + - * / // = /= | not and or jelentése "and then" (és akkor) "or else" (vagy egyébként) | Igen | Igen | Nem | Igen | Igen | Nem | Igen | Igen |
Prolog | :- ?- ; , . =.. = \= < =< >= > == \== - + / * | spy nospy not is mod | Igen | Igen | Igen | Igen | Igen | Nem | Nem | Igen |
Lisp | A Lisp minden függvényt és szimbólumot műveletként definiál.[5] A meghatározás következményeként magának a nyelvnek nincsenek műveletei. | Igen (mivel a műveletek a szokásos függvények) | Nem | Nem | Nem | Nem | Nem | Nem | Igen | |
Smalltalk | (vannak - két karakter hosszúságig[6]) | Az alfanumerikus szimbólumoknál a kulcsszó után vesszőt kell írni | Nem | Igen | Igen | Nem | Nem | Igen | Igen | Igen |
Perl | -> ++ -- ** ! ~ \ + - . =~ !~ * / % < > <= >= == != <=> ~~ & | ^ && || ' // .. ... ?: = += -= *= , => | print sort chmod chdir rand and or not xor lt gt le ge eq ne cmp x | Igen | Igen | Igen | Igen | Igen | Igen | Igen | Nem |
Jegyzetek
[szerkesztés]- ↑ Mathematica reference
- ↑ Maxima reference
- ↑ Archivált másolat. [2016. március 5-i dátummal az eredetiből archiválva]. (Hozzáférés: 2015. június 11.)
- ↑ Archivált másolat. [2014. augusztus 26-i dátummal az eredetiből archiválva]. (Hozzáférés: 2015. június 11.)
- ↑ The Common Lisp HyperSpec
- ↑ Goldberg, Adele: Smalltalk-80: The Language and its Implementation, p. 27, ISBN 0-201-11371-6
Fordítás
[szerkesztés]Ez a szócikk részben vagy egészben a Operator (computer programming) című angol Wikipédia-szócikk ezen változatának fordításán alapul. Az eredeti cikk szerkesztőit annak laptörténete sorolja fel. Ez a jelzés csupán a megfogalmazás eredetét és a szerzői jogokat jelzi, nem szolgál a cikkben szereplő információk forrásmegjelöléseként.