Šablonske niske u programskim jezicima
Uvod
U programskom jeziku JavaScript, šablonske niske (eng. template literals), predstavljaju elegantan način da se vrednosti promenljivih i izraza direktno uvrste u niske - bez potrebe za korišćenjem funkcija ili operatora za konkatenaciju. *
U drugim programskim jezicima, naziv 'šablonske niske' ne upotrebljava se zvanično, ali, principi i tehnike svakako postoje, i stoga ćemo u nastavku napraviti detaljan osvrt na implementaciju koncepta šablonskih niski, ne samo u JavaScript-u, već i u PHP-u, Python-u i C#-u.
Iako su šablonske niske glavna tema članka (kojoj ćemo posvetiti najviše pažnje), iskoristićemo priliku da se na samom početku detaljno upoznamo i sa osnovnim načinima za spajanje niski, preko funkcija i operatora konkatenacije (čime članak praktično postaje svojevrstan praktikum za spajanje niski, u širem kontekstu).
Konkatenacija - osnovni metod spajanja
Osvrnimo se za početak na princip nadovezivanja niski u programskim jezicima sa precizno definisanim tipovima podataka, u kojima je moguće spajati niske (preko operatora za spajanje niski), ali - nije moguće spajati kombinovane podatke.
Za primer možemo uzeti sledeći C++ kod:
Ako bismo kod smestili u funkciju main
i pokrenuli program, sadržaj niske s3
bio bi: Dobar dan!
Međutim, ukoliko je potrebno spojiti nisku sa vrednošću brojčane promenljive:
.... podaci se ne mogu spajati direktno ....
.... već se brojčani podatak prvo mora pretvoriti u nisku, preko odgovarajuće funkcije:
Skriptni jezici su ('generalno'), ponešto 'ležerniji' po pitanju operacija sa niskama, međutim, ne ponašaju se svi skriptni jezici na isti način.
Neki skriptni jezici (kao što je npr. Python), u slučaju upotrebe operatora konkatenacije zapravo se ponašaju slično kao C++ (i drugi strogo tipizirani jezici), dok drugi skriptni jezici (kao što je npr. JavaScript), pružaju izvestan .... 'privid slobode'. *
Budući da primere iz Python-a i JavaScript-a možete isprobavati uporedo sa čitanjem članka (prilično lako), usmerićemo se na spajanje niski unutar standardnih naredbi za ispis u dva pomenuta jezika.
U većini skriptnih jezika, kada se funkciji za ispis preda (samo) jedan argument, odnosno promenljiva bilo kog tipa (makar kada su u pitanju prosti tipovi i niske), tip podatka će biti prepoznat automatski i vrednost će biti uredno ispisana.
Primera radi, ako pozovemo naredbu console.log
u JavaScript-u ....
.... ili funkciju print
u Python-u ....
.... brojčani argument 124
će automatski biti pretvoren u nisku i dobićemo ispis "124"
.
Međutim, ako pokušamo da dodamo string konstantu preko koje se promenljiva imenuje u ispisu, tako da ispis bude "a = 124"
....
.... JavaScript će i ovoga puta uredno ispisati vrednost * ....
.... ali zato Python ....
.... prijavljuje grešku:
Ništa 'strašno', ali, za razliku od JavaScript-a, * funkcije i operatori za ispis niski u (drugim) programskim jezicima, tipično se koriste onako kako smo videli u poslednjem primeru (slika #8): u kombinovanom ispisu u kome se navode brojčane promenljive, potrebno je pozivati funkcije za eksplicitno pretvaranje brojčanih vrednosti u niske (posle napomene, videćemo zašto je tako). **
Razlike između formata zapisa brojčanih vrednosti i niski
Da bismo najlakše razumeli razlike između formata koji se koristi za zapis brojčanih vrednosti, i formata koji se koristi za zapis niski, uzećemo za primer: celobrojnu promenljivu, i nisku - koje naizgled predstavljaju isti podatak.
Celobrojne promenljive tipično zauzimaju 4 bajta (tj. 32 bita), što omogućava predstavljanje vrednosti između -2147483648
i 2147483647
.
Ako se (npr) broj 143 zapiše kao celobrojna promenljiva, zapis ima sledeći oblik:
Niska "143" (naizgled sličan podatak), predstavlja se na primetno drugačiji način, kao niz znakova "1", "4" i "3", koji su kodirani preko odgovarajućih ASCII vrednosti:
- '1' - 49
- '4' - 52
- '3' - 51
Pri ispisu, potrebno je da celobrojna vrednost 143
, bude pretvorena u nisku "143"
, i samo je pitanje da li će program to uraditi bez eksplicitno definisanih programskih instrukcija (kao što je slučaj sa programima u JavaScript-u), ili neće (kao što je slučaj sa programima koji su pisani u većini ostalih programskih jezika).
Konkatenacija preko funkcija
Pre nego što se osvrnemo na operatore konkatenacije, pomenućemo informativno da u programskim jezicima postoje i funkcije za spajanje niski.
Primera radi, pokretanjem sledećeg JS koda ....
.... u konzoli browsera biće ispisano "Dobar dan!".
Međutim, na ovakav pristup se nećemo osvrtati previše, budući da se za spajanje niski uglavnom koriste operatori za spajanje i šablonske niske.
Upotrebu operatora i obrazaca za formatiranje teksta proučavaćemo na primeru sledećih jezika:
- JavaScript - opšteprisutni jezik za skriptovanje web stranica
- PHP - predstavnik serverskih jezika
- Python - predstavnik skriptnih jezika opšteg tipa
- C# - predstavnik C-olikih jezika
Pri upotrebi operatora za spajanje niski, podrazumeva se da su oba operanda niske, a ukoliko određeni operand nije niska - potrebno je da takav operand prethodno bude pretvoren u nisku na propisan način (najčešće, preko ugrađene funkcije za pretvaranje brojčanih vrednosti u niske).
Konkatenacija preko operatora u JavaScript-u
Spajanje niski u JavaScript-u obavlja se preko operatora +
(bez potrebe za eksplicitnim konvertovanjem netekstualnih podataka):
Međutim, "liberalan" pristup u spajanju niski u JavaScript-u, podrazumeva da se na određene pojave mora obratiti pažnja.
Na internetu se može pronaći mnoštvo pošalica na račun navedene situacije ("JavaScript je jedini jezik u kome je 2 + 2 = 22" i sl), ali, to je samo "druga strane medalje" u situaciji u kojoj JS sam pretvara vrednosti promenljivih u niske (onda kada nije krajnje nedvosmisleno da u sabiranju učestvuju dve brojčane vrednosti).
U JavaScript-u, 2 + 2
je (ipak) 4, ali zato naredba "2" + "2"
kao rezultat daje "22".
U situacijama kada je JS interpretator potrebno 'usmeriti' - tako da "2 + 2 (zaista) bude 4" - potrebno je koristiti funkciju parseInt
(ili parseFloat
, za decimalne vrednosti), pre nego što se pozove operator +
, čime se JS interpretatoru nedvosmisleno stavlja do znanja da su u pitanju brojevi (a ne niske), to jest - čime se JS interpretatoru stavlja do znanja da se poziva operacija sabiranja brojeva (a ne operacija spajanja niski).
Formatiranje decimalnih vrednosti u JavaScript-u
Ukoliko je u ispisu potrebno formatirati decimalne vrednosti na određen način (tj. ukoliko je potrebno zadati određen broja decimala), može se koristiti funkcija toFixed(n)
(gde argument n
predstavlja broj decimala):
Konkatenacija u PHP - u
Spajanje niski u PHP-u (kao što je to bio slučaj i sa drugim web jezikom, JavaScript-om), takođe je vrlo jednostavna operacija, ali, specifičnost je operator .
(tačka), umesto uobičajenog operatora +
:
Formatiranje decimalnih vrednosti u PHP-u
Formatiranje decimalnih brojeva u PHP-u, tipično se obavlja preko funkcije number_format
:
Konkatenacija u Python-u
U Python-u, spajanje niski sa vrednostima promenljivih, metodom konkatenacije, podrazumeva upotrebu funkcije str
(koja za predatu brojčanu vrednost vraća odgovarajuću nisku), a sam operator spajanja, i u slučaju Python-a je +
:
Formatiranje decimalnih vrednosti u Python-u
Za formatiranje ispisa decimalnih brojeva, Python koristi C-ovski pristup, koji podrazumeva upotrebu specifikatora konverzije ....
.... ali, u Python-u nema zareza između komandnog stringa i realnih argumenata.
Konkatenacija u C#-u
U programskom jeziku C#, u kome su brojčani podaci tipa Int32
i Float/Double
, praktično objekti, za spajanje vrednosti promenljivih sa niskama koristi se ugrađena metoda ToString
:
Formatiranje decimalnih vrednosti u C#-u
Zadavanje formata decimalnih brojeva (u okviru ugrađene metode ToString
), u C#-u je izvedeno na posebno elegantan način:
U gornjem primeru, odredili smo četiri decimale za ispis, a ako je potrebno u ispisu prikazati (npr) dve vodeće nule, može se predati niska "00.0000"
kao argument pri pozivu metode ToString
.
Prepoznavanje praktičnih nedostataka operatora konkatenacije u slučaju većih niski
Programski kodovi koje ste do sada videli u članku, sami po sebi vas verovatno ne bi mogli "baš skroz" ubediti da postoji išta sporno u tome da za spajanje niski (sa drugim niskama ili sa vrednostima promenljivih i izraza) - nastavimo da koristimo operatore konkatenacije, ali - to je zato što su primeri bili krajnje jednostavni.
Međutim, da bismo videli šta se događa kada se niske "zakomplikuju", razmotrićemo primer koji prikazuje jedan od svakodnevnih poslova u PHP-u (odstupićemo za trenutak od JavaScript-a).
Formatiranje HTML sadržaja u kojima se koriste vrednosti (brojčane i tekstualne), koje su preuzete iz baza podataka - jedan je od glavnih zadataka serverskog jezika kao što je PHP.
Za jednostavne kodove ....
.... svakako se (i dalje) za spajanje može koristiti metoda konkatenacije.
Međutim, ako se zadržimo na ideji sa prethodne slike (umetanje vrednosti promenljivih u HTML kod) - ali pri tom povećamo obim teksta (mada, nećemo ni u novom primeru 'preterivati', zarad preglednosti) - situacija će se promeniti.
U drugom primeru, HTML kod po sledećem obrascu ....
.... treba formatirati preko PHP-a (umetanjem vrednosti koje su preuzete iz baze podataka) ....
.... i sada već vidimo da konkatenacija nije najelegantniji način (ni iz daleka).
Praktično rešenje je (pogađate) - upotreba šablonskih niski.
Definicija pojma šablonskih niski u programskim jezicima
Šablonske niske su tekstualni obrasci koji su zapisani na poseban način (vrlo slično običnim niskama, ali, uz određene izmene i dodatke), tako da pojava identifikatora promenljivih, ili celih izraza, sugeriše prevodiocima da navedene elemente treba - pre dalje obrade - zameniti odgovarajućim vrednostima. *
Za početak, osvrnućemo se na implementaciju koncepta šablonskih niski u JavaScript-u, jedinom jeziku (u ovom članku), u kome se navedeni termin (eng. template literals - šablonske niske), upotrebljava kao zvaničan naziv.
Šablonske niske u JavaScript-u
Pošto smo prethodno izneli poveću količinu informacija koje su vezane za opštu teoriju i druge metode spajanja niski, 'nekako je red' da se što pre osvrnemo na primer koji se odnosi na glavnu temu članka - upotrebu šablonskih niski (za početak, u JavaScript-u):
Kada pokrenemo skriptu, umesto niske ${Math.PI}
, u ispisu će se pojaviti (približna) vrednost matematičke konstante π (Pi).
Međutim, da bismo se 'za prave' upoznali sa šablonskim niskama u JS-u, poslužićemo se praktičnijim primerom (koji smo već "načeli" u prethodnom odeljku).
Ponovo ćemo se baviti 'slikama u HTML kodu' (ali, ovoga puta preko JavaScript-a), i recimo da je preko AJAX-a (uskoro ćemo AJAX-u posvetiti zaseban članak), do JS skripte došao sledeći JSON objekat ....
.... posle čega je potrebno 'umetnuti' podatke iz JSON objekta - u HTML kod koji smo videli u prethodnom odeljku (pri umetanju, biće korišćena sledeća polja iz JSON objekta: naziv slike i opis slike).
Problem najlakše možemo rešiti ako implementiramo funkciju koja preko šablonske niske vraća traženi HTML kod.
Da pojasnimo:
- šablonske niske u JS-u počinju i završavaju se znakom backtick
`
(nije apostrof, već znak koji se na tastaturama tipično nalazi ispod tastera ESC) - izrazi se smeštaju unutar vitičastih zagrada kojima direktno prethodi znak
$
(u gornjem primeru koristili smo obične promenljive, to jest, nismo koristili prave višečlane izraze, međutim, princip zapisa je isti) - šablonske niske se mogu pisati u više redova (bez dodatnog označavanja "prelazaka u novi red")
- vrednosti na koje se pozivamo unutar šablonskih niski, moraju biti unapred definisane (deklarisane i inicijalizovane)
Poslednja stavka zaslužuje posebnu pažnju.
Šablon koji se navodi (unutar niske koja je uokvirena znacima ``
), nije apstraktni šablon opšteg tipa, koji se definiše na određenom mestu i može se proizvoljno pozvati pre nego što su vrednosti koje se koriste u šablonskoj niski inicijalizovane (kao što funkcije možemo zapisivati pre i posle mesta na kojima ih pozivamo). Nasuprot navedenom, konkretan sadržaj niske koja je definisana preko šablona, formira se na licu mesta, shodno unapred definisanim vrednostima.
Ali, postoji i krajnje funkcionalno (i pri tom, sasvim elegantno) rešenje, koje smo već videli: šablonska niska može se smestiti unutar funkcije, a vrednosti koje treba "utisnuti" u šablon, predaju se kao argumenti (i funkcija na kraju vraća formiranu nisku).
Korišćenje izraza u šablonskim niskama
Osim identifikatora promenljivih, u šablonskim niskama takođe se mogu koristiti:
- (matematički) izrazi
- pozivi funkcija
- elementi nizova
- polja objekata
.... što možemo sagledati preko sledećih naredbi:
Još jedan jednostavan primer koji prikazuje lepotu programskih jezika u opštem smislu.
Šabloni za kreiranje niski u PHP-u (heredoc, nowdoc)
U PHP-u, budući da je u pitanju serverski jezik čija je jedna od osnovnih uloga - dinamičko serviranje HTML-a, koncept šablonskih niski koristi se verovatno i češće nego u JS-u (doduše, ne pod navedenim nazivom, već, pod drugim nazivima).
Pored korišćenja operatora konkatenacije, u PHP-u se šabloni za formiranje stringova mogu definisati na još dva (različita) načina, koji su poznati kao heredoc
i nowdoc
.
Međutim, na ovom mestu (pre nego što se osvrnemo na heredoc i nowdoc sintaksu), želimo da ukažemo na to da se većina zahvata pri kreiranju tekstualnih obrazaca koji koriste identifikatore promenljivih, sasvim uspešno može obaviti i preko običnih niski koje su uokvirene navodnicima (makar je tako kada su u pitanju jednostavniji obrasci koji se zapisuju u jednom redu, što ćemo u sledećem odeljku prikazati preko jednostavnog i uobičajenog primera).
Interpretacija niski koje su uokvirene navodnicima
Pretpostavićemo da su se mnogi čitaoci do sada sretali (u svakodnevnom pisanju PHP skripti), sa niskama u kojima se pozivaju vrednosti promenljivih, i može se (najprostije) reći da se u PHP-u podrazumeva da će niske koje su uokvirene navodnicima, biti interpretirane, tako što će identifikatori promenljivih biti zamenjeni vrednostima promenljivih.
Tipičan primer su SQL upiti kakve smo već prikazivali u ranijim člancima:
Prethodno zapisani upit, pri pozivu skripte praktično postaje:
PHP dozvoljava i zapis običnih niski u više redova, ali, heredoc
obrasci svakako predstavljaju elegantnije rešenje ukoliko se niska 'prostire' preko više redova (videćemo uskoro kako heredoc i nowdoc obrasci funkcionišu u praksi).
Ispis niski koje su uokvirene apostrofima
Za razliku od prethodnog zapisa (između znakova navoda), ako se niska zapiše unutar apostrofa, vrednosti promenljivih neće biti interpretirane:
Navedeni princip zapisa može se koristiti (npr), u situacijama kada je potrebno prikazati PHP kod na web sajtovima i sl (kod sa prethodne slike daje sledeći ispis):
Sagledali smo praktične načine definisanja niski koji se koriste u "svakodnevnim situacijama", međutim, ako je potrebno da budemo "zvanični", postoje opcije ....
Heredoc
Takozvana heredoc
sintaksa u PHP-u (prepoznatljiva po početnom delu niske koji je formiran po obrascu: <<<NAZIV
), podrazumeva sledeće:
- u okviru niske mogu se pojavljivati identifikatori i izrazi - koji će biti interpretirani pre ispisa (to jest, biće zamenjeni niskama koje odgovaraju vrednostima promenljivih ili izraza)
- niska može zauzeti više redova.
Razmotrićemo konkretan primer (pri čemu ćemo dodati i nekoliko promenljivih, da bi programski kod mogao da 'proradi' ako isprobavate primere):
Ako pozovemo skriptu koju smo prethodno definisali, dobićemo sledeći ispis:
Baš kao i u slučaju običnih niski koje su zapisane između znakova navoda, i u gornjoj skripti će identifikatori promenljivih biti zamenjeni vrednostima promenljivih.
Takođe - baš kao i u slučaju kada smo koristili JavaScript - vrednosti promenljivih koje se koriste, moraju biti definisane unapred (pre korišćenja šablona).
Da bismo izbegli nedoumice oko toga da li identifikatori u obrascu imaju veze sa promenljivama koje zapravo postoje i imaju vrednost (ili su navedeni identifikatori 'fiktivnih' promenljivih), i u PHP-u se tekstualni obrazac može smestiti unutar funkcije (koja, za razliku od samostalnog heredoc
obrasca, može biti pozvana bilo gde).
Ovoga puta, argument funkcije je objekat klase Osoba
:
Kao i do sada, možemo zaključiti da je standardna notacija niski pod navodnicima, dovoljna za sitnije zahvate, dok se heredoc
format može koristiti za veće blokove, sa većim brojem promenljivih čije vrednosti treba uvrstiti u ispis, pri čemu postoji i dodatna pogodnost: unutar heredoc
obrazaca, znaci navoda mogu se pisati neposredno (primetimo da heredoc
odeljak nije uokviren navodnicima ili apostrofima, što (na izvestan način), sugeriše da navedeni znakovi nemaju poseban značaj u heredoc obrascu).
Ako nije potrebno da se vrednosti promenljivih i izraza interpretiraju, već, da se ispišu neposredno (kao u slučaju niski koje su uokvirene apostrofima), može se koristiti takozvani nowdoc
format.
Nowdoc
Sintaksa nowdoc
obrazaca vrlo je slična (prethodno prikazanoj heredoc
sintaksi), a razlika je u sledećem: pri imenovanju nowdoc
obrazaca, za uokviravanje samog naziva koriste se apostrofi ili navodnici (u donjem primeru naziv je 'SABLON'):
Ukoliko se posle niske <<<
pojavi niska koja je uokvirena apostrofima ili navodnicima, PHP interpretator neće interpretirati obrazac koji sledi, već će obrazac biti prikazan kao običan tekst.
Pokretanjem koda sa prethodne slike dobija se sledeći ispis:
Nowdoc sintaksa se tipično koristi u okolnostima kada je potrebno (doslovno) prikazati veće blokove teksta koji sadrže PHP kod.
Implementacija obrazaca za ispis teksta u Python-u
Najveću pažnju posvetili smo web jezicima, jer upravo se u jezicima kao što su JS i PHP, koncept šablonskih niski primenjuje češće nego kada su u pitanju jezici preko kojih se pišu desktop programi (reklo bi se da je tako), ali, nismo zaboravili ni Python i C#.
Koncept "šabloniziranog teksta" u kome se vrednosti promenljivih i izraza kombinuju sa "običnim tekstom", u Python-u je (kao i mnogo šta drugo), implementiran na jednostavan način, preko tzv. "f-niski":
- niska je uokvirena navodnicima
- na početku (pre navodnika), stoji znak
f
- za 'uokviravanje' izraza (unutar niske), koriste se vitičaste zagrade
{
i}
Programski kod iz prethodnog odeljka daje sledeći ispis:
Implementacija obrazaca za ispis teksta u C#-u
Šablonske niske u C#-u, definišu se na način koji je idejno sličan prethodno prikazanom načinu definisanja f-niski u Python-u:
- niska je uokvirena navodnicima
- na početku (pre navodnika), stoji znak
$
- za 'uokviravanje' izraza (unutar niske), koriste se vitičaste zagrade
{
i}
Kao što je bio slučaj i sa prethodnim jezicima, vidimo da je sintaksa za definisanje šablonskih niski u C#-u, jednostavna za pisanje i razumevanje.
Zaključak
Za kraj, imamo želju da vas potaknemo da se pozabavite svojevrsnim prepoznavanjem obrazaca u razvoju informacionih tehnologija, i razmišljanjem na temu: šta je prava svrha računara.
Ako se zapitamo - šta je to što pokreće razvoj programa, programskih jezika i pojedinačnih tehnika (?), rekli bismo da je odgovor na navedeno pitanje: potreba.
Kada se prepozna realna potreba za nečim, pronađe se i način za rešavanje problema (naravno, u granicama mogućeg; nećemo tek tako pronaći algoritam za sortiranje nizova čija je složenost O(1), ma koliko da to želimo i ma koliko da za takvim algoritmom postoji potreba). :)
Programski jezici visokog nivoa nastali su iz potrebe za jednostavnijim, funkcionalnijim i prirodnijim načinom za zapis programa, a šablonske niske su nastale iz potrebe za boljim i prirodnijim načinom za zapis obimnijih kombinovanih niski.
Lepota programiranja (ili, bar, jedna od lepih stvari u vezi sa programiranjem), ogleda se u tome što je u pitanju aktivnost koja omogućava pojedincima da ostvare velike ideje - pod uslovom da se "dovoljno potrude", a to što smo naveli, svakako je drugačije od većine ostalih oblasti ljudske delatnosti, u kojima ostvarivanja velikih ideja najčešće zahteva i velika materijalna ulaganja, ozbiljnu logističku podršku i sl.
Dobra tema za razmišljanje :) ....