# Kódolási stílus – javaslatok

Czirkos Zoltán · 2023.01.13.

Megjegyzések és javaslatok a szépen írt, áttekinthető programokhoz.

Érdemes szem előtt tartani a következő dolgokat.

### Olvashatóság – alapok

- Írjunk jól olvasható kódot. Ennek a legegyszerűbb módja azt írni, amit gondolunk. Ne trükközzünk a nyelvi elemekkel a szép, egyszerű programok írása helyett!
- Ne optimalizáljunk feleslegesen. Csak akkor, ha kiderül, hogy lassú a programunk. Különösen ne végezzünk olyan mikrooptimalizációkat kézzel, pl. `%2` helyett `&1` rossz ötlet.
- Törekedjünk arra, hogy a programkódunk (a feladat megoldása) minél jobban hasonlítson a feladat szövegére. Ne vigyünk bele a megoldásba felesleges csavarokat! Pl. ha 1-től 100-ig ki kell írni a számokat, a szép megoldás ez:

      i = 1
      while i <= 100:
      	print(i)
      	i += 1

  A működő, de nem annyira szerencsés megoldások pedig:

      i = 1
      while i < 101:	# hol a „101” a feladat szövegében?
      	print(i)
      	i += 1

      i = 0
      while i < 100:	# hol a „+1” a feladat szövegében?
      	print(i + 1)
      	i += 1

  Vagy pl. „8 osztója van”:

      if osztok_szama(x) == 8:

  Nem annyira szerencsés:

      if osztok_szama(x) - 8 == 0: # miért?!

  Ezek ZH-kból származó példák voltak.
- Használjunk néven nevezett konstansokat és felsorolt típusokat mágikus számok helyett! Nem szerencsés: `if evszak == 2`. Jobb ennél: `if evszak == NYAR`.
- Használjunk beszédes változóneveket, és használjunk függvényeket a részfeladatokhoz! Például: mit csinál a következő program?

      d = 2
      a = 0
      while a < 20:
      	b = 1
      	c = 2
      	while b and c <= d/2:
      		if d % c == 0:
      			b = 0
      		c += 1
      	if b:
      		print(d)
      		a += 1
      	d += 1

  Megoldás

  Ugyanazt, mint ez:

      def prim_e(szam):
          for oszto in range(2, szam//2 + 1):
              if szam % oszto == 0:
                  return False
          return True

      def main():
          vizsgalt = 2
          db = 0
          while db < 20:
              if prim_e(vizsgalt):
                  print(vizsgalt)
                  db += 1
              vizsgalt += 1

      main()

### Karbantarthatóság – a program struktúrája

- Soha, de soha ne copy-pasteljünk kódot! A copy-pastelt kód helyett használjunk ciklust, listát vagy függvényt a feladattól függően.
- Függvény – hacsak nem ez a feladata – nem csinál I/O-t. Azért lett függvény, hogy paraméterekben kapjon és visszatérési értékben adjon vissza dolgokat. Az I/O a hívó és a felhasználói felület dolga. Az esetleges diagnosztikai kiíratások persze lehetnek kivételek, de az is inkább egy naplózó keretrendszer használatával (vagy pl. `if DEBUG`-okkal védve) javasolt.
- Függvény megírásánál mindig, külön kérés nélkül feltételeznünk kell azt, hogy többször is meg fogják hívni. Így a függvénynek nem lehet felesleges mellékhatása. Például egy kártyapaklit keverő függvénynek valószínűleg nem dolga `random.seed`-et hívni.
- Szintén függvények: ne éljünk olyan előfeltételezésekkel, amelyekről a feladat nem ír, vagy amelyek a hívótól nem várhatóak el. Például ha a függvényünk dolga az, hogy megszámlálja egy szöveg magánhangzóit, és azok darabszámait betegye egy listába, akkor ennek dolga létrehozni a listát.
- Függvény ne hívjon kilépéssel, hasonlókkal kapcsolatos dolgokat, mert a felsőbb szintű kódot ez meglepetéssel fogja érinteni (fájlok lezárása, takarítás elmarad). Ha szükséges, akkor legyen a programnak egy `fatal_error()` függvénye, azt hívjuk.
- Alakítsunk ki értelmes adatszerkezeteket. Fogjuk össze osztályba az összetartozó adatokat. Például `szelesseg, magassag, elem` tipikusan egy `class Palya` nevű típusba való. Gondoljunk arra, hogy legjobb a feladatunkban megjelenő mindenféle entitáshoz egy külön típust rendelnünk a programban.
- Legyen egyértelmű specifikáció arról, hogy mik a paraméterek és kinek a felelősége ellenőrizni a bemenő paraméterek értelmességét.

### Nyelvi eszközök

- Ne írjunk a feltételekhez üres igaz ágat, inkább tagadjuk a feltételt! Tehát ehelyett:

      if x < 0:
          pass   # nem kellene ezt megfordítani?
      else:
          print("nem negatív")

  Inkább írjuk ezt:

      if x >= 0:
          print("nem negatív")

- Ne használjunk `break`-et, `continue`-t feleslegesen. Egy-két speciális esettől eltekintve szebb kódot lehet írni nélkülük.
- Ne írjunk loop-switch szekvenciákat (loop-switch sequence, for-case paradigm). Így nevezzük azt, amikor egymás után következő műveletek kerülnek feleslegesen ciklusba, hogy aztán azon belül esetszétválasztással kelljen újra szétválasztani őket. Pl. a `+----+` karaktersorozat kiírásához, nem túl jó megoldás:

      for i in range(0, n + 1):
          if i == 0 or i == n:
              print("+", end="")
          else:
              print("-", end="")

  Érthetőbben ugyanaz:

      print("+", end="")
      for i in range(1, n):
          print("-", end="")
      print("+");

  Az előbbi nem fejezi ki az egymásutániságot, az utóbbin viszont ránézésre látszik, hogy előbb plusz van, utána mínuszok, végül megint egy plusz.
- Hiába van rá mód, lehetőleg ne keverjük a logikai és az aritmetikai kifejezéseket! Például ha számokat vizsgálunk, igenis írjuk ki az `== 0`-t vagy a `!= 0`-t a feltételekben.
- Használjuk a `bool` típust, ahol logikai értékekkel dolgozunk!
- Ne bonyolítsuk a logikai függvényeket redundáns `if ... return True else return False` vezérlési szerkezettel. Helyes, rövid, egyszerű:

      def paros_e(szam):
          return szam % 2 == 0

  Nem nyújt többet, viszont sokkal hosszabb:

      def paros_e(szam):
          if szam % 2 == 0:  # „ha igaz, akkor igaz, ha hamis, akkor hamis”
              return True
          else:
              return False

  Ugyanez a helyzet a hívás helyén. Helyes, rövid, egyszerű:

      if paros_e(x):
      	...

  Nem nyújt többet, viszont sokkal hosszabb:

      if paros_e(x) == True:
      	...

- Figyeljünk arra, hogy a függvényeinknek minden ágon ugyanaz legyen a visszatérési típusa! Helytelen:

      def van_osztoja(szam):
          for oszto in range(2, szam//2 + 1):
              if szam % oszto == 0:
                  return True

  Ez a függvény `None`-t ad vissza, ha a számnak nincs osztója. Helyesen:

      def van_osztoja(szam):
          for oszto in range(2, szam//2 + 1):
              if szam % oszto == 0:
                  return True
          return False

  Így már mindig `bool` a visszatérési érték.