2013. január 29., kedd

03 - Operátorok, kifejezések, utasítások

Még mielőtt akárminek is nekikezdenék, szeretném jelezni, hogy igazodva a kor igényeihez, ez a bejegyzés 2016. február 26-án újraírásra került. Az eredeti bejegyzés még a Python 2.7.3 használatával készült. Azóta gyakorlatilag az összes jelentős csomag Python 3 kompatibilis verziója elkészült, és magam is napi rendszerességgel Python 3 nyelvű fejlesztéssel foglalkozok.

Ideje folytatni az előző bejegyzésben elkezdett témát. Hogy kerek legyen a történet, foglalkozzunk kicsit az operátorokkal.

Mik azok az operátorok?

Az operátorok olyan elemi műveletek amikre a programozási nyelv képes. Amikor programot írunk, akkor ezeknek az egyszerű műveleteknek a véges sorozatát írjuk le. Ahhoz pedig hogy abból egy jól működő program legyen csupán a logikánkat kell majd használni.

Már az előző bejegyzésben is találkoztunk pár operátorral, ilyen volt a =, vagy a +. Ezek ismerősek lehetnek a matek órákról, nem véletlenül: a legtöbb matematikai művelethez létezik operátor a Pythonban. Az operátoroknak általában két kifejezéssel dolgoznak: az egyik az operátor bal, a másik a jobb odalára kerül. Ezeket a kifejezéseket operandusoknak hívják.

Nem kell a matek szótól hirtelen pánikot kapni, a programozás Pythonban nem egyenletmegoldásokról szól. Részemről, amikor programozok, vannak olyan napjaim, amikor a legbonyolultabb matematikai kifejezés amit írnom kell az egy egyszerű összeadás. A többi napon még annyi sincs.

Aritmetikai operátorok

Aritmetikai műveletek általában azok amiket számokon értelmezünk. Pythonban ez a fogalom jócskán ki van bővítve, mert objektumokon is értelmezhetőek egyes operátorok. Erről majd később beszélünk egyszer.

Lássuk a leggyakoribb operátorokat:

  • =
    Értékadás (assignment). Vele már találkoztunk az előző részben, most akkor beszéljünk kicsit a működéséről is. Az értékadás mindig jobbról-balra történik, azaz a jel jobb oldalán álló kifejezés eredménye bekerül a bal oldalon álló változóba.
  • +
    Összeadás (add). Konstansok, változók értékeit adatjuk össze vele, de összefűzést is jelent (ahogy az előző részben azt be is mutattam).
  • -
    Kivonás (substract). Erről nem is érdemes többet beszélni.
  • *
    Szorzás (multiply).
  • /
    Osztás (divide). Két int osztása egy float-ot eredményez, még akkor is, ha az eredmény amúgy kerek, nem tört (így tehát ez a való osztás, és mellékesen itt implicit típuskonverzió történt). Bár mára ez már történelem, de anno a Python 2-ben ez az operátor mindig int-et eredményezett, tehát szépen lehagyta a törtet az eredményről.
  • //
    Egész osztás (integer division). Ha ennek az operátornak mindkét oldalán int szerepel, akkor az eredménye is int típusú lesz, még akkor is, ha az eredménye amúgy nem egész. Ilyenkor a törtrész elhagyásra kerül. Tehát a 11 // 2 értéke 5. Negatív számoknál kicsit trükkösebb, mert a -11 // 2 eredménye -6. Tehát valójából az érték lefelé, a kisebb érték felé kerekítődik.
  • %
    Maradék képzés (modulo vagy remainder). Ez az operátor azt mondja meg, hogy két tört osztásának mennyi a maradéka. Aki régen osztott papíron az lehet már elfelejtette hogy mi is az pontosan, úgyhogy itt egy példa:

    7'1'3 : 2 = 356
    1 1 
      1 3
        1  <- Ezt a számot kapjuk eredményül

    A színezésre ne figyeljetek
    Mire jó ez az operátor? Például arra, hogy eldöntsük jól egy számról, hogy páros-e vagy páratlan (ha a maradék 1 akkor páratlan, amúgy páros).

  • **
    Hatványozás (power). Az operátor bal oldalán a hatványalap, jobb oldalán a hatványkitevő áll.

A fenti operátorokkal egy csinos kis számológéppé léptettük elé a Pythont.

Logikai operátorok

Mint ismeretes, a számítógépek a bűvös kettes számrendszert használják. Egyesek és nullák, régi történet. A logikai műveletekkel gyakorlatilag egyértelműen eldönthető kérdéseket teszünk fel a számítógépre, amire ő igennel vagy nemmel tud felelni, vagy inkább igazzal vagy hamissal, ami angolul True vagy False, számítosítva meg 1 és 0. Meg is érkeztünk a kettes számrendszerhez.

Az ilyen kérdésekre (hívjuk feltételeknek) kapott válaszokkal tudunk dolgozni. Nézzük milyen operátorok tudnak segíteni ebben:

  • ==
    Egyenlőség (equal). Logikai értéke True ha a két kifejezés egyenlő. Így vizsgálhatjuk hogy egy változó érték egyelnlő-e egy értékkel, és hasonlók.
  • !=
    “Nem egyenlőség” (not equal). Tehát ez az operátor arra ad választ, hogy a bal- és jobb oldala különböző-e.
  • >
    Nagyobb (greater). Avagy a jó öreg kacsacsőr. Akkor eredményez True-t, ha a bal kifejezés nagyobb mint a jobb.
  • <
    Kisebb (less). Akkor eredményez True-t, ha a bal kifejezés kisebb mint a jobb.
  • >=
    Nagyobb egyenlő. Akkor eredményez True-t, a a bal kifejezés nagyobb vagy egyenlő a jobb oldalival. A félreértések elkerülése végett, bár ez két karakter, egyetlen operátort jelöl, tehát nem lehet helyette olyat írni hogy =>, mert az nem létezik.
  • <=
    Kisebb egyenlő. Akkor eredményez True-t, ha a bal oldali kifejezés kisebb vagy egyenlő a jobb oldalival. Ugyan az igaz az írására, mint a >= esetén.
  • or
    “Vagy”. Bizony, ezt normális latin betűkkel, és nem hieroglifákkal kell írni. Akkor eredményez True-t, ha a két oldalán álló kifejezések közül bármelyik True. Ez akkor jön majd jól, ha olyan algoritmust írunk aminek a futása több dologtól is függ, de elég, ha azok közül legalább egy logikailag igaz.
  • and
    “És”. Akkor eredményez True-t, ha mindkét kifejezés az oldalán True. Ez meg majd akkor jön jól, ha olyan algoritmust írunk, aminek a futásához egyszerre mindennek klappolnia kell, tehát mindenkinek logikailag igaz értékkel kell rendelkeznie.
  • not
    “Nem”. A logikai kifejezés tagadása, negációja. A többiekkel ellentétben ez egy egy operandusú művelet, tehát ennek csak a jobb oldalára kell írni kifejezést. True-ra False-t, False-ra True-t eredményez.

Ezzel véget ért egyelőre a felsorolás, de még akadnak más operátorok is a Python nyelvben. Elég csak az előző részben említett index operátorra gondolni. Ezeket az operátorokat a rájuk jellemző adatstruktúrákkal együtt ismertetem, így lehet majd mihez kötni őket.

Ezeket az operátorokat ugyan úgy írhatjuk kifejezések közé, mint az aritmetikaiakat, de ne felejtsük el, hogy ezek mindig valamilyen bool típusú értéket eredményeznek. Lássunk egy rövid példát:

print(1 == 2)
print(1 > 0 and 1 > -1)

Ha ezt lefuttatjuk, akkor először a False majd a True üzenet jelenik meg a konzolban. Ez teljesen logikus, hisz nem igaz hogy az 1 egyenlő lenne a 2-vel, viszont az igaz, hogy az 1 nagyobb mint a 0 és az 1 nagyobb mint a -1.

Igazságtáblák

A logikai operátoroknak elég jól behatárolt, és nagyon kevés bemenete és kimenete létezik, ezért ezekett táblázatokba szokták gyűjteni, ez az igazságtábla. A fenti utolsó 3 operátor igazságtáblái ezek:

or

A B A or B
False False False
False True True
True False True
True True True

and

A B A and B
False False False
False True False
True False False
True True True

not

A not A
False True
True False

Rövidzár

Az and és az or operátorok képesek a rövidzárásra (short circuit). Ez azt jelenti, hogy ha az első operandus alapján biztos a művelet eredménye, akkor a második operandus nem kerül kiértékelésre. Nézzünk néhány példát:

True or print('Második operandus')
False and print('Második operandus')

Ha ezt a programot lefuttatjuk, akkor a konzolban semmi se jelenik meg. A szerint amit feljebb írtam, az első sorban az or első paramétere alapján már biztos hogy True lesz az eredmény, így a futás rá se ugrik a print()-re.
A második sorban, ha az and első operandusa False akkor biztos hogy False lesz az eredmény, és nem is kerül futtatásra a print().

Ez egy fontos tulajdonság, és nagyon hasznos tud lenni olyan szituációkban, amikor olyan kifejezést írunk, amikor tilos a második operandus kiértékelése akkor, ha az első nem teljesült.

Kifejezések

Az előző jó néhány bekezdésben gyakran jelent meg a kifejezés fogalma. Ennyi szöveg után ideje ezt is elamgyarázni.

A kifejezések a forráskód elemi részei. Egyesek egymásba is ágyazhatóak, tehát léteznek összetett kifejezések.

Ahogy leírjuk egy Python fájlban azt hogy:

number = 1 + 2

Úgy valójában 4 kifejezést hoztunk létre:

  1. Vettük az 1 mint konstans kifejezést
  2. Vettük a 2 mint konstans kifejezést
  3. Előállítottunk egy újabb kifejezést úgy, hogy alkalmaztuk az összeadás műveletét a két konstansra.
  4. Az előbb létrejövő kifejezés eredményét (ami az összeadás eredménye) az értékadó operatár eltárolta a number nevű változóban.

Ha ezt realizáltuk, akkor látható, hogy szinte végtelen mélységig pakolgathatóak egymásba a kifejezések, egyáltalán nem kötelező változókba pakolászni részeredményeket. Egy kérdés maradt már csak: ha egy sornyi kifejezést írok le, milyen sorrendben kerülnek kiértékelésre?

Kiértékelési sorrend

A kiértékelési sorrend (order of operations), avagy erősebb kutya baszik, olyan dolog, amivel soha többé nem találkozik az ember az általános iskola után, egész addig, amíg a Facebookon már megint fel nem bukkan egy olyan furmányos talány hogy:

7 - 1 * 0 + 3 / 3 = ?

Az emberek 74%-a hibás eredményt az erre az egyszerű kérdésre.

Akkor most tápoljuk be egy programba:

print(7 - 1 * 0 + 3 / 3)

Az eredmény természetesen 8. Hogy miért? Mert a számítógép soha nem bassza el.

A programozási nyelvekben, így a Pythonban is, a kifejezések kiértékelési sorrendje balról jobbra történik, és bentről kifelé, pont úgy, ahogy a matematikában. Egyes operátorok előnyt élveznek a többiekkel szemben. A sorrend (először a legerősebb, végül a leggyengébb):

  1. **
  2. *, /, //, %
  3. +, -
  4. <, <=, >, >=, !=, ==
  5. not
  6. and
  7. or

Így már teljesen világos hogy miért is 8 az eredmény.

A zárójel

Ahogy a matematikában, úgy a programozásban is előfordul, hogy ki akarjuk kényszeríteni a kifejezések kirétékelési sorrendjét. Ez megszűnteti az olyan problémákat is, mint hogy ki a faszom emlékezett arra, hogy a szorzás előbb következik mint az összeadás.

Zárójelet bárhol alkalmazhatunk a kifejezésben. Írjuk át a fentit hogy egyértelmű legyen:

print(7 - (1 * 0) + (3 / 3))

Ó, hát persze, 1 * 0 az 0, ezt minden hülye tudja!, 3/3 az 1. És akkor 7 - 0 + 1 az 8.

Zárójelet használni semmibe se kerül, cserébe megelőz minden félreértésből adódó problémát. Nem kell spórolni vele.

Önmarágra hivatkozó változó

Kiemelném azt az esetet, amikor az értékadás mindkét odalán ugyan az a változó szerepel.

Mi történik akkor ha leírjuk hogy:

number = 1
number = number + 1

Az 1. sorban csak deklaráltuk a változót. A második sorban viszont a változónak értékül adtuk saját magát úgy, hogy még 1-et hozzá is adtunk. Ez teljesen normális, hisz a jobb oldallal kezdődik a kiértékelés. Ekkor a number egy létező változó, az összeadás értelmezhető rá. A kifejezés értékét (ami 2 lesz) ez után felveszi a number.

Utasítások

Végére maradtak az utasítások. Ezek olyan kifejezések, amikből a forráskód egy sorában egy, és csak az az egy állhat, így tehát nem ágyazhatóak egymásban. Kiértékelésüket tekintve először mindig az utasítás jobb oldala kerül kiértékelésre, és csak legvégül az utasítás maga. Mindig egy operandusúak… már ha beszélhetünk operandusról, a szó szoros értelmében.

Elsőre ez eléggé szigorú megszorításnak tűnik, de ide tartozik az értékadás is. Az értékadás ugyanis nem ágyazható bele semmibe, és egy sorban nem lehet több értékadást egyszerre csinálni még ha úgy is tűnik mintha azt tettük volna.

Egyelőre tehát csak az értékadást, mint utasítást ismerjük, de rövidesen jönnek olyan finomságok, mint az elágazások, ciklusok utasításai, és akkor aztán végre elkezd úgy dolgozni a CPU ahogy kell.

Zárás

Most hogy ennyi mindent megtudtuk arról hogy mennyire jó a Python a matekból a következő részben végre bevonjuk a felhasználót is a játékba!

Ez a bejegyzés a Python tutorialom egyik része. Az összes rész listája itt fellelhető.

-slp

Nincsenek megjegyzések:

Megjegyzés küldése