SITNA CREVCA<>
052017<><>

Kako postati programer (8): Oblast vidljivosti promenljivih i funkcija

Call me maybe

Kolekcije, oblast vidljivosti, callback...

U tekstovima sa početka serijala pomenuli smo kako za C++ i Javu morate unapred zadati kog će tipa biti neka promenljiva (statički tipizirani jezici). Do kraja izvršavanja programa, ove promenljive ne mogu da promene svoj tip (strogo tipizirani jezici).

Vrste jezika prema tipovima, coercion

U JavaScriptu tip promenljive ne morate eksplicitno da zadate, već će interpreter sam zaključiti o kom tipu se radi po pokretanju programa (dinamički tipiziran jezik), a promenljive mogu da promene svoj tip u toku pisanja kôda (slabo tipizirani jezik). Pogledajte primer coercion-a, tj. automatskog menjanja jednog tipa podatka u drugi u JavaScriptu . Da biste isto to uradili npr. u Javi, jedan od načina je da ručno obavestite kompajler da se tipovi „namerno” razlikuju. To možete uraditi putem castovanja, koje govori kompajleru da tip jedne promenljive tretira kao drugi tip. Drugi način je da se koriste ugrađene funkcije u jezicima koje omogućavaju konverziju tipova. Napomena: JavaScript takođe dozvoljava da sami eksplicitno konvertujete tipove.

--------------------------------------- coercion.js

var a = 1 + "2"; // a postaje string "12"

console.log(1 == "1"); // true dolazi do coerciona

console.log(1 === "1");//false === sprecava coercion

-------------------------------------- casting.java

int i = 5; // primitivna prom.

Integer oi = 5; // objekat

double d = 6.6;

d=i; // moze iz uzeg u siri tip

i=d; // !ne moze

i=(int)d;// mora cast-ovanje

// ne postoji primitivni string

String s = "a";

s = (String)i; // !ne moze cast

// mora konverzija tipa

s = String.valueOf(i); // nacin1

s = Integer.toString(i); // nacin2

s = oi.toString(); // nacin3

Dodatno ćemo doprineti konfuziji, jer neki jezici dozvoljavaju da se promenljiva implicitno uvede u program. To znači da umesto tipa promenljive može da stoji samo npr. generičko var. Ako gledamo C#, koji je, generalno gledano, statički tipizirani jezik, kompajler će tip odrediti iz konteksta još u fazi kompajliranja, pre početka rada programa (npr. var N = 5 će N pretvoriti u integer). Ako promenljivu definišete tako što joj dodelite rezultat neke funkcije nejasnog naziva npr. var N = blabla(), onda je bolje umesto var napisati npr. int N = blabla() ako blabla() vraća integer.

Posledica navedenog je da jezici poput JavaScripta omogućavaju da se kôd brzo napiše, bez potrebe da u startu sve bude savršeno definisano. Međutim, zbog toga interpreter jezika ne može da vas unapred upozori na eventualne greške u kôdu (provera se dešava u toku izvršavanja programa tj. runtimea). U vašem kôdu možete nenamerno da promenite tipove putem coerciona. Pošto je sve po pravilima jezika, nećete ni biti upozoreni da greška postoji, a vaš program će davati loše rezultate. U slučaju glomaznih projekata postaje izuzetno teško locirati problematičan kôd. Za veće projekte mnogi JavaScript programeri koriste biblioteke koje uvode strogu tipizaciju podataka i statičku proveru kôda (npr. koristeći Typescript, koji rešava i još nekoliko drugih problema). Tada je provera kôda moguća i pre nego što pokrenete program (compile time). Statički i strogo tipizirani jezici omogućavaju sve vrste provera kako bi vaš program imao što je manje moguće grešaka, ali zahteva dodatni napor dok kôd ne prihvati da se pokrene. Definicije koje smo objasnili nisu striktne, tako da jezici mogu biti više ili manje bliži nekoj od ovih podela.

Kolekcije

Jedno od ograničenja jezika kao što su Java i C++ je to da niz podataka može da sadrži vrednosti samo jednog tipa. Takođe, niz se ne može naknadno proširivati, nakon što ste ga prvi put definisali u programu. U JavaScriptu je situacija obrnuta: u niz možete da dodajete i iz njega brišete elemente različitih tipova u bilo kom trenutku. U prošlom broju smo pomenuli JavaScript objekte koji nekako izgledaju isto kao i nizovi, pa je potrebno znati da postoje razlike. Razlika je u tome da članu objekta pristupate po imenu (stringu), dok elementu niza pristupate preko indeksa (rednog broja elementa u nizu). Niz se obično koristi ako ćete često imati potrebu da „prolazite” kroz niz (što je čest slučaj), a često se niz sastoji od elemenata istog tipa.

U JavaScriptu su nizovi i objekti vrlo fleksibilni, lako je dodavati i brisati elemente itd. Da biste istu funkcionalnost ostvarili u jezicima C++ i Java, ne koriste se nizovi, već posebne strukture podataka – kolekcije. Na primer, slično nizu u JavaScriptu, to je Vector u C++-u ili ArrayList u Javi. Pored listi, postoje skupovi (set) koji predstavljaju kolekciju koja ne može da ima dva ista elementa (dodavanjem istog elementa skup se ne proširuje), zatim mape ili rečnici (map, dictionary), koji su slični objektima u JavaScriptu i zasnovani su na parovima „naziv” : „vrednost”, itd. Kolekcije imaju brojne ugrađene metode kojim ih uređujete ili iz njih izvlačite podatke koji vas interesuju. Vremenom ćete naučiti u kojim slučajevima da koristite koju kolekciju, a razlike su u tome da je jedna kolekcija bolja kada je bitan redosled elemenata, neka je optimizovanija ako često dodajete elemente u sredinu niza, itd.

Boxing Unboxing

U primeru konverzije tipova za Javu sa početka teksta, koristili smo metode objekata Integer i String. Vrlo je važno da pomenemo i razdvojimo postojanje primitivnih tipova u Javi i njihovih parnjaka objekata. Tako, postoji primitivni tip int i objekat Integer. Ranije smo pričali o tome da objekti u Javi mogu imati metode, a ovde ste u prvom listingu mogli da vidite njihove metode za konverziju iz nekompatibilnih tipova int u String. Inače, vrednosti između primitivne promenljive i njegovog pandan objekta se mogu automatski obaviti (autoboxing i unboxing) u zavisnosti od konteksta, tako da je ponekad svejedno šta koristite. Ovo je važno iz više razloga i time ćemo još malo da se pozabavimo u sledećem broju. Na primer, kolekcije u Javi mogu da primaju samo objekte, ne i primitivne vrednosti.

Foreach, Iterator

Većina jezika koristi skraćenu verziju for petlje, koja je zgodna zbog bolje čitljivosti. Ukoliko vam je potreban indeks niza kroz koji prolazite da biste pristupili prethodnom ili sledećem elementu, koristite klasičnu petlju.

--------------------------------- java foreach.java

String[] sladoledi = new String[]{"kapri", "leni", "tigar"};

for (int i = 0; i < sladoledi.length; i++) {

String sladoled = sladoledi[i];

System.out.println(sladoled);//kapri, leni..

}

for (String sladoled : sladoledi) {

System.out.println(sladoled);//kapri, leni..

}

Postoji više načina za kretanje kroz kolekciju, ali imajte u vidu da se često umesto for petlje koriste tzv. iteratori. Korišćenje iteratora je dobro kada u toku kretanja kroz kolekciju želite da neki element obrišete iz nje. Dakle, postoji više načina da se prolazi kroz kolekcije.

------------------------------- java kolekcije.java

import java.util.ArrayList;

import java.util.Iterator;

import java.util.List;

List<String> sladoledi;

sladoledi = new ArrayList<>();

//ili npr. sladoledi = new LinkedList<>();

// mozemo da prosirujemo sladolede naknadno...

sladoledi.add("Kapri");

sladoledi.add("Leni");

//tri primera prolazenja kroz kolekciju

Iterator<String> s = sladoledi.iterator();

while (s.hasNext()) {

System.out.println(s.next());

}

for (String sladoled : sladoledi) {

System.out.println(sladoled);

}

//Java verzija 8, foreach + lambda funkcija

sladoledi.forEach((sladoled) -> {

System.out.println(sladoled);

});

//dobijamo ispis Kapri, Leni u sva tri slucaja

Oblast vidljivosti, Hoisting

Ovo je jedan od osnovnih termina u programiranju. Oblast vidljivosti je na engleskom scope i, iako se donekle razlikuje od jezika do jezika, suština je ista – u kom delu kôda možete pristupiti nekoj promenljivoj (ili funkciji) koju ste definisali na drugom mestu u kôdu. Jedan od razloga što se vidljivost promenljivih ograničava u kôdu je, kao i obično, manja potrošnja resursa, manje promenljivih učestvuje u tom delu programa, pa je lakše protumačiti šta program radi, pronaći eventualne greške itd. Loša strana je što manje iskusni programeri dolaze u situaciju da im u nekom delu kôda nije dostupna promenljiva koja im je u tom trenutku potrebna (što ponekad zahteva i veće prekrajanje kôda).

Obično se promenljive definišu u okviru nekog bloka, na primer klase, funkcije, for petlje... Promenljiva je onda najčešće vidljiva samo u okviru tog bloka i svih ostalih blokova unutar njega. Manje-više svi jezici prate ovakvu oblast vidljivosti tzv. leksičkog tipa (lexical scope). Ako želite da se promenljiva vidi svuda u kôdu, što se u velikom broju slučajeva smatra lošom praksom, može da se deklariše kao globalna. Ako imate dve promenljive istog naziva u ugnježdenim blokovima, prednost ima ona koja je deklarisana u okviru svog bloka. U slučaju OOP klasa, vidljivost se određuje i ključnim rečima private, public itd, o čemu smo ranije pisali.

Pomenućemo sada pojam hoistinga, koji je naročito izražen u JavaScriptu. Često se promenljive i funkcije ne mogu koristiti pre nego što se prvi put definišu. Međutim, interpreter JavaScripta po pokretanju programa njih automatski prebacuje na početak bloka, tako da možete prvo pozvati funkciju, iako ste tek kasnije definisali šta ona radi. Ovde je potrebno biti jako obazriv, jer ne podležu sve funkcije hoistovanju, dok hoistovane promenljive ne sadrže i vrednost koja im je u startu dodeljena. U JavaScriptu se scope promenljive ograničava na blok ako se ispred naziva napiše let, a pre verzije ES6 se ograničavala na vidljivost unutar cele funkcije sa rečju var. Ako nema ključne reči, promenljiva se smatra globalnom (JavaScript).

------------------------------- javascript hoist.js

radiHoist();

// ispisuje "Da!"

neRadiHoist();

// greska!

console.log(x);

//nije greska

//ali ispisuje undefined

function radiHoist() {

console.log("Da!");

}

var neRadiHoist = function () {

console.log("Ne!");

};

var x = 5;

console.log(x);

//ispisuje 5

Funkcije, drugi deo

Postoji više načina da se definiše funkcija u JavaScriptu, jer, pored klasičnog definisanja, ona može biti i anonimna (bez imena), a može da se izvrši i u samom trenutku definisanja (IIFE – Immediately Invoked Function Expression). U JavaScriptu su funkcije objekti prve klase (first-class objects), što znači da neka promenljiva može da bude funkcija i da funkcije mogu biti parametri drugih funkcija.

--------------------------------- vrste funkcija.js

// Function Declaration

function f1() {

}

f1(); // poziv funkcije

// Named Function Expression

var x = function f2() {

};

x(); // poziv funkcije

// Anonymous Function Expression

var y = function () {

};

y(); // poziv funkcije

// IIFE – Immediately Invoked Function Expression

(function(){

})();

Funkcija koju prosleđujete kao parametar drugoj funkciji zove se callback funkcija. Ona će biti pozvana kasnije (called back), zavisno od logike napisane u funkciji koja je prima. Funkcija koja je prima zove se funkcijom višeg reda (higher order function). Kada se funkcija stavlja kao parametar, piše se bez zagrada „()”, jer ne želite da je pokrenete, već samo prosledite. Callback funkcija može biti i anonimna funkcija. Često se anonimne funkcije nazivaju i lambda funkcije, mada je to u JavaScriptu malo širi pojam, koji nećemo pokušavati da objasnimo. Lambda funkcija je funkcija koja je prosleđena kao vrednost. Lambda može biti prosleđena kao argument nekoj funkciji, vraćena sa return iz neke funkcije ili dodeljena nekoj varijabli. Može biti anonimna, ali i ne mora. Lambde nemaju svoje this, već se ono binduje na this iz parent scopea. Primer lambdi su fat arrow („=>”) funkcije.

Callback funkcije se često koriste kôd asinhronih operacija (operacije koje ne blokiraju izvršavanje kôda). Na primer, kada se neka radnja odvija u pozadini, callback može da se pozove po završetku te radnje. Radnja može biti transfer podataka sa Interneta ili neka druga duža procedura. Tako radi jQuery Ajax naredba, koju smo opisali u prethodnom broju. Potrebno je paziti da se ne ugnezdi previše callback funkcija, jer u tom slučaju čitljivost programa drastično opada, što ima i ustaljen naziv: „callback hell”. Nije svaka funkcija koja prima callback funkciju asinhrona (npr. telo funkcija foreach, map, filter...), dok svaka asinhrona funkcija ima callback. Da biste u JavaScriptu sami napravili asinhronu funkciju, morate koristiti promise objekte ili timeout funkcije, no o tome nećemo pričati u našem serijalu, kao ni o callback funkcijama u C++-u ili Javi.

--------------------------------------- callback.js

function callback(pozdrav) {

alert(pozdrav);

}

function primaCallback(pozdrav, primljenCallback) {

primljenCallback(pozdrav);

}

//nacin 1

primaCallback("zdravo", callback);

//ispisuje "zdravo"

//nacin2

//callback funkciju smo pisali

//anonimno, direktno kao argument

primaCallback( "zdravo", function(pozdrav){

alert(pozdrav);

});

//ispisuje "zdravo"

Closure je funkcija (ili njena osobina) koja vidi podatke van svoje oblasti vidljivosti. Closure može da vidi promenljive iz svoje roditeljske funkcije (pretpostavka je da su funkcije ugnježdene), čak i ako je roditeljska funkcija prestala sa izvršavanjem.

--------------------------------- closure.js

function funkcija1() {

var ime = ’Svet kompjutera’;

function funkcija2() {

alert(ime);

}

return funkcija2;

}

var f = funkcija1();

// f postaje funkcija2

f();

//funkcija2 pamti promenljivu iz funkcije1

//ispisuje "Svet kompjutera"

Funkcionalno programiranje

Danas imamo dosta ljubitelja funkcionalnih programskih jezika, kao što su Clojure, Erlang, OCaml, F#, Scala, Elixir, LISP... U funkcionalnom programiranju funkcije su takođe prvog i višeg reda i ceo program se sastoji uglavnom od iskaza koji uvek imaju neku vrednost (dakle, ne izraza, podsetite se ranijih tekstova i termina expression i statement).

U funkcionalnom programiranju često se pominje (mada nije obavezno) termin „deklarativan način programiranja”, za razliku od imperativnog, kakav smo do sada koristili u primerima. Imperativnim programiranjem, kucanjem programa govorite kompjuteru šta da uradi i kako da uradi, dok je filozofija deklarativnog pristupa da samo kažete šta želite da postignete.

Još jedna ključna osobina funkcionalnog programiranja je da ne menja vrednosti promenljivih, tj. svi podaci su nepromenljivi (immutable, nasuprot terminu mutable). To znači da funkcije ne menjaju podatke koje im prosleđujete i uvek vraćaju novu vrednost, ne menjaju stanje (state) programa itd. Ovakve funkcije se nazivaju čiste (pure). Jedna od prednosti ove programerske filozofije jeste da kada pravite aplikaciju sa više niti (multi-threading), u klasičnom programiranju morate da koristite razne tehnike kako npr. dva threada ne bi u isto vreme menjala isti niz (racing condition). U funkcionalnom programiranju se uvek prave kopije podataka, stoga nema bojazni za ovakvim konfliktima. Iako deluje da stalno kopiranje podataka degradira performanse programa, postoje suptilni mehanizmi koji to dosta optimizuju.

Postoje tri popularne metode funkcionalnog programiranja koje su našle svoje mesto i u drugim jezicima, a to su: filter, map i reduce. Filter i map su relativno jednostavne funkcije, dok je reduce komplikovaniji i moćniji, ali zbog prostora ostavljamo da ga sami proučite (filter i map mogu da se implementiraju pomoću reduce-a).

Filter jednostavno prolazi kroz niz podataka i vraća novi niz, u kojem su izbačeni neki elementi na osnovu kriterijuma koji ste vi postavili (kriterijum se proverava na osnovu callback funkcije, koju ste prosledili funkciji filter). Map radi sličnu stvar, samo što ne izbacuje, već menja podatke niza. Kao što možete videti, ovo je deklarativno (ne kažete kako da se ide kroz niz) i imutabilno rešenje (originalni niz se ne dira i vraća se nova kopija obrađenog originalnog niza). Nizovi u JavaScriptu imaju ugrađene ove metode u sebi. Pošto ove funkcije vraćaju niz, one se mogu nadovezivati jedna na drugu, npr:

noviniz = nekiniz.filter(...).map(...)

Potražite ove funkcije i u vašem jeziku po izboru (uveden je u Javu 8, ima ga Swift...).

------------------------ funkcionalni javascript.js

var niz = [1, 2, 3];

var niz1 = [];

var niz2 = [];

// for petlja – sta zelimo i kako

// zbog tog "kako" smatra se impertivnim

// nacinom programiranja

for (var i = 0; i < niz.length; i++) {

niz1.push(niz[i] * 2);

}

niz.forEach(function (brojIzNiza) {

niz2.push(brojIzNiza * 2);

});

// map – deklarativno programiranje,

// samo sta zelimo da uradimo

var noviNiz3 = niz.map(function (brojIzNiza) {

return brojIzNiza * 2;

}); // noviNiz3 = [2,4,6]

// skraceno:

// map + fat arrow (lambda) function (EcmaScript6)

var noviNiz4 = niz.map(brojIzNiza => brojIzNiza * 2);

//filter

var noviNiz5 = niz.filter(function (brojIzNiza) {

return brojIzNiza > 1;

}); // noviNiz5 = [2,3]

var noviNiz6 = niz.filter(brojIzNiza => brojIzNiza > 1);

--------------------------- javascript fat arrow.js

//ES6 je doneo fat arrow funkcije

//vidite koliko je moguce skratiti kod

var saberi = function (a, b) { return a + b; };

var saberi = (a, b) => { return a + b };

var saberi = (a, b) => a + b;

var dodaj1 = a => a + 1; //primer 1 parametar

IDE, breakpoint, refactoring

Za kraj malo odmora od listinga, uz spisak nekih pogodnosti koje pruža IDE i koje mi često koristimo. Jedna od njih je brzo otvaranje fajla koji vam je potreban. U slučaju NetBeansa, kada pritisnite ’Ctrl+Alt+O’, otvara se prozorčić u kojem možete da ukucate ime fajla. Dovoljno je da upišete par slova iz imena i verovatno će se lista suziti na svega par fajlova, od kojih je tu i onaj koji ste tražili.

Ako držite taster ’Ctrl’ i kliknete na neku promenljivu ili funkciju, kursor će odmah skočiti na mesto na kome je ona definisana (makar to bio i drugi fajl). To je zgodno, jer ne morate da ručno skrolujete ekran ili tražite fajl. Nakon što ste videli šta vas je zanimalo, možete se vratiti „nazad” na mesto sa kog ste krenuli kombinacijom ’alt+levo’ (i ’Alt+desno’ da se opet vratite „unapred”). Ovakve skraćenice znatno štede vreme i povećavaju efikasnost rada. ’Alt+shift+F’ će automatski „srediti”, tj. formatirati vaš tekst tako da svuda bude isti stil i dobra čitljivost.

Neretko ćete, posle dužeg vremena provedenog kucajući program, imati promenljive ili funkcije čiji nazivi ne odražavaju ono čemu su namenjene, jer ih u startu niste baš najsrećnije nazvali. IDE omogućava refactoring, kome je jedna od mogućnosti da napišete novo ime promenljive, a program će je sam promeniti svuda u kôdu gde je to potrebno. Ovo je dosta naprednije od klasičnog Search & replace, koji nude editori teksta, u kom možete slučajno promeniti i neki deo kôda koji ne bi trebalo. Ako se prisetite OOP i korišćenja getera i setera, mnoga radna okruženja nude automatsko generisanje ovih metoda.

Da biste proveravali da li vaš program dobro radi, u samom procesu stvaranja kôda često ćete na ekranu ili konzoli ispisivati vrednosti nekih promenljivih putem print ili alert naredbe. Zatim, možete koristiti assert komande ili neki od logera kojim ćete snimiti korake rada vašeg programa u fajl (zgodno za naknadnu proveru kôda na udaljenom računaru ili uređaju). Ipak, u toku razvoja aplikacije, jedna od najkorisnijih stvari koje IDE olakšava je debagovanje i postavljanje breakpointa na liniju kôda. Kada program prilikom izvršavanja naiđe na mesto koje ste označili za prekid, izvršavanje se pauzira, a vi imate uvid u vrednosti svih dostupnih promenljivih i mnogih drugih podataka. Vrednosti promenljivih možete ručno promeniti, možete izvršavati naredne linije kôda, liniju po liniju, ili nastaviti izvršavanje programa. Na ovaj način imate detaljan uvid u rad programa i tada je lakše otkriti zašto je došlo do neke greške u kôdu. Napominjemo da proverite da li je IDE postavio program u Debug konfiguraciju i da li ste pokrenuli Debug umesto Run. Što se JavaScripta tiče, breakpoint se može postaviti i u browseru (developer tools).

IDE nudi mnogo više od ovoga što smo napisali, ali je njegov značaj više nego jasan. Za kraj, IDE se može odlično povezati sa nekim od popularnih version control sistema, ali o tome u sledećem broju.

Ognjen POPOVIĆ

 
 NOVE TEHNOLOGIJE
Roborace

 TRŽIŠTE
Osuđenost na Microsoft Windows?

 NA LICU MESTA
INAT Summit
LG i Arena Cineplex saradnja
Otvaranje Big Fashion šoping centra

 KOMPJUTERI I FILM
Guardians of the Galaxy Vol. 2
Pirates of the Caribbean: Salazar’s Revenge
Alien: Covenant
King Arthur: Legend of the Sword
Filmovi, ukratko

 SITNA CREVCA
Kako postati programer (8): Oblast vidljivosti promenljivih i funkcija
Šta mislite o ovom tekstu?

 VREMENSKA MAŠINA
Referendum, Big Blue i Teorija haosa

 PRST NA ČELO
Nebo nije granica
Home / Novi brojArhiva • Opšte temeInternetTest driveTest runPD kutakCeDetekaWWW vodič • Svet igara
Svet kompjutera Copyright © 1984-2018. Politika a.d. • RedakcijaKontaktSaradnjaOglasiPretplata • Help • English
SKWeb 3.22
Opšte teme
Internet
Test Drive
Test Run
PD kutak
CeDeteka
WWW vodič
Svet igara



Naslovna stranaPrethodni brojeviOpšte informacijeKontaktOglašavanjePomoćInfo in English

Svet kompjutera