Hyrje në Programim PROGRAMIMI NË JAVA ERCAN CANHASI ARTA MISINI Universiteti "Ukshin Hoti "Prizren Fakulteti i Shkencave Kompjuterike
Programimi në JAVA Autorë: Ercan Canhasi dhe Arta Misini Shtator 2019
iv Përmbajta Figurat vii Tabelat viii 1 Hyrje 1 1.1 Lesionet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.2 Detyra praktike . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.3 Punë praktike . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2 Konceptet bazë të programimit 3 2.1 Krijimi dhe ekzekutimi i një programi në JAVA: hap-pas-hapi . . . . . 3 2.1.1 Përdorimi i një editori . . . . . . . . . . . . . . . . . . . . . . . . 3 2.1.2 Kompajlimi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 2.1.3 Ekzekutimi i një programi . . . . . . . . . . . . . . . . . . . . . . 4 2.2 Struktura e një programi . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2.2.1 Deklaratat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.2.2 Komentet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.2.3 Dhëmbëzimi, kryeradha dhe paraqitja . . . . . . . . . . . . . . . 5 2.3 Variablat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 2.3.1 Identifikatorët . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.3.2 Deklarimi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.3.3 Vlerëdhënia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.4 Llojet e të dhënave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.4.1 Numrat e plotë (integers) . . . . . . . . . . . . . . . . . . . . . . 10 2.4.2 Numrat me pikë lëvizëse . . . . . . . . . . . . . . . . . . . . . . 11 2.4.3 Karakteret dhe teksti . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.5 Paraqitja e tekstit me printf . . . . . . . . . . . . . . . . . . . . . . . . 13 2.6 Shprehjet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.6.1 Operatorët aritmetikorë . . . . . . . . . . . . . . . . . . . . . . . 15 2.6.2 Operatorët matematikorë . . . . . . . . . . . . . . . . . . . . . . 15 2.6.3 Konvertimi implicit . . . . . . . . . . . . . . . . . . . . . . . . . 16 2.6.4 Konvertimi eksplicit . . . . . . . . . . . . . . . . . . . . . . . . . 17 2.6.5 Operatorët logjikë . . . . . . . . . . . . . . . . . . . . . . . . . . 18 2.6.6 Operatorët relacionalë . . . . . . . . . . . . . . . . . . . . . . . . 19 2.6.7 Prioriteti i operatorëve . . . . . . . . . . . . . . . . . . . . . . . . 19 2.7 Deklarimet e kushtëzuara . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.7.1 Deklarimi if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.7.2 Deklarimi else . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.7.3 Deklarimi else if . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.7.3.1 Gjetja e vlerës maksimale: Një shembull . . . . . . . . 22 2.8 Shprehjet e kushtëzuara . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 2.8.1 Operatori ternar . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
v 2.9 Deklarata switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.10 Klasa Scanner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 2.11 Modeli Hyrje-Procesim-Dalje . . . . . . . . . . . . . . . . . . . . . . . . 28 2.12 Detyra praktike . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 2.12.1 Caktimet dhe Shprehjet . . . . . . . . . . . . . . . . . . . . . . . 30 2.12.2 Programe të thjeshta . . . . . . . . . . . . . . . . . . . . . . . . . 31 3 Iterimi 32 3.1 Cikli while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 3.2 Cikli do-while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 3.3 Cikli for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 3.4 Ciklet dhe Akumulatori . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 3.5 Llogaritja e vlerës mesatare: Një shembull . . . . . . . . . . . . . . . . . 37 3.5.1 Ciklet e pacaktuara . . . . . . . . . . . . . . . . . . . . . . . . . . 38 3.5.2 Cikli përcjellës . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 3.5.3 Cikli sentinel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 3.6 Korrigjimi i një programi . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 3.7 Detyra praktike . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 3.7.1 Tipet numerike . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 3.7.2 Iterimi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 4 Funksionet statike 44 4.1 Deklarimi i funksionit . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 4.1.1 Funksionet void . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 4.1.2 Funksionet që kthejnë një rezultat . . . . . . . . . . . . . . . . . 45 4.2 Thirrja e funksionit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 4.3 Fushëveprimi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 4.4 Kalimi i parametrave sipas-vlerës . . . . . . . . . . . . . . . . . . . . . . 49 4.5 Kalimi i parametrave sipas-referencës . . . . . . . . . . . . . . . . . . . 50 4.6 Detyra praktike . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 4.6.1 Funksionet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 4.6.2 Programe të vogla . . . . . . . . . . . . . . . . . . . . . . . . . . 53 5 Vargjet 54 5.1 Deklarimi dhe inicializimi i vargjeve . . . . . . . . . . . . . . . . . . . . 54 5.2 Indeksimi i vargjeve . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 5.3 Shembuj duke përdorur vargjet . . . . . . . . . . . . . . . . . . . . . . . 56 5.3.1 Krijimi dhe inicializimi i një vargu . . . . . . . . . . . . . . . . . 56 5.3.2 Llogaritja e vlerave që ruhen në një varg . . . . . . . . . . . . . 57 5.3.3 Shuma e elementeve të një vargu . . . . . . . . . . . . . . . . . . 58 5.3.4 Deklarimi for i zgjeruar . . . . . . . . . . . . . . . . . . . . . . . 59 5.4 Vargjet shumë-dimensionale . . . . . . . . . . . . . . . . . . . . . . . . . 60 5.4.1 Shembull me vargjet dy-dimensionale: Paraqitja e vlerave të elementeve . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 5.5 Përdorimi i vargjeve si parametra të një funksioni . . . . . . . . . . . . 62 5.6 Stringjet: vargjet e karaktereve . . . . . . . . . . . . . . . . . . . . . . . 63 5.7 Lista e argumenteve me gjatësi të ndryshueshme . . . . . . . . . . . . . 65 5.8 Përdorimi i argumenteve të vijës komanduese . . . . . . . . . . . . . . 66 5.9 Detyra praktike . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 5.9.1 Vargjet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 5.9.2 Modifikimi i vargjeve . . . . . . . . . . . . . . . . . . . . . . . . 69
vi 5.9.3 Vargjet shumë-dimensionale . . . . . . . . . . . . . . . . . . . . 70 6 Rekursioni 71 6.1 Shembull: funksioni rekursiv për faktorielin . . . . . . . . . . . . . . . 72 6.2 Shembull tjetër: Fibonacci . . . . . . . . . . . . . . . . . . . . . . . . . . 73 6.3 Detyra praktike . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 6.3.1 Rekursioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 A Shembuj dhe Detyra 77 A.1 Konceptet bazë të programimit . . . . . . . . . . . . . . . . . . . . . . . 77 A.2 Iterimi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 A.3 Funksionet statike . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 A.4 Vargjet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 B Provime përfundimtare 103 B.1 Provimi përfundimtar (Janar 2020) . . . . . . . . . . . . . . . . . . . . . 103 B.2 Provimi përfundimtar (Prill 2020) . . . . . . . . . . . . . . . . . . . . . . 104 B.3 Provimi përfundimtar (Qershor 2020) . . . . . . . . . . . . . . . . . . . 105 B.4 Provimi përfundimtar (Shtator 2020) . . . . . . . . . . . . . . . . . . . . 106 Indeksi Alfabetik 107
vii Figurat 2.1 Përfaqësimi skematik i deklarimit if-else. . . . . . . . . . . . . . . . 21 3.1 Përfaqësimi skematik i deklarimit while. . . . . . . . . . . . . . . . . . 33 3.2 Përfaqësimi skematik i deklarimit do while. . . . . . . . . . . . . . . 34 3.3 Përfaqësimi skematik i deklarimit for. . . . . . . . . . . . . . . . . . . 35 3.4 Gabimi i gjetur. Nga logbook i kalkulatorit me transmetues të Mark II Aiken në Universitetin e Harvardit, 9 Shtator 1945. . . . . . . . . . . . . 40
viii Tabelat 2.1 Tabela ASCII (American Standard Code for Information Interchange). 12 2.2 Funksionet matematikore në JAVA. . . . . . . . . . . . . . . . . . . . . . 16 2.3 Operatorët logjikë në JAVA. . . . . . . . . . . . . . . . . . . . . . . . . . 18 2.4 Operatorët relacionalë në JAVA. . . . . . . . . . . . . . . . . . . . . . . . 19 2.5 Prioriteti i operatorëve. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.6 Metodat e klasës Scanner për të lexuar vlerat hyrëse nga tastiera në JAVA. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
1 Kapitulli 1 Hyrje Gjatë kursit Hyrje në Programim ju do të mësoni principet bazë të programimit. Për këtë qëllim ne përdorim gjuhën programuese JAVA. JAVA është një gjuhë e orientuar në objekte e zhvilluar nga Sun Microsystems, e cila është dizajnuar të jetë një gjuhë e pavarur nga makina. Programimi i Orientuar në Objekte (ang. Object-oriented programming, OOP) është një metodologji e zhvillimit të softuerit në të cilën një program konceptualizohet si një grup i objekteve që punojnë së bashku. Objektet krijohen duke përdorur struktura të quajtura klasa, dhe ato përmbajnë të dhëna dhe deklarime që përdorin këto të dhëna. JAVA është plotësisht e orientuar në objekte, siç do të shohim më vonë kur krijojmë klasa dhe i përdorim ato për të krijuar objekte. Disa gjuhë programuese kërkojnë një interpreter për të ekzekutuar programet e tyre. Interpreteri është një program që interpreton secilin rresht të një programi kompjuterik dhe i tregon kompjuterit se çfarë të bëjë. Avantazhi i gjuhëve të interpretuara është se ato janë më të shpejta për t’u testuar. Disavantazhi kryesor është që gjuhët e interpretuara ekzekutohen më ngadalë se programet tjera. Gjuhët tjera programuese kërkojnë një kompajler. Kompajleri merr një program kompjuterik dhe e përkthen atë në një formë që kompjuteri mund ta kuptojë. Programi i kompajluar mund të ekzekutohet direkt pa pasur nevojë për një interpreter. Programet e kompajluara ekzekutohen shumë më shpejt se programet e interpretuara por marrin më shumë kohë për t’u testuar. JAVA është një gjuhë e pazakontë sepse kërkon një kompajler dhe një interpreter. Për këtë do të diskutojmë më vonë. 1.1 Lesionet Në lesionet, trajtohen ndërtimet e ndryshme bazë të programimit. Gjithashtu, tregohet se si ato implementohen në gjuhën programuese JAVA. Ato përfshijnë deklarimet e kushtëzuara, iterimin, funksionet statike, vargjet dhe matricat. 1.2 Detyra praktike Detyra programimi jepen gjatë pjesëve të detyrave praktike. Studentët ndahen në grupe dhe kryejnë detyrat e tyre nën udhëzimin e një mësimdhënësi. Këto detyra realizohen duke përdorur laps dhe letër (pa kompjuter). Gjatë këtyre detyrave praktike kërkohet një shkallë e lartë vetë-motivimi. Studentët zgjidhin detyrat vetë,
2 Kapitulli 1. Hyrje nën mbikëqyrjen e mësimdhënësit në vend se të dëgjojnë atë teksa shpjegon se si të trajtojnë problemin. 1.3 Punë praktike Për të zbatuar aftësitë e fituara të programimit në praktikë, janë pjesët praktike kompjuterike. Çdo javë, një grup i detyrave të programimit publikohen në Google Classroom. Për të zgjidhur detyrat e një jave të caktuar, studenti duhet të aplikojë materialin e mësuar në mësimin e asaj jave dhe të javëve të kaluara. Studentët ndahen në grupe të vogla (si në detyrat praktike). Për secilin grup caktohet një kohë në javë (sipas orarit) gjatë së cilës kompjuterët janë në dispozicion. Përgjatë një kohe të caktuar, detyrat për atë javë mund të punohen nën mbikëqyrjen e asistentit. Jashtë kësaj kohe të caktuar, nëse detyrat e caktuara nuk janë përfunduar (që është një situatë e zakonshme, nuk pritet që të përfundohen detyrat brenda dy orëve), studentët duhet të punojnë vetë në detyrat e tyre, pa mbikëqyrje.
3 Kapitulli 2 Konceptet bazë të programimit 2.1 Krijimi dhe ekzekutimi i një programi në JAVA: hap-pashapi Ne tani shpjegojmë hapat e zakonshëm që përdoren në krijimin dhe ekzekutimin e një aplikacioni në JAVA duke përdorur një mjedis zhvillimi në JAVA. Programet në JAVA normalisht zhvillohen përmes ciklit edito-kompajlo-ekzekuto. Ne diskutojmë këto faza në kontekst të Java SE Developmment Kit (JDK). Ju mund të shkarkoni versionet e JDK-së dhe dokumentacionin e tij nga: https://www.oracle.com/java/technologies/javase-downloads.html 2.1.1 Përdorimi i një editori Kodi burimor i një programi në JAVA është një skedar i thjeshtë teksti me ekstensionin .java dhe emrin e skedarit, për shembull MyProgram.java. Meqë kodi burimor i një programi është thjesht një skedar i zakonshëm teksti ju mund ta krijoni dhe ta ndryshoni atë me ndonjë editor teksti që është i instaluar në sistemet e laboratorit. Një editor është një përpunues i thjeshtuar teksti që ruan skedarët e tij si skedarë të thjeshtë teksti ASCII (pra, fontet dhe madhësitë e tyre nuk janë problem). Shumë studentë preferojnë të përdorin editorin Notepad++. Sigurisht, ju jeni të lirë të përdorni çfarëdo editori tjetër. Shumë zhvillues softueri përdorin softuerë të quajtur Integrated Development Environment (IDE). IDE-të ofrojnë vegla që mbështesin procesin e zhvillimit të softuerit, duke përfshirë editorët për të shkruar dhe edituar programet dhe korrigjuesit për të lokalizuar gabimet logjike —gabime të shkaktuara nga programet që ekzekutohen në mënyrë jokorrekte. IDE-të më të njohura përfshijnë Eclipse (https://www.eclipse.org) dhe NetBeans (https://www.netbeans.org). 2.1.2 Kompajlimi JAVA është një gjuhë programuese që kompajlohet dhe interpretohet. Para se të mund të ekzekutoni një program së pari duhet ta konvertoni kodin e tij burimor në një formë që është e ekzekutueshme nga kompjuteri. Ky proces konvertimi quhet kompajlim. Programi që kryen këtë konvertim quhet kompajler. Kodi burimor kompajlohet duke përdorur kompajlerin e JAVA-së. Për shembull, për të kompajluar një program të quajtur MyProgram.java, shënojmë:
4 Kapitulli 2. Konceptet bazë të programimit javac MyProgram.java në dritaren komanduese të sistemit operativ (d.m.th., Command Prompt në Windows, shell prompt në Linux, Terminal aplikacion në Mac OS X). Nëse programi kompajlohet, kompajleri krijon një skedar .class të quajtur MyProgram.class që përmban versionin e kompajluar të programit. Ky version i kompajluar i JAVA-së quhet bytecode. Skedari me ekstensionin .class, i cili nuk është i lexueshëm nga njeriu, përmban kodin e makinës që mund të ekzekutohet nga procesori i kompjuterit. Ky është skedar i ekzekutueshëm. Kompajlimi dështon nëse kompajleri zbulon ndonjë gabim në kodin burimor. Në këtë rast kompajleri do të raportojë një mesazh gabimi, duke treguar emrin e skedarit dhe numrin e rreshtit ku gabimi është zbuluar. Zakonisht është e qartë nga ky raport se ku është i lokalizuar gabimi në programin. Gabimet e zbuluara nga kompajleri quhen gabime gjatë kompajlimit. Gabimet që ndodhin gjatë kompajlimit janë aktualisht lloji më i thjeshtë i gabimeve. Shumica e gabimeve që ndodhin gjatë kompajlimit janë për shkak të thyerjes së rregullave sintaksore. Kompajleri i JAVA-së përkthen kodin burimor të JAVA-së në kod binar (bytecodes) që përfaqëson detyrat që ekzekutohen në fazën e ekzekutimit të programit. Nëse jeni duke punuar në një projekt më të madh programimi atëherë sigurisht ju do të ndani programin tuaj në disa skedarë me ekstensionin .java. Ju mund të kompajloni të gjithë këta skedarë në të njëjtën kohë, nëse janë në të njëjtin direktorium duke përdorur komandën: javac *.java 2.1.3 Ekzekutimi i një programi Programi i kompajluar ekzekutohet nga Java Virtual Machine (JVM). Një makinë virtuale është një softuer që simulon një kompjuter, por fsheh sistemin operativ dhe harduerin nga programet që ndërveprojnë me të. Pas kompajlimit të kodit burimor, programi ekzekutohet duke shënuar: java MyProgram Nëse programi juaj është pa gabime, në ekran do të shfaqet rezultati i programit. Nëse ndodh një gabim në ekzekutimin e programit, interpreteri do ta kapë atë dhe do të ndalojë ekzekutimin e tij. Gabimet që zbulohen nga interpreteri quhen gabime gjatë ekzekutimit. 2.2 Struktura e një programi Një program në JAVA përbëhet prej disa pjesëve standarde. Prandaj, është e përshtatshme të kemi një strukturë të programit. Gjatë këtij kursi do të përdorim strukturën vijuese si një pikë startuese për çdo program që do të zhvillojmë: PROGRAMI 2.1: Struktura e programit në JAVA 1 /* file : MyProgram.java */ 2 /* author : Arta Misini */ 3 /* date : Tue Sep 3 2019 */ 4 /* Description:
2.2. Struktura e një programi 5 5 * Please type here a description of 6 * what this program is supposed to do. 7 */ 8 public class MyProgram { 9 public static void main(String [] args) { 10 /*here you place your own code*/ 11 } 12 } Tani, ky kod mund të jetë plotësisht i pakuptueshëm për ju. Mirëpo, mos u shqetësoni, ju do të kuptoni strukturën së shpejti. 2.2.1 Deklaratat Thelbi i një programi përbëhet nga një sekuencë e deklaratave. Një deklaratë është një komandë elementare, e tillë si "zvogëlo vlerën e variablës a për 2"ose "përsërit kodin vijues 10 herë derisa të plotësohet një kusht i dhënë". Secila deklaratë vendoset në një rresht të ndarë dhe përfundon me ; (pikëpresje). Kompjuteri ekzekuton deklaratat një-nga-një dhe në radhën në të cilën ato paraqiten në program. 2.2.2 Komentet Një koment në një program në JAVA është një tekst i lexueshëm nga njeriu që është i mbyllur nga /* dhe */. Vini re se ju mund të shënoni komente që përbëhen nga një ose më shumë rreshta, siç tregohet edhe në kodin e strukturës së programit. Komentet injorohen nga kompajleri i JAVA-së dhe kanë synim të bëjnë logjikën e programit më të lehtë për t’u kuptuar nga ju dhe të tjerët. Është e zakonshme që komentet të vendosen në fillim të programit dhe përmbajnë informacion në lidhje me atë se kush e ka shkruar programin dhe kur. Gjithashtu, përmbajnë një përshkrim të funksionalitetit të programit dhe informacion tjetër ndihmës. Meqë kompajleri injoron komentet, shumë programerë janë bërë përtacë dhe vështirë se përdorin komentet në programet e tyre. Kjo është vërtetë praktikë e keqe! Ju mund të ndihmoni të tjerët të kuptojnë logjikën e kodit tuaj duke dhënë komente të dobishme, kështu që përdoreni këtë mundësi. Përveç kësaj, ju gjithashtu përfitoni vetë. Për shembull, nëse ju doni të shtoni ndonjë funksionalitet në një program që ju e keni zhvilluar para një kohe të gjatë, është e pamundshme që ende të mbani në mend të gjitha detajet e dizajnit të programit. Në këtë situatë, komentet e mira dhe dokumentacioni janë shumë të dobishëm. 2.2.3 Dhëmbëzimi, kryeradha dhe paraqitja Me white-space nënkuptojmë hapësirat (ang. spaces), tabet (ang. tabs) dhe rreshtat e rinj (ang. newlines). Një rresht i ri, gjithashtu i njohur edhe si një thyerje rreshti ose shënues i fundit të rreshtit, është një karakter special që tregon fundin e një rreshti të tekstit. Në gjuhën programuese JAVA, programeri është i lirë të përdorë hapësirat kudo që dëshiron. Hapësirat përdoren për të dhënë një paraqitje të strukturuar të një programi dhe plotësisht injorohen nga kompajleri. Sa për informacion, janë disa gjuhë programimi që detyrojnë kufizime në paraqitjen, për shembull gjuha programuese Python. Meqë kompajleri i JAVA-së injoron hapësirat, dy programet vijuese janë plotësisht të njëjtë:
6 Kapitulli 2. Konceptet bazë të programimit PROGRAMI 2.2: Paraqitja e programit: Mëyra I 1 public class MyProgram { 2 public static void main(String [] args) { 3 System.out.println("Hello world"); 4 } 5 } PROGRAMI 2.3: Paraqitja e programit: Mënyra II 1 public 2 class MyProgram { 3 public static void main(String[] 4 args) { 5 System.out.println("Hello world"); 6 } 7 } Qartësisht, mënyra e parë preferohet meqë është më e lehtë të lexohet. Meqë hapësirat injorohen nga kompajleri, ne mund t’i përdorim ato për të bërë programin tonë më të lexueshëm. Në JAVA, pjesët e kodit janë të rrethuar nga kllapat { dhe }. Këto pjesë të kodit, duke përfshirë edhe kllapat, quhen blloqe. Kodi i një blloku pa kllapat quhet trupi i bllokut. Gjatë këtij kursi, kemi miratuar marrëveshjen për të paraqitur trupin e bllokut duke përdorur hapësira shtesë. Meqë blloqet mund të jenë të mbivendosura (një bllok brenda një blloku tjetër), kjo automatikisht jep nivele të shumta të dhëmbëzimit. Ne kemi miratuar marrëveshjen se një kllapë hapëse { ndiqet nga një rresht i ri, dhe rrisim nivelin e dhëmbëzimit për trupin e bllokut korrespondues. Ne zvogëlojmë nivelin e dhëmbëzimit kur arrijmë në kllapën mbyllëse }. Vini re se i vendosim kllapës mbyllëse } një nivel dhëmbëzimi më të vogël se trupi i bllokut korrespondues. Një shembull i kësaj marrëveshjeje kodimi jepet në programin vijues: PROGRAMI 2.4: Nivelet e dhëmbëzimit 1 /* this is indentation level 0 */ 2 public static void main (String [] args) { 3 /* this is indentation level 1 */ 4 if (1 < 2) { 5 /* this is indentation level 2 */ 6 System.out.println("Ok"); 7 } 8 else { 9 /* this is also indentation level 2 */ 10 System.out.println("Error"); 11 } 12 /*We are back at indentation level 1*/ 13 System.out.println("Goodbye"); 14 } 2.3 Variablat Në JAVA, të dhënat ruhen dhe modifikohen në variabla. E dhënë mund të jetë çkado, për shembull numër dhe tekst. Një variabël është një lokacion në memorien
2.3. Variablat 7 e kompjuterit ku mund të ruhet një vlerë për përdorim të mëvonshëm në program. Mund të imagjinoni një variabël si një ’sirtar’ me një etiketë (emri i variablës) në të, në të cilën ju mund të ruani një të dhënë (vlerë). Në ndonjë pikë të caktuar në programin tuaj, secila variabël ’përmban’ një vlerë të caktuar, ’përmbajtjen’ e ’sirtarit’. Një variabël në një gjuhë programuese është e ndryshme nga një variabël në kuptimin matematikor. Ndonjëherë, variablat matematikore quhen ’të panjohura’, që aktualisht është një term më i mirë, meqë vlera zakonisht është e pa njohur. Në një kohë gjatë ekzekutimit (por jo në kohën e të shkruarit) të programit, vlera e një variable është e njohur. Secila variabël ka një lloj të të dhënave. Lloji i të dhënave i një variable përcakton se çfarë lloj të vlerës ju mund të ruani në atë lokacion të memories. Për shembull, ka lloje numerike të të dhënave (për numrat), dhe lloje karakteresh (për tekstet). 2.3.1 Identifikatorët Një identifikator është një emër për një variabël ose funksion (shih kapitullin 4). Identifikatorët fillojnë me një shkronjë. Pjesa e mbetur e emrit mund të përmbajë shkronja, numra dhe viza. Shkronjat e mëdha dhe të vogla janë të rëndësishme! Për shembull, identifikatorët value dhe Value janë të ndryshëm! Pra, gjuha programuese JAVA është case-sensitive. Në këtë kurs, ne miratojmë këtë marrëveshje. Një identifikator fillon me një shkronjë të vogël. Në pjesën e mbetur të identifikatorit, secila fjalë e re fillon me një shkronjë të madhe përderisa shkronjat tjera janë të vogla. Shembuj të identifikatorëve që përdorin këtë format janë thisIsAnExample, notEqualTo dhe hasValue1. Në literaturën e gjuhës programuese JAVA ju do të gjeni mënyra të ndryshme për formatin e identifikatorëve. Kjo që ne do ta përdorim quhet camelCase. 2.3.2 Deklarimi Para se ju të mund të përdorni një variabël, ju duhet t’i tregoni kompajlerit të JAVA-së në lidhje me ekzistencën e variablës. Ju deklaroni një variabël duke përcaktuar llojin e tij të të dhënave të ndjekur nga emri i variablës (identifikatori) dhe një pikëpresje. Ky quhet deklarimi i variablës. Në vazhdim jepet një shembull i tre deklarimeve: double weight; char letter; long timeAgo; Pas këtyre deklarimeve, variablat weight, letter dhe timeAgo ekzistojnë dhe mund të përdoren në pjesën e mbetur të programit. Një diskutim lidhur me llojet e mundshme të të dhënave mund të gjendet në pjesën 2.4. Para se të shikoni vlerën e një variable, ajo së pari duhet të përmbajë një vlerë. Ne do të përdorim termat lexim i një variable për të shikuar përmbajtjen e një variable dhe shkrim i një variable për të modifikuar përmbajtjen e një variable. Procesi i të
8 Kapitulli 2. Konceptet bazë të programimit shkruarit të një vlere fillestare në një variabël quhet inicializim i variablës. Një variabël mund të deklarohet dhe t’i caktohet një vlerë fillestare në një deklaratë të vetme, siç tregohet në shembullin vijues: double pi = 3.14159265; int two = 2; Është praktikë e mirë të shtoni komente në programin tuaj që përshkruajnë se për çfarë përdoren variablat në programin tuaj. Të gjitha variablat në JAVA duhet të deklarohen me një emër dhe një lloj të dhënash para se ato të mund të përdoren. Emri i variablës mundëson programin t’i qaset vlerës së variablës në memorie. 2.3.3 Vlerëdhënia Vlerëdhënia përdoret për të modifikuar vlerën e një variable. Është një deklaratë që ka formën <variabla> = <shprehja>;. answer = 17 + 25; Në këtë shembull, vlera e shprehjes 17 + 25 do të llogaritet dhe i caktohet variablës answer. Vini re se përdorimi i simbolit = është pak i pakuptueshëm: nuk përdoret për të nënkuptuar barazimin matematikor! Aktualisht do të ishte më mirë të përdorim simbolin e definimit := (që përdoret në gjuhën programuese Pascal dhe derivatet e tij) ose një shigjetë <-, por fatkeqësisht hartuesit e gjuhës programuese JAVA kanë vendosur ndryshe. Në shembullin e mësipërm, ana e djathtë e vlerëdhënies është një shprehje konstante (ajo përcaktohet në 42). Megjithatë, ana e djathtë nuk ka nevojë të jetë konstante dhe mund të jetë një shprehje që përmban variabla. Një shprehje e vlerëdhënies së tillë është: c = 2 * a + b; Në këtë shembull, vlera e variablës a shumëzohet me 2 dhe i shtohet vlera e variablës b. Vlera rezultuese ruhet në variablën c. Shpesh, vlerëdhënia është një operacion në variablën në anën e majtë të simbolit të barazimit. Një shembull tipik mund të jetë një deklaratë që shton numrin 5 në vlerën e variablës l. Për të bërë këtë, deklarata ’normale’ duhet të jetë l = l + 5. Megjithatë, operatori special i vlerëdhënies += ekziston për këtë vlerëdhënie të veçantë: l += 5; Në mënyrë të ngjashme, kemi operatorët: -=, *= dhe /=. Është e zakonshme të përdoret një variabël si një numërues i thjeshtë që nevojitet të rritet ose të zvogëlohet në mënyrë të rregullt. Sigurisht, ne mund të rrisim një variabël counter për një, me deklaratën counter = counter + 1 ose counter += 1. Megjithatë, ekziston një tjetër shënim i shkurtuar për këtë vlerëdhënie. Ky shënim është i formës <variabla>++;. Operatori ++ quhet operatori i inkrementimit (rritjes). Kështu që, deklarata counter = counter + 1 mund të impementohet si:
2.3. Variablat 9 counter++; Në mënyrë të ngjashme, kemi operatorin e dekrementimit (zvogëlimit): counter- -; Ekzistojnë dy versione të operatorëve të inkrementimit dhe dekrementimit. Versioni i inkrementimit i shpjeguar më sipër quhet operatori i pas-inkrementimit (ang. postincrement), meqë simboli ++ shfaqet menjëherë pas variablës counter. Versioni tjetër është operatori i para-inkrementimit (ang. pre-increment) në të cilin simboli ++ shfaqet menjëherë para variablës. Një shembull për këtë do të ishte: ++counter; Diferenca ndërmjet dy versioneve është delikate. Operatorët e para-inkrementimit dhe pas-inkrementimit inkrementojnë (ose dekrementojnë) operandën e tyre për 1 dhe vlera e shprehjes është vlera rezultuese e inkrementuar (ose dekrementuar). Në anën tjetër, operatorët e pas-inkrementimit dhe pas-dekrementimit inkrementojnë (ose dekrementojnë) vlerën e operandit të tyre për 1, por vlera e shprehjes është vlera origjinale e operandit para operacionit të inkrementimit (ose dekrementimit). Kjo është qartësuar në shembullin e mëposhtëm: a = 40; b = a++; c = ++a; Pas ekzekutimit të këtij fragmenti të kodit, ne kemi a = 40, b = 40 dhe c = 42. Vini re se rreshti i dytë dhe i tretë i këtij shembulli janë në fakt dy vlerëdhënie në një deklaratë të vetme! Kodi i fragmentit të mësipërm të programit është ekuivalent me kodin vijues: a = 40; /* b = a++ është ekuivalent me */ b = a; a = a + 1; c = ++a është ekuivalent me */ a = a + 1; c = a; Meqë operatorët e inkrementimit/dekrementimit modifikojnë operandën e tyre, përdorimi i një operatori të tillë më shumë se një herë brenda të njëjtës shprehje mund të prodhojë rezultate të padefinuara. Për shembull, në deklaratat e tilla si x = (x- -) * (++x) nuk është e qartë në çfarë radhe operatorët e inkrementimit dhe dekrementimit janë kryer. Prandaj, kurrë mos përdorni ndërtime të pakuptimshme sikur kjo!
10 Kapitulli 2. Konceptet bazë të programimit 2.4 Llojet e të dhënave 2.4.1 Numrat e plotë (integers) Një numër i plotë është një numër pa komponentin e pjesshëm. Për shembull, 21, 4, 0 dhe -2048 janë numra të plotë, ndërsa 2.5 nuk është një numër i plotë. Gjuha programuese JAVA ka lloje të ndryshme për ruajtjen e numrave të plotë. Këto lloje dallojnë në vlerën maksimale (dhe minimale) që mund të arrijnë. Numrat e plotë përfaqësohen në kompjuter nga vargje të njëshave dhe zerove (të quajtura bite). Një bit mund të ketë vetëm një nga dy vlerat: zero ose një. Një sekuencë e n biteve në një rresht mund të përdoret për të koduar secilin numër të plotë pa shenjë (jo-negativ) më të vogël se 2 n , në mënyrë unike. Ky përfaqësim quhet përfaqësim binar i numrit të plotë. Në parim, ky përfaqësim punon njëjtë si përfaqësimi decimal i një numri. Për shembull, me numrin decimal 135 ne kodojmë në fakt 135 = 1 · 100 + 3 · 10 + 5 = 1 · 102 + 3 · 101 + 5 · 100 . Në mënyrë të ngjashme, përfaqësimi binar (16-bitësh) i numrit 135 është: (135)10 = (0000000010000111)2, meqë 135 = 128 + 4 + 2 + 1 = 1 · 2 7 + 0 · 2 6 + 0 · 2 5 + 0 · 2 40 · 2 31 · 2 21 · 2 11 · 2 0 . Sigurisht, me një sekuencë të n bitëve ne mund të përfaqësojmë vetëm 2 n numra të plotë (numrat prej 0 deri në 2 n − 1). Prandaj, për numrat e barabartë ose më të mëdhenj se 216 ne kemi nevojë për më shumë se 16 bite. Në mënyrë që të paraqesim numrat negativë, ne duhet të përfshijmë shenjën minus në përfaqsimin binar të një numri. Ekzistojnë disa përfaqësime, por të gjitha ato kërkojnë një bit për shenjën. Efektivisht, kjo përgjysmon vlerën maksimale që ne mund të përfaqësojmë për çdo numër të plotë. Për shembull, me n bite ne mund të përfaqësojmë numrat e plotë me shenjë nga - 2 n−1 në 2 n−1 − 1. Ne arrijmë në tabelën vijuese: lloji i të dhënave bitët minimumi maksimumi short 16 -32768 32767 int 32 -2147483648 2147483647 long 64 -9223372036854775808 9223372036854775807 Për qëllime më praktike, lloji int ofron bitë të mjaftueshëm për të përfaqësuar numrat e plotë që përdorim në programet tona pa pasur nevojë të shqetësohemi nëse numri është me apo pa shenjë. Prandaj, ne do të përdorim atë si lloj të paracaktuar (ang. default) të të dhënave për të ruajtur numra të plotë. Nëse dëshironi të përdorni një numër të plotë konstant decimal në programin tuaj, ju thjesht mund të përdorni shënimin decimal p.sh., x = 42. Megjithatë, mos filloni numrat decimalë brenda programit tuaj të paraprirë me 0! Siç duket, zero paraqet një përjashtim këtu. Numrat që fillojnë me 0 interpretohen si të ashtuquajtur numra oktalë: 042 interpretohet pastaj si 34 (d.m.th. 4 * 8 + 2). Kështu që, caktimi x = 042 është ekuivalent me caktimin x = 34. Përveç numrave oktalë, ekzistojnë gjithashtu edhe numrat heksadecimalë. Gjatë këtij kursi, ne do të përdorim vetëm numrat decimalë.
2.4. Llojet e të dhënave 11 2.4.2 Numrat me pikë lëvizëse Sigurisht, ndonjëherë na nevojiten numrat jo të plotë të tillë si 3.14159. Këta numra quhen numra me pikë lëvizëse (ose numra jo të plotë). Llojet float dhe double përdoren për ruajtjen e numrave me pikë lëvizëse. Se si këta numra përfaqësohen në formatin binar varet nga kompajleri dhe kompjuteri që ne përdorim. E vetmja gjë që është e specifikuar është që lloji double ka të paktën më shumë precizitet (dhe rang) se lloji float. Se cilën ta zgjedhni varet në aplikacionin që ju jeni duke programuar. Vini re se vlerat me pikë lëvizëse në kodin programin tuaj shkruhen duke përdorur një pikë (.), p.sh., 3.14 (nuk përdoret 3,14). Për më tepër, me e dikush mund të paraqesë eksponentët në shënimin shkencor. Për shembull, 2.3e − 2 përfaqëson 2.3x10−2 ose 0.023. Për një kompjuter standard ne paraqesim rangjet vijuese: lloji i të dhënave bitët maksimumi float 32 ±1038 double 64 ±10308 2.4.3 Karakteret dhe teksti Një shkronjë e vetme quhet karakter. Shembuj të karaktereve janë: ’a’, ’A’, ’z’, ’Z’, ’6’, ’+’ dhe ’-’. Një karakter ka llojin e të dhënave char. Karakteret në kodin burimor të një programi janë të mbyllur me thonjëza njëshe: char first = ’A’; char last = ’Z’; Karakteret përfaqësohen si numra të plotë (të vegjël). Ky lloj i të dhënave është mjaftueshëm i gjerë për të përfaqësuar secilin karakter si një vlerë pozitive. Në lidhje me llojin char ekziston një keqkuptim i përhershëm. Nuk është e vërtetë se një char gjithmonë përfaqësohet nga një bajt (8 bitë). Për shkak se karakteret përfaqësohen si numra, ne mund të zbatojmë operacione aritmetike në to. Për shembull, shprehja ’1’ + ’2’ është valide. Rezultati është një karakter, kështu që nuk është teksti "21" as karakteri ’3’. Rezultati i saktë fitohet duke mbledhur vlerat numerike 49 dhe 50 (lokacionet e ’1’ dhe ’2’ në tabelën ASCII të karaktereve), kështu që fitojmë karakterin në lokacionin 99 në tabelën ASCII, që është karakteri ’c’. Siç mund ta shihni në tabelën 2.1, karakteret ’A’, ’B’,..., ’Z’ janë nënsekuenca të njëpasnjëshme në tabelën ASCII . Kështu që, shprehja ’A’ + 1 është mënyra e komplikuar për të shkruar ’B’. E njëjta gjë vlen edhe për shkronjat ’a’, ’b’,..., ’z’ dhe për shifrat ’0’, ’1’,..., ’9’. Kështu që, shprehja ’5’ + 1 jep karakterin ’6’. Një sekuencë e karaktereve (teksti) quhet String. Stringjet në program janë të mbyllur me thonjëza të dyfishta. Shpesh, ne dëshirojmë të përfshijmë karaktere jo të printueshme në një string. Kjo mund të bëhet duke përdorur përfaqësime speciale për këto
12 Kapitulli 2. Konceptet bazë të programimit TABELA 2.1: Tabela ASCII (American Standard Code for Information Interchange). Decimal Hex Char Decimal Hex Char Decimal Hex Char Decimal Hex Char 0 0 [NULL] 32 20 [SPACE] 64 40 @ 96 60 ‘ 1 1 [START OF HEADING] 33 21 ! 65 41 A 97 61 a 2 2 [START OF TEXT] 34 22 " 66 42 B 98 62 b 3 3 [END OF TEXT] 35 23 # 67 43 C 99 63 c 4 4 [END OF TRANSMISSION] 36 24 $ 68 44 D 100 64 d 5 5 [ENQUIRY] 37 25 % 69 45 E 101 65 e 6 6 [ACKNOWLEDGE] 38 26 & 70 46 F 102 66 f 7 7 [BELL] 39 27 ’ 71 47 G 103 67 g 8 8 [BACKSPACE] 40 28 ( 72 48 H 104 68 h 9 9 [HORIZONTAL TAB] 41 29 ) 73 49 I 105 69 i 10 A [LINE FEED] 42 2A * 74 4A J 106 6A j 11 B [VERTICAL TAB] 43 2B + 75 4B K 107 6B k 12 C [FORM FEED] 44 2C , 76 4C L 108 6C l 13 D [CARRIAGE RETURN] 45 2D - 77 4D M 109 6D m 14 E [SHIFT OUT] 46 2E . 78 4E N 110 6E n 15 F [SHIFT IN] 47 2F / 79 4F O 111 6F o 16 10 [DATA LINK ESCAPE] 48 30 0 80 50 P 112 70 p 17 11 [DEVICE CONTROL 1] 49 31 1 81 51 Q 113 71 q 18 12 [DEVICE CONTROL 2] 50 32 2 82 52 R 114 72 r 19 13 [DEVICE CONTROL 3] 51 33 3 83 53 S 115 73 s 20 14 [DEVICE CONTROL 4] 52 34 4 84 54 T 116 74 t 21 15 [NEGATIVE ACKNOWLEDGE] 53 35 5 85 55 U 117 75 u 22 16 [SYNCHRONOUS IDLE] 54 36 6 86 56 V 118 76 v 23 17 [ENG OF TRANS. BLOCK] 55 37 7 87 57 W 119 77 w 24 18 [CANCEL] 56 38 8 88 58 X 120 78 x 25 19 [END OF MEDIUM] 57 39 9 89 59 Y 121 79 y 26 1A [SUBSTITUTE] 58 3A : 90 5A Z 122 7A z 27 1B [ESCAPE] 59 3B ; 91 5B [ 123 7B { 28 1C [FILE SEPARATOR] 60 3C < 92 5C \ 124 7C | 29 1D [GROUP SEPARTATOR] 61 3D = 93 5D ] 125 7D } 30 1E [RECORD SEPARATOR] 62 3E > 94 5E ^ 126 7E ∼ 31 1F [UNIT SEPARATOR] 63 3F ? 95 5F _ 127 7F [DEL] karaktere. Për shembull, një thyerje rreshti në një string përfaqësohet nga \n. Shënimi që përdor një backslash (\) para një karakteri të printueshëm quhet escaping. Për shembull, fragmenti i programit System.out.print("The quick brown fox \njumps over the lazy dog.\n"); do të rezultojë në daljen: The quick brown fox jumps over the lazy dog. Në mënyrën e njëjtë, \t paraqet një karakter tab. Thonjëzat e dyfishta në një string (normalisht kjo do të tregojë fundin e stringut) gjithashtu mund të shënohen si karakter escape, kështu që përdorim shënimin \" për të përfshirë thonjëzat e dyfishta në një string. Backslash gjithashtu shënohet si karakter escape, kështu që shënojmë \\. Një shembull më i komplikuar është: System.out.println("3\t4\t5\t\\12\n 4\t5\t6\t\\\"15\"\n"); Ky fragment kodi do të prodhojë daljen e mëposhtme:
2.5. Paraqitja e tekstit me printf 13 3 4 5 \12 4 5 6 \"15" 2.5 Paraqitja e tekstit me printf Metoda System.out.printf (f nënkupton "formatted") paraqet të dhënat e formatuara. Fragmenti i mëposhtëm i kodit përdor këtë metodë për të paraqitur stringjet "Welcome to" dhe "JAVA Programming!". System.out.printf("%s \n%s \n", "Welcome to", "JAVA Programming!"); Rezultati do të paraqitet si: Welcome to JAVA Programming! Ky fragment kodi invokon metodën System.out.printf për të paraqitur rezultatin e programit. Thirrja e metodës specifikon tre argumente. Kur një metodë kërkon argumente të shumta, ato vendosen në një listë të ndarë me presje. Ky fragment kodi paraqet një deklaratë të vetme: JAVA lejon që deklaratat e gjata të ndahen në disa rreshta. Argumenti i parë i metodës printf është një string formatimi që përbëhet nga një tekst dhe specifikues të formatit. Teksti afishohet nga printf ashtu siç do të realizohej nga print ose println. Secili specifikues i formatit është një vendmbajtës për një vlerë dhe specifikon llojin e të dhënës që afishohet. Specifikuesit e formatit gjithashtu mund të përfshijnë informacion opsional formatimi. Specifikuesit e formatit fillojnë me një shenjë përqindjeje (%) të ndjekur nga një karakter që përfaqëson llojin e të dhënës. Për shembull, specifikuesi i formatit %s është një vendmbajtës për një string. Stringu formatizues në fragmentin e mësipërm të kodit specifikon që printf do të afishojë dy stringje, secili i ndjekur nga një karakter rresht i ri. Në pozicionin e specifikuesit të parë të formatit, printf zëvendëson vlerën e argumentit të parë pas stringut formatues. Në secilin pozicion të specifikuesit të njëpasnjëshëm të formatit, printf zëvendëson vlerën e argumentit të ardhshëm. Ky shembull zëvendëson "Welcome to" për %s e parë dhe "JAVA Programming!" për %s e dytë. Dalja tregon se si dy rreshtat e tekstit janë paraqitur. Argumenti i parë i funksionit printf është specifikimi i formatit, që është një string, dhe kështu që duhet të mbyllet me thonjëza dyfishe. Argumentet tjera (nëse ka) janë një listë e vlerave që do të printohen sipas formatit të specifikuar. Për këtë kurs është e mjaftueshme të dimë specifikuesit e mëposhtëm të formatimit: • %s : string • %c : një karakter • %d : numër i plotë decimal • %.f : numër jo i plotë
14 Kapitulli 2. Konceptet bazë të programimit • %x : numër heksadecimal • %o : numër oktal • %b : boolean • %n : rresht i ri • %% : përqindja Për më tepër, ekzistojnë disa simbole speciale që mund të përdoren për të formatuar daljen: • \n : thyerje rreshti (rresht i ri) • \r : carriage return (kursori kalon në fillim të rreshtit aktual) • \t : tab • \b : backspace • \\ : printon një backslash (\) • \": printon një thonjëz të dyfishtë (") Në vazhdim janë disa shembuj që demonstrojnë përdorimin e metodës printf: 1 public static void main(String [] args) { 2 int a = 10, b = 25; 3 System.out.printf("%d * %d = %d \n", a, b, (a * b)); 4 System.out.printf("Emri i tij eshte %s. \n", "John"); 5 System.out.printf("PI = %.5f \n", 3.14159); 6 System.out.printf("%c = %.5f \n", ’e’, 2.71828); 7 } dhe si rezultat marrim: 10 * 25 = 250 Emri i tij eshte John. PI = 3.14159 e = 2.71828 2.6 Shprehjet Një shprehje është një formulë në program që përcakton një vlerë të një lloji të caktuar. Siç do ta shihni, ky është një koncept i gjerë. Në vazhdim janë disa shembuj shprehjesh. 5 a b + 1 ”Hello” b * (7 + a) ’A’ + 1 a > b Le të supozojmë se variablat a dhe b janë të llojit int të të dhënave. Emri i një variable është vetë një shprehje që përcakton vlerën e asaj variable. Për shembull, b + 1 është shprehja që rrit vlerën e b për një (që është poashtu e llojit int të të dhënave). Mund të ndryshojmë rendin në të cilin shprehjet llogariten duke përdorur kllapat, ngjashëm si në llogaritjet e zakonshme. Në këtë rast përdoren rregullat e prioritetit të
2.6. Shprehjet 15 operatorëve: për shembull, shprehja b * 7 + a nënkupton (b * 7) + a në vend të b * (7 + a). Secila shprehje jep një vlerë të një lloji të caktuar. Për shembull, shprehja 2 + 4 jep një vlerë të llojit int të të dhënave, ndërsa ’A’ + 1 jep një karakter (char). Një rast i veçantë është shprehja a > b. Kjo quhet shprehje logjike (ang. boolean), që është true ose false. Në JAVA ekziston një lloj i veçantë i të dhënave, i rezervuar për shprehjet logjike, boolean. Kodi në vazhdim, inicializon variablën b në false: boolean b = 5 < 2; 2.6.1 Operatorët aritmetikorë Operatorët bazikë aritmetikorë, mbledhja, zbritja, shumëzimi dhe pjesëtimi mund të përkthehen në operatorët që përdoren në JAVA. Mbledhja dhe zbritja përdorin thjesht + dhe -, shumëzimi realizohet me * dhe pjesëtimi me /. Rregullat e prioritetit të operatorëve janë standarde: shumëzimi dhe pjesëtimi kanë prioritet më të lartë se mbledhja dhe zbritja. Për shembull, 5 * 8 + 2 jep si rezultat 42 (dhe jo 50 = 5 * (8 + 2)) Nëse të dy operandët e operatorit / janë vlera int, atëherë vlera rezultuese gjithashtu do të jetë një vlerë int. Ky rast njihet si pjesëtimi i numrave të plotë: nëse të dy operandët janë vlera int pozitive, atëherë shifrat pas presjes dhjetore largohen. Për shembull, 5 / 2 jep 2 (dhe jo 2.5). Pjesëtimi i numrave të plotë largon pjesën jo të plotë, pra nuk e rrumbullakson në njëshen më të afërt numrin e dhënë: vlera e 999 / 1000 prandaj është 0! Nëse të paktën një nga operandët e operatorit / është një vlerë me presje dhjetore, atëherë vlera rezultuese është gjithashtu një vlerë me presje dhjetore: vlera e 5.0 / 2 është 2.5 (ashtu siç pritet të jetë). Në fakt, shprehjet 5 / 2.0 dhe 5.0 / 2.0 japin 2.5 si rezultat. Përtej pjesëtimit të numrave të plotë, ekziston operatori modulo (%), i cili jep mbetjen pas pjesëtimit. Për shembull, 17 % 5 jep vlerën 2 (meqë 17 = 3 * 5 + 2). Vini re se të dy operandët e operatorit modulo duhet të jenë të llojit int. Të keni parasysh se vlera e a % b nuk ka nevojë të jetë në intervalin [0, b). Nëse a është më e vogël se zero, atëherë a % b do të jetë një vlerë negative. Për shembull, shprehja -17 % 5 jep -2 (meqë -17 == -3 * 5 - 2). Identiteti në vijim gjithmonë është i saktë për numrat e plotë x dhe y (me përjashtim të y == 0): (x / y) * y + x % y == x 2.6.2 Operatorët matematikorë Përtej operatorëve bazikë aritmetikorë, JAVA gjithashtu ofron disa nga operacionet e avancuara matematikore që mund të gjenden në disa kalkulatorë shkencorë. Këto funksione gjenden në paketën java.lang në klasën Math. Nuk ka nevojë të importohet klasa nga kjo paketë duke qenë se paketa është e importuar vetvetiu në momentin kur fillojmë të shkruajmë një klasë në JAVA.
16 Kapitulli 2. Konceptet bazë të programimit Në klasën Math janë të definuara disa funksione matematikore. Gjatë këtij kursi nuk do të përdorim të gjitha funksionet që ofrohen nga kjo klasë, por është mirë të dimë se janë në dispozicion. Disa nga këto funksione janë dhënë në tabelën 2.2. TABELA 2.2: Funksionet matematikore në JAVA. Funksioni Funksioni në JAVA √ x sqrt(x) |x| abs(x) sin(x), cos(x), tan(x) sin(x), cos(x), tan(x) sin−1 (x), cos−1 (x), tan−1 (x) asin(x), acos(x), atan(x) e x exp(x) x y pow(x, y) ln(x) log(x) log10(x) log10(x) 2.6.3 Konvertimi implicit Nëse shumëzojmë dy numra të plotë (ints), rezultati do të jetë gjithashtu një int. Por, cili është lloji rezultues i shumëzimit të një double dhe një int? Në këtë rast, kompajleri automatikisht konverton llojin e të dhënave që ka kapacitet më të vogël të vlerave të lejuara (këtu int) në një vlerë të llojit të të dhënave që ka kapacitet më të madh të vlerave të lejuara (këtu double), dhe pastaj ekzekutohet shumëzimi. Vlera rezultuese është më e madhe nga të dy llojet e të dhënave, prandaj në këtë rast duhet të jetë double. Ky lloj i konvertimit automatik njihet si konvertim implicit. Konvertimi automatik i llojit të të dhënave bëhet nga kompajleri (në kohën e kompajlimit). Në shprehjet në të cilën operandet kanë lloje të ndryshme të të dhënave, një lloj duhet të ketë kapacitet më të madh të vlerave të lejuara, i cili është ’superlloji’ i kësaj shprehjeje. Në shprehjet e tilla, nënllojet me kapacitet më të vogël konvertohen në mënyrë implicite në superllojin. Shprehja llogaritet, dhe rezultati është një vlerë e superllojit. Për shembull, merrni parasysh pjesën e mëposhtme të kodit: short a = 1; int b = 2; long c = 3; c = (a + b) + c; Sigurisht, vlera përfundimtare e c është 6, por është interesante të shikojmë se si llogaritet shprehja (a + b) + c. Së pari, llogaritet shprehja a + b meqë gjendet brenda kllapave. Për shkak se, a ka llojin e të dhënave short dhe b ka llojin e të dhënave int, vlera e a konvertohet në mënyrë implicite në int dhe pastaj i shtohet vlerës së variablës b. Rezultati, prandaj është një vlerë int. Kjo vlerë, pastaj konvertohet në llojin e të dhënave long dhe i shtohet variablës c, duke rezultuar në vlerën 6 të llojit të të dhënave long. Kjo vlerë i caktohet variablës c. Llojet e të dhënave për numrat e plotë konvertohen nga llojet me kapacitet të vogël të vlerave të lejuara në ato me kapacitet më të madh, d.m.th., në drejtimin char → short → int → long. Një konvertim i tillë i numrave të plotë është gjithmonë i sigurtë, d.m.th., nuk ka gabime që ndodhin. E njëjta vlen dhe është e saktë për
2.6. Shprehjet 17 konvertimin e numrave me presje lëvizëse. Ata konvertohen në drejtimin float → double. Megjithatë, nëse tentojmë të përziejmë llojet e të dhënave për numrat e plotë dhe numrat me presje lëvizëse në një shprehje të vetme, atëherë mund të ndodhin gabime të pazakonshme. Për shembull, është definuar se një int konvertohet automatikisht në një float (meqenëse janë të dyja 32 bitë në një PC) ose një double. Për shkak të granularitetit të përfaqësimit të numrave me presje lëvizëse, kjo mund të rezultojë në një konvertim të pasigurtë thjesht për shkak se disa vlera ekzakte int nuk mund të përfaqësohen si një vlerë ekzakte float. Merrni parasysh deklarimin dhe inicializimin e mëposhtëm: double x; x = 3; Ndoshta nuk e vini re, por në këtë caktim vlere ndodh: vlera 3 int konvertohet në vlerën 3.0 double meqë ana e majtë e caktimit të vlerës është një double. Merrni parasysh pjesën e mëposhtme të kodit: short a = 50; int b = 100; double x; x = a / b; Mund të mendojmë se vlera përfundimtare e x është 0.5, por në fakt është 0.0 meqë pjesëtimi a / b rezulton në vlerën 0 int (pjesëtimi i numrave të plotë largon pjesën jo të plotë). Pastaj, vlera 0 int konvertohet në vlerën 0.0 double dhe i caktohet variablës x. 2.6.4 Konvertimi eksplicit Shënimi që përdoret për konvertimin eksplicit është: (<lloji i synuar i të dhënave>) shprehja Për shembull, me (int) 3.7 konvertojmë vlerën 3.7 double në vlerën 3 int. Vini re se vlerat me presje lëvizëse që konvertohen në vlera të plota nuk rrumbullaksohen: shifrat pas presjes dhjetore largohen. Sigurisht, nëse konvertohet një lloj me kapacitet më të madh të vlerave të lejuara në një lloj me kapacitet më të vogël, do të humbë preciziteti. Për shembull, nëse konvertojmë një vlerë int (që është 32 bitë në një PC) në një vlerë short (që është 16 bitë), atëherë humbin disa bitë. Kjo do të thotë që në një PC, një vlerë int që kërkon më shumë se 16 bitë për tu përfaqësuar në mënyrë të saktë, nuk do të i ’mbijetojë’ konvertimit në një vlerë short. Rezultati nuk është i definuar, por ka shumë gjasa që vlera short përfaqësohet nga 16 bitët e fundit të vlerës origjinale int.
18 Kapitulli 2. Konceptet bazë të programimit Operatori i konvertimit eksplicit ka prioritet më të lartë. Për shembull, (int) 3.5 * 2 llogaritet në 6 meqë 3.5 konvertohet në int (pra 3) para shumëzimit. Kështu që, nevojitet të shënohet (int) (3.5 * 2) për të marrë vlerën 7. Mbledhja e një vlere int dhe një vlere char automatikisht konvertohet në një vlerë int. Është e pranueshme të ndodhë një gjë e tillë. Megjithatë, nëse rezultati i kësaj mbledhjeje caktohet në një variabël të llojit char, ka mundësi që rezultati të jetë më i madh se vlera më e madhe që është e mundshme në bashkësinë e karaktereve. Kjo quhet mbingarkesë. Printimi i një karakteri të tillë mund të prodhojë karaktere interesante në ekran. 2.6.5 Operatorët logjikë Ne tashmë e dimë se për shprehjet logjike (boolean-e), që llogariten në vlerat true ose false, numrat e plotë përdoren me interpretimin se zero përdoret për të përfaqësuar vlerën false, ndërsa ndonjë vlerë jo-zero përdoret për të përfaqësuar vlerën true. Operatorët logjikë janë operatorë që kanë shprehje logjike si operande dhe kthejnë një vlerë logjike (boolean-e) si rezultat. Këta operatorë rrjedhin nga algjebra boolean-e (logjike) dhe janë përmbledhur në tabelën 2.3. operacioni shqiptimi kuptimi a && b “a and b” true (1) nëse të dyja a dhe b janë true (1), përndryshe false (0) a || b “a or b” true (1) nëse a ose b ose të dyja janë true (1), përndryshe false (0) !a “not a” true (1) nëse a është false (0), përndryshe false (0) a ^ b “a xor b” true (1) nëse a ose b është true (1) por jo të dyja, përndryshe false (0) TABELA 2.3: Operatorët logjikë në JAVA. Në vazhdim janë disa shembuj se si përdoren këta operatorë: readyToGo = doorIsLocked && !lightsOn; canBuy = (hasCash && money >= price) || hasCreditCard; Prioriteti relativ i këtyre operatorëve është (nga më i lartë në më të ulët) !, ^, &&, ||. Për shembull, shprehja logjike 1 || 1 && 0 || 0 llogaritet në true (1), meqë ajo llogaritet si 1 || (1 && 0) || 0 (dhe jo si (1 || 1) && (0 || 0) që do të jepte false si rezultat). Për lexim më të qartë, është shpesh më mirë të përdorim kllapa edhe pse rregullat e prioritetit lejojnë ti largojmë ato. Evaluimi i operatorëve logjikë me dy argumente kryhet në një mënyrë më të shkurtuar: nëse rezultati i operatorit është i njohur për shkak të vlerës të operandit të parë, atëherë vlera e oparandit të dytë nuk llogaritet. Për shembull, është e sigurtë (por e
2.6. Shprehjet 19 çuditshme) të shënohet (1 == 2) && (1 / 0 == 3). Meqë (1 == 2) u llogarit në 0 (false), rezultati i operatorit && do të jetë 0. Prandaj, shprehja 1 / 0 == 3 nuk llogaritet në tërësi dhe një gabim ’division by zero’ nuk do të ndodhë. Shprehja (1 / 0 == 3) && (1 == 2) është ekuivalente logjikisht (matematikisht), por do të rezultojë në një gabim ’division by zero’. Kryesisht në deklarimet e kushtëzuara (shih pjesën 2.7) do të përdorim këtë karakteristikë mjaft shpesh. 2.6.6 Operatorët relacionalë Shpesh, na nevojitet të krahasojmë vlerat e dy shprehjeve. Janë gjashtë operatorë relacionalë (ose operatorë të krahasimit) në dispozicion: operatori në JAVA < <= == >= > ! = kuptimi matematikor < ≤ = ≥ ≤ 6= TABELA 2.4: Operatorët relacionalë në JAVA. Rezultati i një krahasimi (d.m.th., vlera e shprehjes) është ose false (0) (në rastin kur krahasimi është jo i saktë) true ose (1) (në rastin kur krahasimi është i saktë). Vini re se krahasimi ndërmjet vlerave të dy shprehjeve asnjëherë nuk kthen ndonjë numër tjetër pozitiv (si 2, ose 42), edhe pse të gjithë numrat e plotë pozitivë konsiderohen të përfaqësojnë vlerën logjike true. Shpesh, operatori i vlerëdhënies = shënohet aksidentalisht ku mendohet operatori i krahasimit ==. Ky gabim shpesh nuk detektohet nga kompajleri, sepse operatori i vlerëdhënies është një shprehje valide që jep vlerën e caktuar. Kështu, a == 3 jep një vlerë logjike që tregon nëse a ka vlerën 3! Shprehja e vlerëdhënies y = 3 * (x = 3) është një mënyrë e ndërlikuar (dhe e shëmtuar) për të caktuar në variablën x vlerën 3 dhe në variablën y vlerën 9 në një deklaratë të vetme. 2.6.7 Prioriteti i operatorëve JAVA zbaton operatorët në shprehjet aritmetike në një sekuencë precize të përcaktuar nga rregullat e prioritetit të operatorëve, që janë përgjithësisht të njëjtë si ato që ndiqen në algjebër: • Operacionet e shumëzimit, pjesëtimit dhe mbetjes zbatohen së pari. Nëse një shprehje përmban disa operacione të tilla, ato zbatohen nga e majta në të djathtë. Operatorët e shumëzimit, pjesëtimit dhe mbetjes kanë të njëjtin nivel të prioritetit. • Operacionet e mbledhjes dhe zbritjes zbatohen në vazhdim. Nëse një shprehje përmban disa operacione të tilla, operatorët zbatohen nga e majta në të djathtë. Operatorët e mbledhjes dhe zbritjes kanë të njëjtin nivel të prioritetit. Këto rregulla mundësojnë që JAVA të zbatojë operatorët në rendin e duhur. Kur themi që operatorët zbatohen nga e majta në të djathtë, ireferohemi në shoqërimin e tyre. Disa operatorë shoqërohen nga e djathta në të majtë. Tabela 2.5 përmbledh rregullat e prioritetit të operatorëve.
20 Kapitulli 2. Konceptet bazë të programimit Oparatorët Operacionet Operatorët Operacionet () kllapat & bitwise AND [] ^ bitwise exclusive OR ++ inkrementimi | bitwise inclusive OR – dekrementimi && AND i kushtëzuar ! negacioni || OR i kushtëzuar * shumëzimi ? : operatori ternar / pjesëtimi = caktimi % mbetja += caktimi i mbledhjes + mbledhja -= caktimi i zbritjes - zbritja *= caktimi i shumëzimit > më e madhe se /= caktimi i pjesëtimit >= më e madhe se ose e barabartë me %= caktimi i mbetjes < më e vogël se &= caktimi i bitwise AND <= më e vogël se ose e barabartë me ^= caktimi i exclusive OR == e barabartë me | caktimi i inclusive OR != jo e barabartë me TABELA 2.5: Prioriteti i operatorëve. 2.7 Deklarimet e kushtëzuara Procesi i kontrollimit të rrjedhjes dhe degëzimit të programit mbështetet në gjendjen aktuale të programit. JAVA ka deklarimet e mëposhtme të kushtëzuara: • if për të specifikuar bllokun e kodit që do të ekzekutohet, nëse një kusht i caktuar është i saktë • else për të specifikuar bllokun e kodit që do të ekzekutohet, nëse kushti i njëjtë nuk është i saktë • else if për të specifikuar një kusht të ri testimi, nëse kushti i parë nuk është i saktë • switch për të secifikuar disa blloqe alternative të kodit që do të ekzekutohen 2.7.1 Deklarimi if Sintaksa e deklarimit if është si vijon: 1 if(kushti logjik) { 2 /* blloku i kodit qe ekzekutohet nese kushti eshte i sakte;*/ 3 //... 4 } 5 else { 6 /*blloku i kodit qe ekzekutohet nese kushti nuk eshte i sakte; */ 7 //... 8 } Kushti duhet të jetë një shprehje logjike dhe prandaj vlera e saj duhet të jetë logjike, true ose false. Nëse kushti logjik është i saktë, ekzekutohen deklarimet e degëzimit if. Nëse kushti logjik nuk është i saktë, atëherë ekzekutohen deklarimet e degëzimit else. Kjo paraqitet në mënyrë skematike në figurën 2.1. Një shembull: PROGRAMI 2.5: Deklarimi if-else: Numri çift apo tek
2.7. Deklarimet e kushtëzuara 21 vlera e kushtit logjik? then statements else statements true false FIGURA 2.1: Përfaqësimi skematik i deklarimit if-else. 1 public class CiftTek { 2 public static void main(String [] args) { 3 int x = 8; 4 if (x % 2 == 0) { 5 /* x eshte cift pjeseto me 2 */ 6 x /= 2; 7 } else { 8 /* x eshte tek shumezo me 3 dhe shto 1 */ 9 x = 3 * x + 1; 10 } 11 System.out.println(x); 12 //Afishohet 4 13 } 14 } 2.7.2 Deklarimi else Shpesh, nuk paraqitet nevoja për degëzimin else, në të cilin rast mund të largohet: 1 if(kushti logjik) { 2 /* blloku i kodit qe ekzekutohet nese kushti eshte i sakte;*/ 3 //... 4 } Një shembull pa degëzimin else: PROGRAMI 2.6: Deklarimi if: Numri maksimal 1 public class Max { 2 public static void main(String [] args) { 3 /* x == A, y == B*/
22 Kapitulli 2. Konceptet bazë të programimit 4 if (x < y) { 5 x = y; 6 } 7 /* x == max(A, B)*/ 8 } 9 } 2.7.3 Deklarimi else if Në ndonjë rast na nevojitet të definojmë edhe ndonjë kusht tjetër logjik nëse kushti i parë logjik në deklarimin if nuk është i saktë. Për këtë problem përdorim një deklarim else if: 1 if(kushti i pare logjik) { 2 /*blloku i kodit qe ekzekutohet nese kushti i pare eshte i sakte;*/ 3 //... 4 } 5 else if(kushti i dyte logjik) { 6 /*blloku i kodit qe ekzekutohet nese kushti i pare nuk eshte i sakte dhe kushti i dyte logjik eshte i sakte;*/ 7 //... 8 } 9 else { 10 /*blloku i kodit qe ekzekutohet nese kushti i pare nuk eshte i sakte dhe kushti i dyte nuk eshte i sakte;*/ 11 //... 12 } Një shembull që përfshin përdorimin e deklarimit else if: PROGRAMI 2.7: Deklarimi if-else if-else: Ora 1 public class KohaIme { 2 public static void main(String [] args) { 3 int time = 22; 4 if (time < 10) { 5 System.out.println("Miremengjes."); 6 } else if (time < 20) { 7 System.out.println("Miredita."); 8 } else { 9 System.out.println("Mirembrema."); 10 } 11 //Afishohet Mirembrema. 12 } 13 } 2.7.3.1 Gjetja e vlerës maksimale: Një shembull Supozojmë se dëshirojmë të gjejmë vlerën maksimale të disa vlerave. Për të zgjidhur këtë problem mund të përdorim disa strategji (metoda). Duke përdorur deklarimet e kushtëzuara if ... else if ... else mund ta arrijmë këtë zgjidhje. Shembulli në vazhdim jep këto tri strategji zgjidhjeje për problemin e gjetjes së vlerës maksimale në mesin e tre vlerave të dhëna.
2.7. Deklarimet e kushtëzuara 23 PROGRAMI 2.8: Strategjia e parë: Krahasimi i secilës vlerë me vlerat tjera 1 public class MaxS1 { 2 public static void main(String [] args) { 3 int x1 = 13, x2 = 9, x3 = 3; 4 int max; 5 6 if((x1 >= x2) && (x2 >= x3)) 7 max = x1; 8 else if ((x2 >= x1) && (x2 >= x3)) 9 max = x2; 10 else 11 max = x3; 12 13 System.out.println(max); 14 } 15 } Strategjia e parë krahason secilën vlerë me të gjitha vlerat e tjera. Kjo strategji bën më shumë krahasime ndërmjet vlerave të dhëna. Duke krahasuar secilën vlerë me vlerat e tjera të dhëna, në fund në variablën max do të mbetet vlera e cila ka kaluar të gjitha testimet në degëzimet e kushtëzuara, e cila është maksimalja. PROGRAMI 2.9: Strategjia e dytë: Pema e vendimmarrjes 1 public class MaxS2 { 2 public static void main(String [] args) { 3 int x1 = 13, x2 = 19, x3 = 3; 4 int max; 5 6 if (x1 >= x2) { 7 if (x1 > x3) 8 max = x1; 9 else 10 max = x3; 11 } 12 else { 13 if (x2 > x3) 14 max = x2; 15 else 16 max = x3; 17 } 18 System.out.println(max); 19 } 20 } Strategjia e dytë është e ngjashme me të parën, veçse këtu është treguar në mënyrë më të detajuar shtegu i cili ndiqet në momentin kur plotësohet njëri nga kushtet e dhëna. PROGRAMI 2.10: Strategjia e tretë: Përpunimi sekuencial 1 public class MaxS3 { 2 public static void main(String [] args) { 3 int x1 = 13, x2 = 19, x3 = 3; 4 int max = x1; 5
24 Kapitulli 2. Konceptet bazë të programimit 6 if (x2 >= max) 7 max = x2; 8 if (x3 >= max) 9 max = x3; 10 11 System.out.println(max); 12 } 13 } Strategjia e tretë përcakton mënyrën më të përdorur në gjetjen e vlerës maksimale. Duke supozuar fillimisht se vlera maksimale është vlera e parë (në rastin tonë variabla x1), vazhdojmë krahasimin e vlerave të mbetura të dhëna vetëm me maksimumin. Herën e parë që vlera e ndonjë variable tjetër kalon testimin në kushin përkatës atëherë ajo vlerë do të caktohet të jetë vlera maksimale e ruajtur në variablën max. Secili nga tre programet e mësipërme në fakt zgjidhin problemin e njëjtë: atë të gjetjes së vlerës maksimale në mesin e tre vlerave. Mirëpo, secila strategji e përdorur zbaton një lloj të ndryshëm logjike. Strategjia e tretë mbetet të jetë metoda e përdorur në programet të cilat brenda tyre kërkojnë dhe zbatojnë algoritmin e gjetjes së vlerës maksimale në një varg numrash (shih 5 për vargjet). 2.8 Shprehjet e kushtëzuara 2.8.1 Operatori ternar JAVA ofron një operator të kushtëzuar (? :) që mund të përdoret në vend të një deklarate if ... else. Ky është i vetmi operator ternar në JAVA (operatori që merr tre operande). Së bashku, operandët dhe simboli ? : formojnë një shprehje të kushtëzuar. Sintaksa bazë: shprehja1 ? shprehja2 : shprehja3 Operandi i parë shprehja1 është një shprehje logjike (d.m.th., një kusht që llogaritet në një vlerë logjike —true ose false), operandi i dytë shprehja2 është vlera e shprehjes së kushtëzuar nëse shprehja logjike është true dhe operandi i tretë shprehja3 është vlera e shprehjes logjike nëse shprehja logjike është false. PROGRAMI 2.11: Gjetja e vlerës absolute duke përdorur operatorin ternar 1 public class OpTernar { 2 public static void main(String [] args) { 3 System.out.println("Vlera absolute" + ( i < 0 ? - i : i)); 4 } 5 } Programi printon vlerën e argumentit së shprehjes së kushtëzuar të funksionit println. Shprehja e kushtëzuar në këtë deklaratë llogaritet në - i nëse shprehja logjike i < 0 është e saktë dhe në i nëse shprehja logjike është e pasaktë. Kështu që, kjo deklaratë me operatorin e kushtëzimit kryen të njëjtin funksion si deklarata if ... else. Prioriteti i operatorit të kushtëzuar është i ulët, kështu që e gjithë shprehja e kushtëuar vendoset në kllapa. Do të shohim se shprehjet e kushtëzuara mund të përdoren në disa situata ku deklarimet if ... else nuk munden.
2.9. Deklarata switch 25 2.9 Deklarata switch Deklarimi if ... else është i kufizuar nga fakti se mund të konsiderohen vetëm dy raste. Ndonjëherë është e nevojshme të konsiderojmë më shumë se dy raste. Këtë mund ta zgjidhim duke përdorur edhe deklarimin nga pjesa 2.7.3 ose në formën e deklarimit if ... else të mbivendosur (nested) si në shembullin në vijim: PROGRAMI 2.12: Deklarimi if ... else if ... else: Nota 1 public class VleresimiIfElse { 2 3 public static void main(String [] args) { 4 int nota = 4; 5 if (nota == 5) { 6 System.out.println("shkelqyeshem"); 7 } else { 8 if (nota == 4) { 9 System.out.print("shume mire"); 10 } else { 11 if (nota == 3) { 12 System.out.print("mire"); 13 } else { 14 if (nota == 2) { 15 System.out.print("mjaftueshem"); 16 } else { 17 System.out.print("dobet"); 18 } 19 } 20 } 21 } 22 } 23 } Shembulli i mësipërm ka 4 kushte të ngjashme. Deklarimet e mbivendosura if ... else e bëjnë të vështirë programin të lexohet dhe kuptohet, si dhe japin një strukturë të pakëndshme të programit. Prandaj, është më mirë të përdoret deklarimi switch. Në deklarimin switch mund të krahasoni vlerën e variablës nota me vlera konstante pa përdorur deklarimet if të mbivendosura. Nëse nota është e barabartë me një nga vlerat e listuara, atëherë ekzekutohet deklarimi korrespondues në case. Kështu që, ne mund të rishkruajmë shembullin e mësipërm si vijon: PROGRAMI 2.13: Deklarimi switch: Nota 1 public class VleresimiSwitch { 2 3 public static void main(String [] args) { 4 int nota = 4; 5 switch (nota) { 6 case 5: System.out.println("shkelqyeshem"); 7 break; 8 case 4: System.out.print("shume mire"); 9 break; 10 case 3: System.out.print("mire"); 11 break; 12 case 2: System.out.print("mjaftueshem"); 13 break; 14 default: System.out.print("dobet");
26 Kapitulli 2. Konceptet bazë të programimit 15 } 16 } 17 } Kur rrjedha e ekzekutimit të programit arrin në deklarimin break, ekzekutimi do të vazhdojë në kllapën mbyllëse të deklarimit switch. Nëse vlera e variablës nota nuk është e barabartë me vlerën në një nga cases atëherë ekzekutohen deklarimet në default. Duhet të jeni të kujdesshëm për karakteristikën e deklarimit switch që quhet fallthrough. Një fall-through ndodh kur ekzekutimi ka arritur fundin e kodit të një case-i në të cilin është përputhur vlera përkatëse dhe e cila nuk përfundon me një deklarim break. Në këtë rast, ekzekutimi vazhdon ("falls through") në deklarimet e shoqëruara me case-in e ardhshëm. Kjo karakteristikë përdoret në shembullin e mëposhtëm (vini re notat 2 dhe 3): PROGRAMI 2.14: Deklarimi switch: Nota 2 1 public class VleresimiSwitchFTh { 2 3 public static void main(String [] args) { 4 int nota = 3; 5 switch (nota) { 6 case 5: System.out.println("shkelqyeshem"); 7 break; 8 case 4: System.out.println("shume mire"); 9 break; 10 case 3: System.out.println("mire"); 11 case 2: System.out.println("mjaftueshem"); 12 break; 13 default: System.out.println("dobet"); 14 } 15 } 16 } Meqë variabla nota përmban vlerën 3, atëherë do të ekzekutohen të dy cases njëkohësisht, edhe case 3 edhe case 2, dhe si rezultat do të kemi: mire mjaftueshem 2.10 Klasa Scanner Përveç paraqitjes së daljes në ekran ne gjithashtu mund të përdorim hyrjen nga tastiera. Klasa Scanner përmban funksione të ndryshme për leximin e vlerave hyrëse nga tastiera, ngjashëm se si funksioni println shkruan daljen në ekran. Këto funksione të klasës Scanner mundësojnë leximin e vlerave që i përkasin llojeve të ndryshme të të dhënave. Për të përdorur këtë klasë, nevojitet të krijohet një objekt i klasës dhe mund të përdorim ndonjë nga funksionet e kësaj klase, por së pari nevojitet të importohet klasa nga paketa java.util, dhe kjo deklaratë duhet të jetë rreshti i parë në program: import java.util.Scanner;
2.10. Klasa Scanner 27 Shembulli në vazhdim paraqet leximin e dy numrave të plotë nga tastiera dhe llogaritjen e prodhimit të tyre: PROGRAMI 2.15: Klasa Scanner: Shembull 1 import java.util.Scanner; 2 3 public class ScannerEx { 4 5 public static void main(String [] args) { 6 int x, y; 7 Scanner input = new Scanner(System.in); 8 System.out.print("Jepni vleren e x-it: "); 9 x = input.nextInt(); 10 System.out.print("Jepni vleren e y-it: "); 11 y = input.nextInt(); 12 System.out.println("Prodhimi i " + x + " dhe " + y + " eshte: " + (x * y)); 13 } 14 } Në shembullin e mësipërm, kemi përdorur metodën nextInt() për të lexuar numrat e plotë (integers). Për të lexuar llojet tjera të të dhënave duhet të përdorim metodat përkatëse të cilat paraqiten në tabelën 2.6: TABELA 2.6: Metodat e klasës Scanner për të lexuar vlerat hyrëse nga tastiera në JAVA. Metoda Përshkrimi nextBoolean() Lexon një vlerë boolean nga një përdorues nextByte() Lexon një vlerë byte nga një përdorues nextDouble() Lexon një vlerë double nga një përdorues nextFloat() Lexon një vlerë float nga një përdorues nextInt() Lexon një vlerë int nga një përdorues nextLine() Lexon një vlerë String nga një përdorues nextLong() Lexon një vlerë long nga një përdorues nextShort() Lexon një vlerë short nga një përdorues Në shembullin e mëposhtëm, përdorim metoda të ndryshme për të lexuar të dhënat e llojeve të ndryshme, nga tastiera: PROGRAMI 2.16: Klasa Scanner: Shembull 2 1 import java.util.Scanner; 2 3 public class ScannerEx1 { 4 5 public static void main(String [] args) { 6 Scanner input = new Scanner(System.in); 7 System.out.println("Sheno emrin dhe rrogen: "); 8 9 // String input 10 String name = input.nextLine(); 11 12 // Numerical input 13 int age = input.nextInt(); 14 double salary = input.nextDouble();
28 Kapitulli 2. Konceptet bazë të programimit 15 16 // Output input by user 17 System.out.println("Name: " + name); 18 System.out.println("Age: " + age); 19 System.out.println("Salary: " + salary); 20 } 21 } Vini re se nëse gabimisht shënoni hyrjen e gabuar (p.sh., tekst në vend të një numri), do të paraqitet një mesazh përjashtimi-gabimi (e tillë si "InputMismatchException"). Përjashtimet i trajtojmë më vonë. 2.11 Modeli Hyrje-Procesim-Dalje Modeli Hyrje-Procesim-Dalje (ang. Input Process Output (IPO model)) është qasja më e përdorur në analizimin e sistemeve dhe inxhinierinë softuerike për të përshkruar strukturën e një programi kompjuterik ose ndonjë procesi tjetër. Shumica e teksteve të hyrjes në programim dhe analizimit të sistemeve prezantojnë këtë model si strukturën bazë për përshkrimin e një procesi. Një program kompjuterik ose ndonjë lloj tjetër procesi që përdor modelin hyrjeprocesim-dalje (input-process-output model) pranon hyrje nga përdoruesi ose ndonjë burim tjetër (p.sh., një fajll), bën disa llogaritje me hyrjet, dhe pastaj kthen rezultatin e këtyre llogaritjeve të kryera. Sistemi ndan punën në tre komponente: • Hyrja - një listë e të dhënave që jepen me problemin • Dalja - një listë e daljeve që kërkohen • Procesimi - një listë e veprimeve që nevojiten të prodhojnë daljet e kërkuara. Ky është gjithashtu algoritmi që ju shkruani! Për shembull, një program mund të shkruhet për të konvertuar temperaturën nga Farenheit në Celsius. Duke ndjekur modelin IPO, programi duhet: • Të pyesë përdoruesin për temperaturën në Farenheit (hyrja) • Të kryejë një llogaritje për të konvertuar temperaturën nga Farenheit në temperaturën korresponduese në Celsius (procesimi) • Të paraqesë temperaturën në Celsius (dalja) PROGRAMI 2.17: Modeli IPO: Shembull 1 1 import java.util.Scanner; 2 3 public class IPOModelEx1 { 4 5 public static void main(String [] args) { 6 Scanner input = new Scanner(System.in); 7 //hyrja (input) 8 System.out.print("Temperatura ne Farenheit: "); 9 double f = input.nextDouble(); 10 11 //procesimi (process) 12 double c; 13 c = (f - 32) * 5 / 9; 14 15 //dalja (output)
2.11. Modeli Hyrje-Procesim-Dalje 29 16 System.out.println(f + " Farenheit eshte " + c + " Celsius" ); 17 } 18 } Temperatura ne Farenheit: 100 100 Farenheit eshte 37.7777777777778 Celsius
30 Kapitulli 2. Konceptet bazë të programimit 2.12 Detyra praktike 2.12.1 Caktimet dhe Shprehjet Vërejtje paraprake: në komente (ndërmjet /* dhe */) ka deklarime për gjendjen e variablave. Deklarimi për gjendjen para caktimit quhet para-kusht. Deklarimi për gjendjen pas ekzekutimit të caktimit quhet pas-kusht. Mbani në mend që shenja = përdoret për vlerëdhënien dhe nuk do të thotë barazi. Në deklarimet (para dhe pas kushteve) barazia shënohet me shenjën ==. Ushtrimi 2.12.1.1 — Caktimet e përbëra. Variablat x,y,z janë deklaruar si vijon: int x, y, z; Llogaritni në secilin nga rastet e mëposhtme pas-kushtin në formën e dhënë. 1. /*x==A,y==B*/ x=x-y;y=y-x /*x==?,y==?*/ 2. /*x==A,y==B*/ x=x+y;y=x-y;x=x-y /*x==?,y==?*/ 3. /*x+y==A,x+z==B*/ x=x+y;y=y-z /*x==?,y==?*/ 4. /*3x+9y==A*/ x=x+y;y=2*y /*?x+?y==A*/ Ushtrimi 2.12.1.2 — Derivimi i deklarimeve të përbëra. Variablat x,y,z janë deklaruar si vijon: int x, y, z; Gjeni për secilën nga rastet e mëposhtme një deklaratë (të përbërë) që jep paskushtin e dhënë duke filluar nga një gjendje fillestare (iniciale) me të cilën përputhet para-kushti. 1. /*x==A,y==B*/ ? /*x==A+B,y==A-B*/ 2. /*x==A,y==B,z==C*/ ? /*x==B,y==C,z==A*/ 3. /*3x+8y==A*/ ? /*x+y==A*/ 4. /*x==A,y==B,z==C*/ ? /*x==y,x+y+z==A+B+C*/ 5. /*x==A,y==B*/ ? /*x===max(A,B),z==A+B*/ 6. /*x==A,y==B,A>0,B>0*/ ? /*x*y<0,x+y==min(A,B)*/ 7. /*x==A*/ ? /*A==5*y+z,0≤z<5*/ Ushtrimi 2.12.1.3 — Pjesëtimi i numrave të plotë dhe mbetja pas pjesëtimit. Variablat x,y,z janë deklaruar si vijon: int x, y, z; Gjeni për secilën nga kërkesat e mëposhtme një shprehje ekuvalente në JAVA: 1. x është tek. 2. x është një pjesëtues i y. 3. x është një pjesëtues i y dhe y është një pjesëtues i z 4. Ose x është një pjesëtues i y ose y është një pjesëtues i z, por jo dyjat së bashku. 5. Shifra e fundit e x (në shënimin decimal) është 7. 6. Dy shifrat e fundit të x (në shënimin decimal) janë 7.
2.12. Detyra praktike 31 2.12.2 Programe të thjeshta Në detyrat e mëposhtme nevojitet të zhvilloni programe të vogla për probleme relativisht të thjeshta. Detyra 2.12.2.1 Paketimi Shkruani një program që pyet përdoruesin të shënojë gjatësinë e një brinje të një kubi dhe dimensionet e një kutie, dhe printon numrin maksimal të kubeve që mund të vendosen në kutinë. Vini re se brinjët e secilit kub duhet të jenë paralele me brinjët e kutisë. Mund të supozoni se të gjitha gjatësitë janë numra të plotë (në centimetër). Detyra 2.12.2.2 Rrumbullaksimi Në shumicën e supermarketeve çmimet rrumbullaksohen në 5 centë. Shkruani një program që pyet përdoruesin të shënojë numrin e centëve që duhet të paguhen dhe printon ato të rrumbullaksuar në 5 centë. Detyra 2.12.2.3 Teorema e Pitagorës Shkruani një program që pyet përdoruesin të shënojë gjatësinë e tre brinjëve të një trekëndëshi dhe pastaj printon nëse ky trekëndësh është kënddrejtë ose jo. Mund të supozoni që gjatësitë e brinjëve janë numra të plotë. Detyra 2.12.2.4 Mbivendosja Një rreth mund të përfaqësohet nga koordinatat e qendrës së tij dhe rrezës së tij. Në këtë detyrë, ju mund të supozoni se koordinatat dhe rrezja janë numra të plotë. Në mënyrë të qartë, rrezja është numër jo-negativ. Shkruani një program që lexon të dhënat e dy rrathëve dhe pastaj printon nëse këta dy rrathë (pjesërisht) mbivendosen në njëri tjetrin ose jo. Vini re që dy rrathë që preken ndërmjet tyre konsiderohen të jenë të mbivendosur.
32 Kapitulli 3 Iterimi Në JAVA ekzistojnë tre struktura të ndryshme të iterimit (përsëritjes) : deklarimi for, deklarimi while dhe deklarimi do while. Deklarimi for përdoret për iterimet ku paraprakisht është i njohur numri i hapave. Dy deklarimet e fundit përdoren për iterimet ku nuk është i njohur paraprakisht numri i hapave. Një strukturë e përsëritjes gjithashtu quhet cikël (ang. loop). Secili lloj i ciklit ka një trup dhe një kusht terminimi. Trupi i ciklit përmban deklarimet që do të përsëriten. Kushti i terminimit paraqet një shprehje logjike që përcakton se sa herë do të ekzekutohet trupi i ciklit. 3.1 Cikli while Sintaksa e deklarimit while është si vijon: 1 while(kushti i terminimit) { 2 trupi i ciklit; 3 } Deklarimet e trupit përsëriten përderisa kushti i terminimit llogaritet në true. Një përfaqësim skematik i kësaj paraqitet në figurën 3.1. Zakonisht, deklarimet e trupit të ciklit modifikojnë variablat në kushtin e terminimit ashtu që përfundimisht kushti i terminimit llogaritet në false dhe cikli terminon. Siç mund ta shihni edhe nga figura 3.1, kushti i terminimit testohet para se të ekzekutohet deklarata e parë e trupit të ciklit. Kështu që, nëse kushti i terminimit llogaritet në false para se të fillojë cikli, trupi i ciklit asnjëherë nuk do të ekzekutohet. Çfarë mund të themi në lidhje me vlerën e variablave në kushtin e terminimit në fillim të trupit të ciklit (rasti në të cilin kushti i terminimit plotësohet), dhe gjendjen menjëherë pas ciklit while? Pas ekzekutimit të ciklit, është e qartë që kushti i terminimit është false, përndryshe cikli nuk do të kishte përfunduar. Nga ana tjetër, në fillim të trupit të ciklit ne e dimë se kushti i terminimit është true, përndryshe cikli asnjëherë nuk do të kishte filluar. Ky shpjegim është paraqitur në fragmentin vijues të kodit: 1 while (kushti i terminimit) { 2 /*Ne kete pike, kushti i terminimit plotesohet (eshte true)*/ 3 trupi i ciklit; 4 } 5 /*Ne kete pike, kushti i terminimit nuk plotesohet (eshte false)*/ Një shembull konkret është paraqitur në vazhdim:
3.2. Cikli do-while 33 kushti i terminimit trupi i ciklit true false FIGURA 3.1: Përfaqësimi skematik i deklarimit while. PROGRAMI 3.1: Deklarimi while: Shembull 1 2 public class WhileEx { 3 4 public static void main(String []args) { 5 int i = 1; 6 while (i != 11) { 7 /*Ne kete pike: i != 11*/ 8 System.out.println("i * 2 = " + (i * 2)); 9 i++; 10 } 11 /*Ne kete pike: i == 11 */ 12 } 13 } 3.2 Cikli do-while Deklarimi do while përdoret nëse dëshirojmë që trupi i ciklit të ekzekutohet të paktën njëherë (pavarësisht vlerës fillestare të kushtit të terminimit). Sintaksa e tij është: 1 do { 2 trupi i ciklit; 3 } while(kushti i terminimit); Pasi të ekzekutohet trupi i ciklit, llogaritet shprehja logjike që përfshihet në kushtin e terminimit. Nëse kushti i terminimit është true, cikli do të ekzekutohet përsëri. Përndryshe, cikli terminon (përfundon). Struktura e këtij cikli është paraqitur në mënyrë skematike në figurën 3.2. Vini re se nuk mund të jemi të sigurtë që kushti i terminimit plotësohet në fillim të trupit të ciklit, meqë mund të mos jetë true në iterimin e parë. E vetmja gjë që mund të themi me siguri është që kushti i terminimit është false pas ekzekutimit të ciklit: 1 do { 2 /*Ne kete pike kushti i terminimit mund te jete false vetem ne itermin e pare*/
34 Kapitulli 3. Iterimi trupi i ciklit kushti i terminimit false true FIGURA 3.2: Përfaqësimi skematik i deklarimit do while. 3 trupi i ciklit; 4 } while(kushti i terminimit); 5 /*Ne kete pike, kushti i terminimit nuk plotesohet (eshte false)*/ Një shembull konkret është paraqitur në vazhdim: PROGRAMI 3.2: Deklarimi do while: Shembull 1 2 public class DoWhileEx { 3 4 public static void main(String []args) { 5 int i = 1; 6 do { 7 System.out.println("i * 2 = " + (i * 2)); 8 } 9 while (i != 11); 10 /*Ne kete pike: i == 11 */ 11 } 12 } 3.3 Cikli for Forma më e përgjithshme e deklarimit for: 1 for(inicializimi; kushti i terminimit; modifikimi) { 2 trupi i ciklit; 3 } Këtu, inicializimi dhe modifikimi janë deklarime. Semantika e ciklit for shpjegohet thjeshtë duke e konvertuar së pari në një formë ekuivalente të ciklit while.
3.3. Cikli for 35 Forma e përgjithshme e ciklit for është ekuivalente me ciklin vijues while: 1 inicializimi; 2 while(kushti i terminimit) { 3 trupi i ciklit; 4 modifikimi; 5 } Nga ky kod ekuivalent, është e lehtë të shohim se çfarë është semantika e ciklit for. Së pari, ekzekutohet inicializimi. Në vazhdim, ekzekutohet trupi i ciklit për aq kohë sa kushti i terminimit llogaritet në true. Në secilin iterim, menjëherë pas ekzekutimit të trupit të ciklit, ekzekutohet deklarimi i modifikimit. Zakonisht, deklarimi i modifikimit modifikon një ose më shumë variabla që përfundimisht bëjnë kushtin e terminimit, false. Struktura e ciklit for është paraqitur në mënyrë skematike në figurën 3.3. inicializimi kushti i terminimit trupi i ciklit modifikimi false true FIGURA 3.3: Përfaqësimi skematik i deklarimit for. Një përdorim i zakonshëm i ciklit for është iterimi nëpër një bashkësi të numrave të plotë, për shembull brenda një intervali numrash [0..10]: PROGRAMI 3.3: Deklarimi for: Shembull 1 2 public class ForEx { 3 4 public static void main(String []args) { 5 int sum = 0, i = 0; 6 for(i = 0; i < 10; i++) { 7 sum += i; 8 } 9 System.out.println("Shuma e numrave nga 1 deri ne 10 eshte: " + sum);
36 Kapitulli 3. Iterimi 10 } 11 } E kemi parë që mund të rishkruajmë ndonjë cikël for si një cikël while. Megjithatë, edhe e kundërta është e saktë. Është gjithashtu e mundur të konvertojmë një cikël while në një formë ekuivalente të ciklit for për shkak se inicializimi, kushti i terminimit dhe modifikimi i një cikli for janë opsionale dhe kështu që mund të largohen. Kështu që, secili cikël while mund të konvertohet lehtësisht në një cikël for si vijon: 1 for( ; kushti i terminimit; ) { 2 trupi i ciklit; 3 } Kështu që, në një mënyrë mund të themi që cikli while dhe cikli for janë ekuivalentë. Dallimi ndërmjet dy cikleve është tërësisht konceptual. Zgjedhja se cilin lloj cikli ta përdorim nuk është një shkencë ekzakte, por thjesht një çështje preference. Një cikël for zakonisht përdoret për të iteruar nëpër një bashkësi vlerash. Në anën tjetër, cikli while zakonisht përdoret për të ekzekutuar trupin e ciklit për aq kohë sa kushti i terminimit plotësohet. Nëse shkruani një cikël for të cilit i mungon inicializimi, mund të jetë më mirë të përdorni ciklin while. Nëse shënoni një cikël while që së pari nevojitet të inicializohet një variabël, që modifikohet në fund të trupit të ciklit, atëherë është më mirë të përdorni një cikël for. 3.4 Ciklet dhe Akumulatori Akumulatori është një variabël që programi përdor për të llogaritur shumën ose prodhimin e një vargu të numrave. Programi kompjuterik e bën këtë duke përdorur një cikël që shton ose shumëzon secilën vlerë të njëpasnjëshme në akumulatorin. Për të vizualizuar se si punon kjo, përpiquni të mbledhni disa numra në kokën tuaj, të themi 12, 7, 9, 10. Shumica e njerëzve e bëjnë këtë duke mbajtur gjurmë të vlerës totale të akumuluar. Në vazhdim jepet një shembull i zakonshëm i përdorimit të një cikli definitiv (të caktuar) dhe një akumulatori. Ky është specifikimi i programit: • lexo një numër të plotë pozitiv n • llogarit 1 + 2 + 3 + ... + n • printo formulën dhe rezultatin Për shembull, nëse n është 5, atëherë dalja duhet të paraqesë: 1 + 2 + 3 + 4 + 5 = 15 PROGRAMI 3.4: Përdorimi i akumulatorit: Llogaritja e shumës së një vargu numrash 1 2 import java.util.Scanner; 3 4 public class ShumaVargu { 5 6 public static void main(String []args) {
3.5. Llogaritja e vlerës mesatare: Një shembull 37 7 Scanner s = new Scanner(System.in); 8 System.out.print("Shtyp n: "); 9 int n = s.nextInt( ); 10 int acc = 0; 11 12 for (int i = 1; i <= n; i++) { 13 acc += i; 14 System.out.print("+" + i); 15 } 16 17 System.out.println(" = " + acc); 18 } 19 } 3.5 Llogaritja e vlerës mesatare: Një shembull Supozojmë se dëshirojmë të shkruajmë një program që mund të llogarisë mesataren e një vargu të numrave të pranuar nga tastiera, si hyrje. Programi duhet të punojë me çfarëdo numri të numrave. Nuk është e nevojshme që të mbajmë shënime për çdo numër të dhënë, mjafton që të dimë shumën e numrave dhe sa numra janë dhënë (numrin e numrave). Algoritmi për llogaritjen e mesatares së numrave të dhënë nga tastiera jepet në vazhdim: • lexo numrin e numrave, n • inicializo sum me 0 • përsërit n herë – çdo herë lexo vlerën e x-it – vlerën e x-it shtoje në sum • afisho mesataren si sum / n PROGRAMI 3.5: Deklarimi for: Vlera mesatare 1 2 import java.util.Scanner; 3 4 public class VleraMesatare1 { 5 6 public static void main(String []args) { 7 Scanner s = new Scanner(System.in); 8 System.out.println("Jepni numrin e numrave, ose vleren e nit: "); 9 int n = s.nextInt(); 10 double acc = 0.0; 11 for(int i = 0; i < n; i++) { 12 System.out.println("jepni vleren e numrit numer " + (i + 1) + ": "); 13 double num = s.nextDouble(); 14 acc = acc + num; 15 } 16 System.out.println("Vlera mesatare: " + (acc / n)); 17 } 18 }
38 Kapitulli 3. Iterimi 3.5.1 Ciklet e pacaktuara Problemi është që numri i përsëritjeve duhet të jetë i paradefinuar. Cikli for është i definuar, që do të thotë se numri i përsëritjeve përcaktohet në momentin kur cikli fillon. Kështu që, kemi nevojë për një mjet tjetër! Ciklet e pacaktuara ose të kushtëzuara (while) vazhdojnë iterimin (përsëritjen) përderisa kushti plotësohet. Një përdorim i mirë i cikleve të pacaktuara është shkruarja e cikleve interaktive. Ciklet interaktive lejojnë që përdoruesi në bazë të kërkesës të përsërisë pjesë të caktuara të një programi. Algoritmi për llogaritjen e mesatares së numrave të dhënë nga tastiera duke përdorur ciklet interaktive jepet në vazhdim: • inicializo sum me 0.0 • inicializo numratori me 0 • perseritja është "po" • while perseritja është "po" – lexo vlerën e x-it – vlerën e x-it shtoje në sum – rrit numratori për një – pyet përdoruesin nëse dëshiron të vazhdojë të japë vlera • afisho mesataren si sum / numratori PROGRAMI 3.6: Deklarimi for: Vlera mesatare përmes ciklit interaktiv 1 2 import java.util.Scanner; 3 4 public class VleraMesatareWhile { 5 6 public static void main(String []args) { 7 Scanner s = new Scanner(System.in); 8 Scanner s1 = new Scanner(System.in); 9 10 double sum = 0.0; 11 int numratori = 0; 12 char perseritja = ’p’; 13 14 while(perseritja == ’p’ || perseritja == ’P’) { 15 System.out.print("Jepni vleren e x-it: "); 16 double x = s.nextDouble(); 17 sum += x; 18 numratori++; 19 System.out.print("Shtypni p per te vazhduar: "); 20 perseritja = s1.nextLine().charAt(0); 21 } 22 System.out.println("Vlera mesatare: " + (sum / numratori)); 23 } 24 } 3.5.2 Cikli përcjellës Cikli përcjellës vazhdon të përpunojë të dhënat deri në arritjen e vlerës së veçantë e cila e sinjalizon fundin. Kjo vlerë e veçantë quhet përcjellësi, i cili duhet të dallohet nga të dhënat që përpunohen si pjesë e të dhënave.
3.5. Llogaritja e vlerës mesatare: Një shembull 39 PROGRAMI 3.7: Deklarimi for: Vlera mesatare përmes ciklit përcjellës 1 2 import java.util.Scanner; 3 4 public class VleraMesatareUnazaPercjellese { 5 6 public static void main(String []args) { 7 Scanner s = new Scanner(System.in); 8 Scanner s1 = new Scanner(System.in); 9 10 double sum = 0.0; 11 int numratori = 0; 12 13 System.out.print("Jepni vleren e x-it: (negative per daljen ): "); 14 double x = s.nextDouble(); 15 16 while(x >= 0) { 17 sum = sum + x; 18 numratori++; 19 System.out.print("Jepni vleren e x-it: (negative per daljen): "); 20 double x = s.nextDouble(); 21 } 22 System.out.println("Vlera mesatare: " + sum / numratori); 23 } 24 } 3.5.3 Cikli sentinel Cikli sentinel vazhdon të përpunojë të dhënat derisa përdoruesi nuk shtyp tastin Enter, i cili e sinjalizon fundin. Algoritmi për llogaritjen e mesatares së numrave të dhënë nga tastiera jepet në vazhdim duke përdorur ciklin sentinel: • inicializo sum me 0.0 • inicializo numratori me 0 • lexo vlerën e parë si string, xStr • përsërit derisa xStr nuk është ”” – shndërro vlerën e x-it në numër – vlerën e x-it shtoje në sum – rrit numratori për një – lexo vlerën e x-it si string, xStr • afisho mesataren si sum / numratori PROGRAMI 3.8: Deklarimi for: Vlera mesatare përmes ciklit sentinel 1 2 import java.util.Scanner; 3 4 public class VleraMesatareSentinelLoops { 5 6 public static void main(String []args) { 7 Scanner s = new Scanner(System.in); 8
40 Kapitulli 3. Iterimi 9 double sum = 0.0; 10 int numratori = 0; 11 12 System.out.println("Jepni vleren e x-it: (shtypni Enter per daljen): "); 13 String xStr = s.nextLine(); 14 15 while(!xStr.isEmtpy()) { 16 sum = sum + Double.parseDouble(xStr); 17 numratori++; 18 System.out.println("Jepni vleren e x-it: (shtypni Enter per daljen): "); 19 String xStr = s.nextLine(); 20 xStr = s.nextLine(); 21 } 22 System.out.println("Vlera mesatare: " + sum / numratori); 23 } 24 } 3.6 Korrigjimi i një programi Procesi i zbulimit dhe korrigjimit të gabimeve në programet kompjuterike quhet debugging. Është e zakonshme që një gabim programimi të quhet bug. Ky term buron nga koha kur kompjuterët ishin makina shumë të mëdha që ishin të pajisura me transmetues elektrikë të cilët nuk ishin të mbështjellur. Kjo lejonte që insekte të vogla të tilla si mizat ose molat të kapen ndërmjet kontakteve të transmetuesve. Natyrisht, kjo çoi në dështime. Fillimisht, korrigjimi i gabimeve ishte procesi i gjetjes dhe largimit të të metave që shkaktonin mos-funksionimin e makinerisë (Figura 3.4). FIGURA 3.4: Gabimi i gjetur. Nga logbook i kalkulatorit me transmetues të Mark II Aiken në Universitetin e Harvardit, 9 Shtator 1945. Gjetja e një gabimi në një program mund të jetë e vështirë dhe kërkon shumë kohë. Në situata të thjeshta, programeri mund të shtypë vlerat e variablave duke shtuar deklarimin System.out.println(); në program. Duke kontrolluar rezultatet e këtyre deklaratave ndihmëse, programeri mund të jetë në gjendje të ndjekë gabimin dhe ta korrigjojë atë. Për shembull, deklarata shtesë për printimin në fragmentin e
3.6. Korrigjimi i një programi 41 programit vijues ndihmon për të gjetur gabimin ’pjesëtimi me zero’ meqë programi thyhet pasi të shtypet i = 0. 1 int count = 0; 2 int n = 5; 3 for(int i = n; i >= 0; i--) { 4 System.out. println("i = " + i); /*deklarimi i debugging*/ 5 if (n % i == 0) { 6 count++; 7 } 8 } Kjo metodë e debugging zakonisht përdoret kur ekzistojnë vetëm disa variabla që duhet të paraqiten në ekran në mënyrë që të gjejmë gabimin që ka ndodhur. Kur ekzistojnë shumë variabla, kjo metodë mund të bëhet konfuze për shkak të sasisë së madhe të rezultatit të prodhuar. Veçanërisht, në kombinim me ciklet, kjo metodë gjeneron shumë rezultat (korrigjimi) që është i dobishëm.
42 Kapitulli 3. Iterimi 3.7 Detyra praktike 3.7.1 Tipet numerike Ushtrimi 3.7.1.1 Supozoni deklarimet e mëposhtme: short a = 350; int b = 3; int c = 10; long d = 1; float x = 391; double y = 0.71; double z = 0.35; Përcaktoni për secilën nga vlerëdhëniet e mëposhtme llojin e shprehjes në anën e djathtë të shenjës =. Gjithashtu specifikoni nëse caktimi i një shprehje të këtij lloji në variablën në anën e majtë është ’i sigurtë’, d.m.th., nëse lloji i variablës në anën e majtë është të paktën i madh aq sa lloji i shprehjes. Nëse vlerëdhënia është e sigurtë, përcaktoni gjithashtu vlerën e anës së djathtë të vlerëdhënies. 1. d = a * b; 2. c = a / b; 3. y = c / b; 4. y = b + x; 5. a = a + 1; 6. a++; 7. b = a + 1; 8. d = 100 * (x - y); 9. x = (float)(a / b); 10. z = (float)a / c; 11. a = (int)y + x; 12. a = (int)(y + x); 13. a = (short)(y + x); 14. c = (int)a / z; 15. c = a / (int)z; 16. c = (int)(a / z); 3.7.2 Iterimi Në detyrat e mëposhtme nevojitet të zhvilloni programe të vogla për probleme relativisht të thjeshta. Detyra 3.7.2.1 Eksponentimi Shkruani një fragment programi që llogarit b e , me një numër të plotë b të dhënë dhe një numër të plotë pozitiv e. Sigurisht, nuk jeni të lejuar të përdorni funksionet e ndërtuara nga klasa Math.