051.1 Lecke 1
Tanúsítvány: |
Open Source Essentials |
---|---|
Verzió: |
1.0 |
Témakör: |
051 Szoftver alapismeretek |
Fejezet: |
051.1 Szoftver komponensek |
Lecke: |
1/1 |
Bevezetés
A szabad (free) és nyílt forráskódú szoftvereket (open source software) — gyakran rövidítik FOSS-nak — mindennapi életünk szerves részévé váltak, többnyire anélkül, hogy ennek tudatában lennénk. A FOSS például minden internetes tevékenységünk mögött megtalálható valamilyen formában: a számítógépen, ahol a böngészőben weboldalakat nézegetünk, vagy a szervereken, amelyek tárolják ezeket a weboldalakat, és amint meghívjuk, azonnal megjelenítik őket.
Mi az a szoftver?
Mielőtt azonban megvizsgálnánk a szabad és nyílt forráskódú szoftverek sajátosságait, tisztáznunk kell, hogy mi is valójában a szoftver. Kezdjük egy nagyon általános leírással: a szoftver bármilyen formában a számítógépek nem fizikai, immateriális része. A szoftver biztosítja, hogy a számítógép fizikai részei (a hardver) kölcsönhatásba lépjenek egymással, és azt, hogy a számítógép képes legyen parancsokat fogadni és feladatokat végrehajtani.
Egy okostelefon, egy laptop vagy egy szerver az adatközpontban csak egy fémből és műanyagból készült gép, ha kikapcsolják. Amint bekapcsolják, elindul a szoftver, olyan kódolt parancssorozatok formájában, amelyek ennek a gépnek az egyes komponenseit vezérlik, amelyek lehetővé teszik, hogy a felhasználó interakcióba lépjen a géppel, és amelyek az egyes alkalmazások meghívásával nagyon specifikus feladatokat hajtanak végre.
A szoftverfejlesztők dolga, hogy elemezzék azokat a feladatokat, amelyeket a számítógépnek végre kell hajtania, és úgy határozzák meg azokat, hogy képes is legyen azokat elvégezni. A fejlesztők által használt eszközök olyan sokfélék és változatosak, mint a szoftver által végrehajtott feladatok.
Egyes szoftveres feladatok nagyon szorosan kapcsolódnak a hardverhez és a számítógép architektúrájához, például a memória címzése és kezelése vagy a különböző processzek (folyamatok) kezelése. A rendszerprogramozók ezért a hardverhez közel dolgoznak.
Az alkalmazásfejlesztők ezzel szemben inkább a felhasználóra összpontosítanak, és olyan alkalmazásokat programoznak, amelyek lehetővé teszik a felhasználók számára, hogy hatékonyan és intuitív módon végezzék feladataikat. Egy összetett alkalmazásra példa lehet egy szövegszerkesztő program, amely a szövegformázáshoz szükséges összes funkciót menükben vagy gombokban biztosítja, és a szöveget úgy is megjeleníti, ahogyan az végül nyomtatásra kerülhet.
Általában az algoritmus egy probléma megoldási módja. Például egy átlag kiszámításához a szokásos algoritmus az, hogy összeadjuk értékek gyűjteményét, és az összeget elosztjuk az értékek teljes számával. Bár az algoritmusokat hagyományosan programozók tervezik és hajtják végre, manapság már a mesterséges intelligencia is generál algoritmusokat.
Az ebben a fejezetben található fogalmak segíthetnek megérteni a FOSS erősségeit és kockázatait, megalapozott döntéseket hozni, és akár azt is eldönteni, hogy szoftverfejlesztővé szeretnénk-e válni.
Programozási nyelvek
A programozási nyelvek erősen strukturált, mesterséges nyelvek, amelyek megmondják a számítógépnek, hogy mit tegyen. A programokat általában szöveges formában írják, de egyes nyelvek grafikus formájúak. A szoftverfejlesztők ezen a mesterséges nyelven írnak utasításokat (úgynevezett kódot) a számítógépnek. A számítógép hardvere azonban nem hajtja végre közvetlenül ezt a kódot. A hardver közvetlenül csak a memóriában tárolt bitminták sorozatát, az úgynevezett gépi kódot vagy gépi nyelvet tudja végrehajtani. Minden programozási nyelvet vagy egy compiler (fordító) alakít át gépi kóddá, vagy egy másik gépi kódú program, az úgynevezett interpreter (értelmező) értelmezi, hogy a hardver végre tudja hajtani ezeket az utasításokat.
A jelenleg legelterjedtebb programozási nyelvek közé tartozik a Python, a JavaScript, a C, a C++, a Java, a C#, a Swift és a PHP. Mindegyik programozási nyelvnek megvannak a maga egyedi erősségei és gyengeségei, a nyelv kiválasztása pedig a projekttől és a fejlesztő igényeitől függ. A Java például népszerű választás nagyvállalati alkalmazások fejlesztésekor, míg a Pythont gyakran használják tudományos számításokhoz és adatelemzéshez.
A fejlesztők lenyűgöző kreativitásról tettek tanúbizonyságot a programozási nyelvek megtervezése során. Eredetileg alacsony szintű nyelvek voltak, amelyek a számítógép utasításaihoz hasonlítottak. A nyelvek egyre inkább magas szintűvé váltak, ami azt jelenti, hogy az utasítások erőteljes kombinációit rövid kifejezésekkel próbálják ábrázolni. Egyes nyelvek az emberek természetes gondolkodásmódját tükrözik, miközben megőrzik a helyes futtatáshoz szükséges szigort.
Jelenleg körülbelül 400 programozási nyelvet ismernek, bár sokat közülük csak nagyon szűk piaccal rendelkező alkalmazásokban vagy legacy (hagyaték) környezetben használnak. Mindegyiket bizonyos feladatok megoldására fejlesztették ki.
A programozási nyelvek karakterisztikája, szintaxisa és felépítése
A programozási nyelv kiválasztása jelentős hatással lehet egy szoftverprojekt teljesítményére, skálázhatóságára és könnyű fejleszthetőségére. Ezek a szakaszok a nyelvek fontos elemeit ismertetik.
A programozási nyelvek karakterisztikája
A programozási nyelvek néhány közös jellemzője és tulajdonsága a következő:
- Párhuzamosság (concurrency)
-
A párhuzamosság több feladat egyidejű kezelését jelenti, akár különböző hardverprocesszorokon történő futtatással, akár egyetlen processzor váltakozó használatával. A programozási nyelv által támogatott párhuzamosság mértéke nagymértékben befolyásolhatja a nyelv teljesítményét és skálázhatóságát, különösen a valós idejű feldolgozás vagy nagy adatmennyiséget igénylő alkalmazások esetében. Az egyes különálló elemeket nevezhetjük folyamatnak (process), feladatnak (task) vagy szálnak (thread).
- Memóriakezelés (memory management)
-
A memóriakezelés a memória kiosztása és felszabadítása egy programban. A nyelvtől vagy a futási környezettől függően a memóriakezelést a programozó végezheti manuálisan, vagy automatikusan. A megfelelő memóriakezelés kulcsfontosságú annak biztosításához, hogy a program hatékonyan használja a memóriát, és hogy az ne fogyjon el, illetve ne okozzon egyéb problémákat. Ha egy program nem szabadítja fel a fel nem használt memóriát, akkor a program memóriaszivárgást (memory leak) okoz, amely fokozatosan növeli a memóriahasználatot, amíg a program össze nem omlik, vagy a teljesítményben negatív hatások nem jelentkeznek.
- Megosztott memória (shared memory)
-
A megosztott memória a folyamatok közötti kommunikációs mechanizmus egy típusa, amely lehetővé teszi, hogy több processz olvassa és kezelje a memória egy közös régióját. A megosztott memória gyakori a hardverekben, például a lemezmeghajtókban és hatékony módja lehet a folyamatok közötti adatmegosztásnak is. A mechanizmus azonban gondos szinkronizálást és kezelést igényel az adatsérülések megelőzése érdekében. A race condition néven ismert hiba akkor lép fel, ha az egyik folyamat váratlanul megváltoztatja az adatokat, miközben egy másik processz használja azokat.
- Üzenettovábbítás (message passing)
-
Az üzenettovábbítás egy olyan kommunikációs mechanizmus a folyamatok között, amely lehetővé teszi számukra az adatcserét és tevékenységeik koordinálását. Ezt általában a párhuzamos programozásban használják a folyamatok közötti kommunikáció megvalósítására, és különböző mechanizmusokon keresztül valósítható meg, például socketekkel, csővezetékekkel (pipe) vagy üzenetsorokkal (message queue).
- Szemétgyűjtés (Garbage collection)
-
A szemétgyűjtés egy automatikus memóriakezelési technika, amelyet egyes programozási nyelvek használnak a folyamat futása közben, a már nem használt memória visszanyerésére. Ez segíthet a memóriaszivárgások megelőzésében, és megkönnyítheti a fejlesztők számára a helyes és hatékony kód írását, de teljesítménybeli többletköltséget is okozhat, és megnehezítheti a program pontos viselkedésének ellenőrzését.
- Adattípusok (data types)
-
Az adattípusok határozzák meg, hogy a programban milyen típusú információ jeleníthető meg. Az adattípusok lehetnek a nyelvben előre meghatározottak vagy felhasználó által definiáltak, és lehetnek egész számok, lebegőpontos számok (azaz a valós számok közelítései), sztringek, tömbök és egyebek.
- Bemenet és kimenet (input és output) (I/O)
-
A bemenet és a kimenet olyan mechanizmusok, amelyekkel adatokat olvashatunk és írhatunk egy programba, illetve egy programból. A bemenetnek különböző forrásai lehetnek, például felhasználói kattintások és billentyűleütések, egy fájl vagy hálózati kapcsolat, míg a kimenet különböző célállomásokra küldhető, például egy kijelzőre, egy fájlba vagy egy hálózati kapcsolatra. Az I/O lehetővé teszi a programok számára, hogy kölcsönhatásba lépjenek a külvilággal, és információt cseréljenek más rendszerekkel.
- Hibakezelés (error handling)
-
A hibakezelés a program végrehajtása során fellépő hibákat észleli, és reagál azokra. Ide tartoznak az olyan hibák, mint a nullával való osztás vagy a nem talált kért fájl. A hibakezelés lehetővé teszi, hogy a programok hiba esetén is folytassák a futást, javítva ezzel megbízhatóságukat és robusztusságukat.
Az imént felsorolt fogalmak alapvető fontosságúak a programozási nyelvek működésének megértéséhez, valamint a hatékony és karbantartható kód megírásához.
A programozási nyelvek szintaxisa
Egy programozási nyelv szintaxisa a program utasítások és kifejezések írására vonatkozó szabályokat jelenti. Fontos, hogy a szintaxis jól definiált és konzisztens legyen, hogy a programozó hatékonyan tudja megírni és megérteni a kódját. A legtöbb programozási nyelv építőkövei a következők:
- Eljárások (procedures) és függvények (functions)
-
Az eljárásokat és függvényeket újrafelhasználható kódblokkok definiálására használják, amelyek többször is meghívhatók.
- Változók (variables)
-
A változók a memória darabjait képviselik, és olyan adatokat tárolnak, amelyek manipulálhatók és átadhatók eljárások és függvények között.
- Operátorok (operators)
-
Az operátorok olyan kulcsszavak vagy szimbólumok (például
+
és-
), amelyek értékeket rendelnek a változókhoz és aritmetikai műveleteket végeznek el. - Vezérlési szerkezet (control structure)
-
A programkód általában abban a sorrendben hajtódik végre, ahogyan meg van írva, de a feltételes utasítások (conditional statement) megváltoztatják a végrehajtás menetét. Az, hogy melyik kódot hajtjuk végre legközelebb, különböző feltételeken alapul, például a memória tartalmán, a billentyűzet állapotán, a hálózatról érkező csomagokon stb. Az iterációs utasítás (loop statement) a feltételes utasítás egy speciális formája, arra alkalmas, hogy ugyanazokat a műveleteket hajtsa végre egy sor adathalmazon. A kivétel (exception), amely hiba esetén speciális kódot hív elő, egy másik vezérlési struktúra.
Ezeknek a konstrukcióknak a szintaxisa és viselkedése programozási nyelvenként eltérő lehet, a nyelvválasztás pedig nagy hatással lehet a kód olvashatóságára és karbantarthatóságára.
Könyvtárak
Egy jó programozási nyelvnek meg kell könnyítenie a programok fejlesztését és a meglévő kód újrafelhasználását. Sok programozási nyelv rendelkezik olyan mechanizmussal, amellyel az eljárásokat és függvényeket más programokban újrafelhasználható részekre lehet szervezni.
A könyvtár (library) egy adott funkciót vagy célt támogató eljárások és funkciók gyűjteménye, egyetlen fájlban egyesítve. Az egyik nagyon fontos követelmény egy jó programozási nyelvvel szemben, hogy sok könnyen használható könyvtár álljon rendelkezésre. A Python például széles körben elismert jó nyelv a mesterséges intelligenciával kapcsolatos programok fejlesztéséhez, mivel számos, mesterséges intelligencia feldolgozására alkalmas könyvtárral rendelkezik.
A programok méretének és összetettségének növekedésével a könyvtárak, mint kész építőelemek egyre fontosabbá válnak. Ez különösen igaz a nyílt forráskódú világban, ahol az emberek szívesen veszik át és használják újra a mások által készített kódot. Ennek eredményeképpen minden programozási nyelvhez kialakult egy könyvtárakból álló ökoszisztéma, és az olyan csomagkezelők, mint a PHP esetében a composer
, a Python esetében a pip
és a Ruby esetében a gems
, megkönnyítik a könyvtárak telepítését.
A könyvtárak szintén fontosak a fordított nyelvek számára. Az egy futtatható fájl lérehozásához szükséges, több bináris fájl és előre lefordított könyvtár kombinálását linkelésnek, és az ezt a műveletet végző eszközt linker-nek nevezik. A linkelésnek két típusa van: A statikus linkelés, amikor csak a szükséges könyvtárkódot tartalmazza a végleges alkalmazás végrehajtható fájlja, és a dinamikus linkelés, amikor a rendszerbe telepített könyvtárat az azt használó összes alkalmazás megosztja. Jelenleg a dinamikus linkelés az előnyben részesített megközelítés, amelyet a kisebb méretű futtatható alkalmazások és a kisebb memóriahasználat jellemez futásidőben.
Vegyük figyelembe, hogy mivel ugyanazokat a könyvtárakat több program is használhatja, a könyvtárak változatai közötti különbségek még nagyobb problémát jelenthetnek, mint az alkalmazások esetében. Kalandozzunk el egy kicsit és idézzük fel, hogyan kell nézni a verziószámokat. Általánosan használatos a Semantic versioning, amely a verziókat három, pontokkal elválasztott számmal jelöli. Egy tipikus verzió lehet a 2.39.16
, ami egy 2-es főverziót jelöl (ez a szám valószínűleg csak néhány évente egyszer változik), egy 39-es mellékverziót a főverzión belül (ami néhány havonta frissülhet, és fontos funkcióváltozásokat tartalmazhat), és egy 16-os gyorsváltású revíziót (ami egyetlen hibajavítás (bugfix) miatt változhat). A későbbi verziók és revíziók magasabb számokkal rendelkeznek.
Egy egyszerű példa
Nézzünk meg egy nagyon egyszerű példát egy számítógépes programra Python nyelven, hogy nagyjából képet kapjunk az említett elemek közül néhányról.
Természetes nyelven megfogalmazva, a programnak a következőket kell tennie: “Kérje meg a felhasználót, hogy adjon meg egy számot, és ellenőrizze, hogy ez a szám páros vagy páratlan! Végül írja ki az eredményt!”
És itt van a kód, amit elmenthetünk az simpleprogram.py
fájlba:
num = int(input("Adjon meg egy szamot: "))
if (num % 2) == 0:
print("A megadott szam PAROS.")
else:
print("A megadott szam PARATLAN.")
Még ebben a néhány sornyi kódban is megtalálhatjuk a fent említett jellemzők és szintaxiselemek nagy részét:
-
Az 1. sorban beállítjuk a
num
változót és hozzárendelünk egy értéket az=
operátorral. -
A hozzárendelt érték megfelel a felhasználó bemenetének (az
input()
függvényen keresztül). Ezenkívül azint()
függvény biztosítja, hogy ez a bemenet lehetőség szerint egész szám adattípussá konvertálódjon. A függvénynek zárójelben átadott kifejezést paraméternek vagy argumentumnak nevezzük. -
Ha a felhasználó egy betűsorozatot ír be, a Python a hibakezelés részeként hibát ír ki. Ha egy tizedes számot adunk meg, az
int()
függvény átalakítja azt az egész számra, például az5.73
-at5
-re. -
A következő sorokban a vezérlési szerkezet egy feltétel, az
if
éselse
kulcsszavakkal szabályozza, hogy mi történik a két lehetséges eset (a szám páros vagy páratlan) mindegyikében. -
Először a modulo operátor azt vizsgálja, hogy ha (
if
) a beírt szám 2-vel való osztása 0 értéket ad (azaz nincs maradék) — akkor a szám páros. A megduplázott==
az “egyenlő” összehasonlító operátor, amely különbözik az 1. sorban szereplő=
hozzárendelési operátortól. -
A másik esetben (
else
), azaz amikor a 2-vel való osztás eredménye nem 0, a beírt számnak páratlannak kell lennie. -
Mindkét esetben a
print()
függvény az eredményt, vagyis kimenetet szöveges formában adja vissza.
És így néz ki, amikor futtatjuk a programot a parancssorban:
$ python simpleprogram.py Adjon meg egy szamot: 5 A megadott szam PARATLAN.
Ha belegondolunk, hogy már ebben a kis példában is mennyi nyelvi logika van, akkor képet kapunk arról, hogy mire képesek a több ezer fájlra elosztott komplex szoftverek; például az olyan operációs rendszerek, mint a Microsoft Windows, a macOS vagy a Linux, amelyek egy számítógép összes hardverét elérhetővé teszik, ugyanakkor biztosítják, hogy a felhasználók minden más kívánt alkalmazást telepíteni tudjanak, és azokat munkára vagy szórakozásra használhassák.
Gépi kód, Assembly nyelv és az assemblerek
Mint korábban említettük, a hardver közvetlenül csak egy sor bitmintát, az úgynevezett gépi kódot képes végrehajtani. A CPU egy bitmintát szóegységekben (8-64 bit) olvas be a memóriából, és végrehajtja a hozzá tartozó utasítást. Az egyes utasítások meglehetősen egyszerűek, például: “A memóriahely tartalmának átmásolása a B memóriahelyre,” “C memóriahely tartalmának szorzása a D memóriahely tartalmával,” vagy "az X címre érkezett adat beolvasása az eszközre.`" A 8 bites CPU-k korában egyesek képesek voltak megjegyezni a gépi kódban használt összes bitmintát, és közvetlenül programokat írni. Manapság az utasításminták száma nagyságrendekkel megnőtt, és az összes ilyen minta megjegyzése nem túl praktikus.
A gépi kód bitminták, azaz 0-k és 1-ek sorozata, ami az ember számára egyáltalán nem intuitív. A programozás intuitívabbá tételére hozták létre az assembly nyelvet, amelyben az utasítások nevet kaptak, és sztringekkel adhatók meg. Az assembly nyelven az utasításokat, amelyek egy az egyben megfelelnek a gépi kódnak, egyenként írják le. Az utasítások így nézhetnek ki:
move [B], [A] az A memória tartalmának másolása a B memóriába multi R1, [C], [D] C memória tartalmának megszorzása a D memória tartalmával. input R1, [X] az X címre érkezett adatok beolvasása.
Egy utasítás az assembly nyelven egy utasításnak felel meg a gépi kódban, ami pontosan annak az utasításnak felel meg, amelyet a hardver megért és végre tud hajtani. Az assembly nyelv előnyei a gépi nyelvvel szemben a következők:
- Jobb olvashatóság és karbantarthatóság
-
A gépi kódnál sokkal könnyebb olvasni és írni. Ez megkönnyíti a programozók számára a kód megértését, valamint a hibakeresést és a karbantartást.
- Címszámítás automatizálása
-
A gépi kódú programozás is használhatja a változók és függvények fogalmát, de mindent memóriacímekben kell kifejezni. Az assembly nyelv neveket is rendel a memóriacímekhez, ami megkönnyíti a program logikájának kifejezését.
Mivel az assembly nyelv hozzáfér a hardver összes funkciójához, általában a következő esetekben használják:
- Az operációs rendszer architektúrafüggő része
-
Az inicializálási és biztonsági funkciókhoz való hozzáférésre szolgáló, egy adott CPU-architektúrára jellemző utasítások használata csak assembly nyelven lehetséges.
- Alacsony szintű rendszerelemek fejlesztése
-
A számítógép hardverével közvetlenül együttműködő rendszerelemek, például az eszközillesztőprogramok, a firmware és az alapvető input/output rendszer (BIOS) fejlesztésére használják az Assembly nyelvet. Különösen azokhoz a nagysebességű eszközökhöz, amelyek a hardver teljesítményének határait feszegetik, gyakran assembly nyelven programozott illesztőprogramokra és firmware-re van szükség.
- Mikrokontrollerek programozása
-
Ezek kis, alacsony teljesítményű számítógépek, amelyeket a beágyazott rendszerek széles skáláján használnak a játékoktól az ipari vezérlésig. Egyes mikrokontrollerek memóriakapacitása mindössze néhány száz bájt, és általában assembly nyelven programozzák őket.
Az assembly nyelvi kódot egy asszembler nevű alkalmazás gépi kóddá alakítja át, mielőtt végrehajtaná. Az assembler a legrégebbi programozási eszköz, és számos olyan előnnyel jár, amely a gépi kódú programozásban elképzelhetetlen volt. A zűrzavar kedvéért néha az emberek az assembly nyelvet assemblerként emlegetik.
A gépi kód és az assembly nyelv hardveres processzoronként eltérő. Azért nevezik őket “alacsony szintű nyelveknek” (low-level languages), mert közvetlenül a hardveren működnek. A számítás és a bemenet/kimenet fogalma azonban minden processzoron ugyanaz. Ha a közös fogalmakat az emberek számára könnyebben érthető módon lehet kifejezni, a programozás hatékonysága drámaian javulhat. Itt jönnek a képbe a “magas szintű nyelvek” (high-level languages).
Fordított nyelvek
A fordított nyelvek (compiled languages) olyan programozási nyelvek, amelyeket vagy gépi kódra, vagy egy köztes formátumra, az úgynevezett bytecode-ra (bájtkód) fordítanak le. A bytecode-ot a célszámítógépen egy futásidejű virtuális gép hajtja végre. A virtuális gép lefordítja a bytecode-ot az egyes számítógépeknek megfelelő gépi kódra. A bytecode lehetővé teszi, hogy a programok platformfüggetlenek, és bármely kompatibilis virtuális géppel rendelkező rendszeren futtathatóak legyenek.
A magas szintű programozási nyelven írt forráskód gépi kódra vagy bytecodera történő lefordítását egy fordító (compiler) végzi. A közvetlenül gépi kódot előállító fordított nyelvekre példa a C és a C++. A bytecodeot előállító nyelvek közé tartozik a Java és a C#.
A gépi kód és a bytecode közötti választás a projekt követelményeitől függ, például a teljesítménytől, a platformfüggetlenségtől és a fejlesztés egyszerűségétől.
Értelmezett nyelvek
Az értelmezett nyelvek (interpreted languages) olyan programozási nyelvek, amelyeket egy értelmező (interpreter) hajt végre, ahelyett, hogy gépi kódba fordítaná. Az interpreter beolvassa a forráskódot és végrehajtja a benne szereplő utasításokat. Az interpreter képes közvetlenül feldolgozni a forráskódot, anélkül, hogy azt más fájlformátumba alakítaná át. Így a compilerrel ellentétben, amely a teljes programot a végrehajtás előtt gépi kóddá fordítja le, az interpreter minden egyes kódsort beolvas és azonnal végrehajt, lehetővé téve a programozó számára, hogy az egyes sorok eredményét végrehajtás közben lássa.
Az értelmezett nyelveket általában scriptelésre (scripting) használják, ami a feladatokat automatizáló rövid programokra, parancssori interfészekre, valamint batch és jobok vezérlésére utal. Az interpreteres nyelveken írt scriptek könnyen módosíthatók és újbóli fordítás nélkül is végrehajthatók, így jól alkalmazhatók olyan feladatokhoz, amelyek gyors prototípus-fejlesztést vagy rugalmas és gyors iterációt igényelnek. Ezzel a kényelemmel együtt jár néhány lehetséges hátrány is. Például egy értelmezett program lassabban fut, mint a vele egyenértékű fordított program, valamint a forráskód bárki számára hozzáférhető, aki a script birtokában van.
Az értelmezett nyelvek közé tartozik például a Python, a Ruby és a JavaScript. A Pythont széles körben használják a tudományos számítástechnikában, az adatelemzésben és a gépi tanulásban, míg a Rubyt gyakran használják a webfejlesztésben és automatizálási scriptek készítéséhez. A JavaScript egy kliensoldali scriptnyelv, amely a webböngészőkbe ágyazva dinamikus és interaktív weboldalak létrehozására szolgál.
Adatorientált nyelvek
Az adatorientált nyelvek (data-oriented languages) olyan programozási nyelvek, amelyeket nagy mennyiségű adat feldolgozására és kezelésére optimalizáltak. Úgy tervezték őket, hogy hatékonyan kezeljenek nagy mennyiségű strukturált vagy strukturálatlan adathalmazt, valamint biztosítják az adatbázisokkal, adatstruktúrákkal, és az adatfeldolgozáshoz és -elemzéshez szükséges algoritmusokkal való munkavégzéshez szükséges eszközkészletet.
Az adatorientált nyelveket számos alkalmazásban használják, többek között az adattudományban, a nagy adatelemzésben (big data) , a gépi tanulásban és az adatbázis-programozásban. Jól alkalmazhatók olyan feladatokhoz, amelyek nagy mennyiségű adat feldolgozásával és elemzésével járnak, például adattisztítás és -átalakítás, adatvizualizáció és statisztikai modellezés.
Az adatorientált nyelvek közé tartozik például az SQL (a Structured Query Language rövidítése), az R és a MATLAB. Az SQL a relációs adatbázisok kezelésére használt szabványos nyelv, amelyet széles körben használnak az üzleti és ipari életben. Az R egy statisztikai számításokhoz és grafikákhoz használt programozási nyelv és környezet, amelyet széles körben használnak az adattudományban és a gépi tanulásban. A MATLAB egy numerikus számítási környezet és programozási nyelv, amelyet számos alkalmazásban használnak, többek között a jelfeldolgozásban, a képfeldolgozásban és a pénzügyi számításokban.
Programozási paradigmák
A programozási nyelvek sajátosságai mellett a programozási paradigmák határozzák meg az adott megoldási megközelítést. Egy paradigmára gondolhatunk úgy, mint egy alapvető stratégiára, amellyel egy feladatot megközelítünk, a konkrét követelményektől és feltételektől függően.
Hasonló példa lehet egy ház építése: Az, hogy a falakat téglánként kőművesek húzzák fel, vagy a helyszínen kész betonelemeket állítanak össze, alapvető döntés, amely az igényektől és a körülményektől függ. Milyen tulajdonságokkal rendelkezzen a ház? Hol helyezkedik el? Összekapcsolódik-e más házakkal?
Hasonló módon a paradigmák határozzák meg a programozás irányát: hogy például egy szoftverprojektet kisebb, különálló részekre bontanak-e és ha igen, milyen módon. Minden programozási nyelv egy adott paradigmához illeszkedik a legjobban. Ezért a paradigmaválasztás szorosan összefügg a programozási nyelv kiválasztásával.
A következő paradigmák gyakoriak a programozásban:
- Objektumorientált programozás (OOP)
-
Az OOP az objektumok koncepcióján alapul, amelyek az osztályok példányai, amelyek adatokat és viselkedést foglalnak magukba. Például lehet egy nyelvben a téglalap egy osztály, hogy segítsen a programozónak egy doboz megjelenítésében a képernyőn.
Az OOP az adatok objektumszintű manipulációjára összpontosít. Az OOP megkönnyíti a karbantartható, újrafelhasználható és bővíthető kód írását, és széles körben használják desktop és webes alkalmazásokban, videojátékokban. Az objektumorientált programozási nyelvek közé tartozik például a Java, a C# és a Python.
- Procedurális programozás
-
A procedurális programozás a feladatokat eljárások, azaz meghatározott sorrendben végrehajtható kódblokkok segítségével hajtja végre. Ez megkönnyíti a könnyen követhető, strukturált kód írását, de a projekt méretének és összetettségének növekedésével kevésbé rugalmas és nehezebben karbantartható kódot eredményezhet. A procedurális programozási nyelvek közé tartozik például a C, a Pascal és a Fortran.
A szoftverfejlesztésnek ma más megközelítései is használatosak, amelyekhez egyes nyelvek jobban alkalmazhatók, mint más nyelvek. Emellett a drag-and-drop felületek lehetővé teszik, hogy a nem programozók is írhassanak programokat, valamint a közelmúltban számos online szolgáltatás elkezdett kódot generálni mesterséges intelligencia segítségével, egyszerű nyelvi utasításokból.
Összefoglalva, minden programozási paradigmának megvannak a maga erősségei és gyengeségei, és a paradigma kiválasztása gyakran függ a projekt igényeitől, a fejlesztő tapasztalatától és preferenciáitól, valamint a platform és a fejlesztési környezet korlátaitól. A paradigmák különböző típusainak megértése segíthet az igényeknek megfelelő paradigma kiválasztásában, és lehetővé teheti a jobb és hatékonyabb kód írását.
Gyakorló feladatok
-
Mi a függvények célja?
-
Mi az előnye a bytecodenak a gépi kódú fájlokkal szemben?
-
Mi az előnye a gépi kódú fájlnak a bytecode-dal szemben?
Gondolkodtató feladatok
-
Milyen hátrányai vannak annak, ha egy programot nagyszámú folyamatokra vagy feladatokra osztunk?
-
Számos olyan nyílt forráskódú csomagot találhatunk, amelyek különböző verziókban kínálnak olyan funkciókat, amelyekre szükségünk van a programunkhoz. Milyen szempontok alapján kell kiválasztanunk egy csomagot?
-
Az OOP és a procedurális fejlesztési paradigmákon kívül milyen más szoftverfejlesztési megközelítések léteznek, és melyik az a programozási nyelv, amelyik a legjobban támogatja az egyes megközelítéseket?
Összefoglalás
Ebben a leckében megtanultuk, hogy mi az a szoftver és hogyan fejlesztik a programozási nyelvek segítségével. A számos programozási nyelv nemcsak a szintaxisukban különbözik, hanem például a hardveres erőforrások vagy az adatszerkezetek kezelésében is.
A programozási nyelvek abban is különböznek, hogy az ember által olvasható forráskódot egy értelmező vagy fordítóprogram hogyan alakítja át a számítógép által feldolgozandó végleges gépi kóddá.
A programozási paradigmák meghatározzák a szoftverprojektek stratégiáját, és így a megfelelő programozási nyelvek kiválasztását is, az adott projekt követelményeitől és méretétől függően.
Válaszok a gyakorló feladatokra
-
Mi a függvények célja?
A függvények bizonyos általános tevékenységeket, például egy sztring kiírását foglalják magukba. Egy függvény létrehozásával lehetővé tehetjük, hogy a programunk és más programok kényelmesen és ismételten elvégezhessék a funkciót anélkül, hogy saját kódot kellene írniuk hozzá.
-
Mi az előnye a bytecodenak a gépi kódú fájlokkal szemben?
A bytecode fájl több különböző számítógépen is futtatható, ahol egy virtuális gép a kódot gépi kóddá alakítja. A JavaScript például sok böngészőben, sokféle számítógépen fut.
-
Mi az előnye a gépi kódú fájlnak a bytecode-dal szemben?
A gépi kód a lehető leggyorsabban fut. A bytecode lassabban fut, mert a virtuális gépnek a bytecode futtatása közben gépi kóddá kell azt alakítania.
Válaszok a gondolkodtató feladatokra
-
Milyen hátrányai vannak annak, ha egy programot nagyszámú folyamatra vagy feladatra osztunk?
Amikor egy program folyamatokra van osztva, azoknak kommunikálniuk kell egymással. Ha sok közös adaton dolgoznak, akkor a folyamatok sok költséget fordíthatnak az adatcserére és az adatok védelmére a többszörös, egyidejű változásoktól (versenyfeltételek, race conditions). A folyamatok indításkor és befejezéskor is többletköltséget okoznak. Minél több folyamat van, annál összetettebbé válik a program és annak kölcsönhatásai, így a hibákat nehezebb lehet megtalálni.
A funkcionális paradigma általában megkönnyíti a programok sok folyamatra való felosztását, a megváltoztathatatlanság miatt. A megváltoztathatatlan adatok nem szenvednek a versenyfeltételektől.
-
Számos olyan nyílt forráskódú csomagot találhatunk, amelyek különböző verziókban kínálnak olyan funkciókat, amelyekre szükségünk van a programunkhoz. Milyen szempontok alapján kell kiválasztanunk egy csomagot?
Ellenőrizzük a hibajelentéseket és a csomagokra vonatkozó biztonsági tanácsokat, mert némelyik nagyon hibás és akár az is lehet, hogy nem biztonságos! Nem mindig a legújabb verzió a legjobb, mert lehet, hogy biztonsági hiba van benne.
Nézzük át a fórumot, ahol a fejlesztők beszélgetnek a csomagról, hogy lássuk, aktívan karbantartják-e! A programunkat valószínűleg hosszú ideig fogjuk használni, és azt akarjuk, hogy a csomag később is elérhető és robusztus legyen.
Próbáljunk ki különböző csomagokat, hogy ellenőrizzük a teljesítményüket, valamint a helyességüket.
A legtöbb csomag más csomagokban található függvényektől (függőségek, dependencies) függ, így az egyik függőség gyengesége hatással lehet a programunkra.
-
Az OOP és a procedurális fejlesztési paradigmákon kívül milyen más szoftverfejlesztési megközelítések léteznek, és melyik az a programozási nyelv, amelyik a legjobban támogatja az egyes megközelítéseket?
Az OOP és a procedurális fejlesztési paradigmák mellett a következő típusok is előfordulnak.
A funkcionális programozás a függvények és matematikai fogalmak, például lambdák és lezárások használatát hangsúlyozza, hogy olyan kódot írhassunk, amely inkább a kifejezések kiértékelésén, mint az utasítások végrehajtásán alapul. A funkcionális programozás a függvényeket első osztályúként kezeli — tehát a program által manipulálhatóak --, és hangsúlyozza az immutabilitást, vagyis az olyan változókkal való munkát, amelyek a kezdeti beállítás után nem változhatnak. Ez megkönnyíti a kód átgondolását és tesztelését, valamint az egyidejű és párhuzamos alkalmazások írását. A funkcionális programozási nyelvek közé tartozik például az Erlang, a Haskell, a Lisp és a Scheme.
Az imperatív nyelvek a program különböző állapotokból és különböző állapotokba való átmenetek áramlásának vezérléséhez szükséges utasításokra összpontosítanak.
A deklaratív nyelvek leírják azt, hogy milyen műveleteket kell végrehajtani, valamint az utasítások mögötti logikát. Az utasítások végrehajtásának sorrendje nincs meghatározva. Ezeket az utasításokat, függvényhívásokat és egyéb utasításokat a fordítóprogramok átrendezhetik és optimalizálhatják, amíg megőrzik a mögöttes logikát.
A természetes programozás olyan programozási paradigma, amely természetes nyelvet vagy más emberbarát reprezentációkat használ a program kívánt viselkedésének leírására. Az elképzelés lényege, hogy a programozást olyan emberek számára is elérhetővé tegye, akik nem rendelkeznek formális informatikai képzéssel. A természetes programozási nyelvekre példa a Scratch és az Alice.