Ugrás a tartalomhoz

Művelet (programozás)

Ellenőrzött
A Wikipédiából, a szabad enciklopédiából

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]
Lásd még: Műveletek (számítógépes programozás) (kategórialap)

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]
  1. Mathematica reference
  2. Maxima reference
  3. Archivált másolat. [2016. március 5-i dátummal az eredetiből archiválva]. (Hozzáférés: 2015. június 11.)
  4. Archivált másolat. [2014. augusztus 26-i dátummal az eredetiből archiválva]. (Hozzáférés: 2015. június 11.)
  5. The Common Lisp HyperSpec
  6. 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.