|
Taip pat skaitykite Mitas apie programavimą ir kodavimą
Procedūrinė prieš deklaratyvinę
Tai yra pagrindinė schizma apibūdinti, kaip bus programuojama. Panagrinėkime kiekvieną variantą atskirai.
Procedūrinis programavimas
Procedūrinis arba imperatyvusis programavimas naudotas pirmiausiai, nes jis artimas tam, kaip veikia
kompiuteris. Jis ima vieną instrukciją ir ją įvykdo. Programa tėra vykdomų instrukcijų sąrašas ir, iš esmės,
yra tam tikra procedūra (tarkim, skirta dviejų skaičių sudėčiai). Programavimas yra tiesiog nurodymas, ką turi atlikti mašina
sudėk du skaičius ir parodyk rezultatą - būtų procedūrinė programa, išreikšta lietuvių kalba.
Metams bėgant procedūrinis programavimas sudėtingėjo ir darėsi vis abstraktesnis. Nuo mašininių
komandų pereita prie vis labiau nutolstančių nuo aparatūros instrukcijų. Pagrindinis procedūrinių kalbų
vystymasis buvo funkcijų ir paprogramių hierarchijos sukūrimas. Jei ankstyvajame etape, pvz., rūšiavimas buvo realizuojamas
panaudojant registrus ir mašininio lygio vykdymo išsišakojimus, tai vėliau rūšiavimas įtrauktas kaip programavimo kalbos elementas.
Dideliu proveržiu procedūriniame programavime buvo objektiškai orientuoto programavimo (OO)
įvedimas. Tai leido duomenis ir procedūras sujungti į vieną paketą objektą. Iš praktinės pusės patogu
vienoje vietoje turėti duomenis ir kodą, kuris dirba su tais duomenimis. O kartu ir didelis postūmis
abstraktumo link. Kodas ima mėgdžioti realaus pasaulio objektus. Programiniai elementai turi būsenas ir elgiasi tarsi realūs objektai.
Tačiau tikrovė yra tokia, kad net daugybė OO kalbų naudojama nenaudojant objektų, - taip kaip buvo
senais gerais laikais. Galima sukurti įrankius, tačiau negalima priversti juos naudoti.
Deklaratyvusis programavimas
Jis remiasi idėja, kad paprasčiausiai išdėstoma problema bei tikslai leidžiant sistemai rasti būdus
pasiekti sprendimą. Pvz., duodami užduotį žmogui (užprogramuodami jį) paprastai užduoties nesuskaidote į atskirus žingsnius.
Reikia pastebėti, kad schizma tarp šių dviejų programavimo prieigų taikoma ir vartotojo sąsajos (UI)
kūrimui. Tokios kalbos kaip XAML ar MXML deklaratyviosiomis, nes parašoma UI specifikacija nesirūpinant, kaip bus sukurti
UI realizuojantys objektai. Procedūrinis būdas yra tam tikra tvarka iškviesti objektų konstruktorius ir nurodyti jų savybes.
Žinomiausi deklaratyvių kalbų variantai yra funkcinės ir loginės kalbos.
Funkcinės kalbos
Visos programos turėtų vienaip ar kitaip atlikti kokias nors funkcijas, ar ne? Tačiau funkcinis programavimas
reiškia kažką visai kita!
Šios kalbos bando programavimo kalbas suvesti į matematines funkcijas, kurių rezultatas niekada
nesikeičia, pvz., sin(0.3) visada bus tas pats. Tad funkcija tiesiog yra išrinkimo veiksmas. Beje, matematinės
funkcijos skiriasi nuo funkcijų programavimo kalbose. Šios gali turėti šalutinių efektų ar būsenos pokyčių, pvz.,
function pliusvienas() {
globaluskintamasis++;
return globaluskintamasis;
}
Tokia funkcija yra neleistina funkcinėje kalboje. Joje kintamieji nėra kintamieji ta prasme, kad kartą
aprašyti savo reikšmę turi visą programos vykdymo laiką jie yra surišti su reikšme. Bet kaip tada ciklai ir
sąlyginis programos vykdymas?
Yra du atsakymai. Pirma, kodėl skaičiuojate? Jei dėl to, kad tai algoritmo, kuris gražina sprendinį, dalis,
to nedarykite. Tiesiog gražinkite atsakymą. T.y., jei skaičiuojate sin(0.3) iteraciniu būdu, paslėpkite iteraciją
funkcijoje ir paprasčiausiai gražinkite atsakymą.
Antras atsakymas yra funkcinis būdas tokiam veiksmui atlikti rekursija ir funkcinė kompozicija. Pvz.,
funkcija f(x), gražinanti x+1, gali būti realizuota taip:
x=0;
y=f(x);
z=f(f(x));
ir t.t.
Atkreipkite dėmesį, kad išraiškoje z=f(f(f(f(x)))) tik kintamasis z yra surištas su
rezultatu, joks kintamasis nekeičia savo reikšmės. Iš principo, tokia funkcija veikia tarsi ciklas.
Funkcinės kalbos turi didelį privalumą tame, kad nenurodoma, kaip reikia atlikti funkcijas, o nurodomas
tik jų rezultatas. Jose aiškiau matoma, kas vyksta ir jose lengviau realizuoti tokius dalykus, kaip lygiagrečius skaičiavimus.
Kas tas funkcinis programavimas
Kaip galima nemėgti matematikos?! Ir randasi programuotojų,
norinčių, kad programavimas panašėtų į matematiką.
Būta daugybė bandymų padaryti, kad programas būtų galima patikrinti lygiai taip pat, kaip ir matematinius
įrodymus. Tačiau visgi yra daugybė aspektų, kuriais programavimas visiškai nepanašus į matematiką. Ir esminiu
skirtumu yra tai, kad jis dažniausiai apima laiko ir būsenos pokyčių sąvokas. Programa nėra statinis kokios
nors visuotinės tiesos teiginys; tai veikiau instrukcijų rinkinys, kuriame įtvirtinta laiko seka. Vykdant programą,
ji ne visada eina tuo pačiu keliu. Ji reaguoja į aplinkos būseną ir kiekvieną kartą paleidus gali keisti savo pačios būseną.
Skirtumas tarp programavimo ir matematikos tas, kad programuojant galima (ir kartais būtina!) užrašyti štai
taip: x=x+1
Šis skirtumas yra viena iš priežasčių, kodėl pradedantiesiems sunku perprasti programavimą. Kol jie neišmoksta
laiką laikyti neatsiejama skaitomo bei rašomo kodo dalimi, viskas atrodo labai svetima, o kintamojo sąvoka paslaptinga.
O matematikoje funkcijos neturi būsenos. Jei šiandien lygiai 12 val. apskaičiuosite `sin(p)
reikšmę, gausite tokį patį atsakymą, kaip ir skaičiuodami rytoj tuo pačiu metu.
Bet kuo visa tai susiję su funkciniu programavimu?
Ogi, funkcinis programavimas siekia pašalinti būsenas ir kintamumą bei priartinti programavimą prie statinės
matematikos. Kitaip tariant, bandoma sustabdyti laiką programavime taip, kaip tai daroma matematikoje. Vis
tik tikrovė nėra tokia gryna, kaip gali pasirodyti. Funkciniame programavime netrūksta netvarkos, tačiau
ji dažnai slepiama po gausybe terminų, dėl kurių sunku įžvelgti esmę.
Funkciniame programavime funkcijos yra objektai, kuriuos galima naudoti bet kur, kur tinka skaičius ar išraiška.
Be to, egzistuoja aukštesnės eilės funkcijos, galinčios priimti kitas funkcijas kaip argumentus ir pačios
grąžinti funkcijas kaip rezultatą. Svarbiausia, kad funkcijos niekada nekeičia sistemos būsenos; t.y. jos neturi šalutinio poveikio.
Tik štai, šios taisyklės (vengti šalutinio poveikio) laikytis kiek sunkiau, nes norint, kad sistema pateiktų
rezultatą ar sąveikautų su vartotoju, šalutinis poveikis tampa būtinas, o tam tikra būsena privalo pasikeisti.
Dažnai naudojamas nekintamumo (immutable) įvardijimas, tačiau jo tikroji reikšmė dažnai painiojama
su paprastesne sąvoka - objekto nekintamumu. Objekto nekintamumo idėja yra paprasta. Pvz., jei String
objektas yra nekintamas, jo negalima pakeisti, tačiau tai nereiškia, kad negalite atlikti tokio veiksmo: str1=str1+"A";
Šiuo atveju String objektas, į kurį nurodo kintamasis str1, yra sunaikinamas, o vietoj jo sukuriamas
naujas objektas su pridėta A. Tai beveik savaime suprantama nekintamumo reikšmė, kuri liktų nepastebėta,
jei apie ją nebūtų užsiminta. Tačiau tikroji nekintamumo svarba funkciniame programavime atsiskleidžia per tai, kad kintamieji iš tiesų ... nekinta.
Tarkim, jei F# parašoma
Let x=1
tai x bus lygus 1 visam laikui (nes kintamųjų ir nėra) ir negalima parašyti
x=x+1
Tai kaip funkciniame programavime atliekami įprasti veiksmai (pvz., sudėti pirmus 10 sveikų skaičių).
To esmė: funkciniame programavime simbolis nesiejamas su jokia reikšme tol, kol nėra žinoma galutinė to simbolio
reikšmė. Tai matematikai būdingas sustingusio laiko principas. Kai daugumoje kalbų užrašytume, naudodami ciklą. maždaug taip (gaudami 45):
total=0;
for(i=0; i<10; i++){
total=total+i;
}
Tačiau funkciniame programavime turite pasitelkti rekursiją, taigi skaičiavimas atrodytų taip:
function sumto9(x){
if (x == 9) return x;
return sumto9(x + 1) + x;
}
Ir tada galima užrašyti, total vienu ypu: priskiriant galutinę reikšmę:
let total=sumto9(0)
Beje, reikia atkreipti dėmesį, jog x nesikeičia kaskart iškvietus funkciją, jis tiesiog sukuriamas
naujame kontekste. Neturime kintamųjų (vietoje jų parametrai), o iteraciją (ciklą) pakeičia rekursija.
Gerai, bet kas gi yra ta uodeginė rekursija (tail recursion)?
Jei pavyksta taip sukonstruoti rekursines funkcijas, kad kitos funkcijos iškvietimas įvyktų pačioje funkcijos
pabaigoje (t.y. uodegoje), kompiliatorius gali jas optimizuoti paversdamas ciklu. Tai įmanoma todėl, kad
pasiekus funkcijos uodegą, kompiliatorius gali atmesti visą informaciją apie funkcijos būseną (t. y. jos
dėklo kadrą) ir laikyti naują iškvietimą tos pačios funkcijos iškvietimo dalimi. Ir dėl uodeginės rekursijos
optimizavimo funkcinės kalbos yra tokios pat našios kaip ir nefunkcinės.
Yra daugybė kitų susijusių mechanizmų, leidžiančių atidėti reikšmės susiejimą su simboliu tol, kol ta reikšmė
bus visiškai nustatyta, tačiau visi jie veikia panašiai ir naudoja tuos pačius metodus.
Bene garsiausias funkcinio programavimo mechanizmas, leidžiantis atlikti veiksmus, kurie yra savaime
suprantami nefunkcinėse kalbose, yra monada, kuri į funkcinį programavimą grąžina operacijų sekos idėją. Monados
suteikia priemonių šalutiniams poveikiams, įvesties ir išvesties (I/O) operacijoms, kintamųjų priskyrimui ir
panašiems veiksmams įgyvendinti taip, kad viskas atrodytų statiška žinoma, jei tik neįsižiūrite labai atidžiai.
Taigi, ar visa tai nėra magija, kuria turėtų rūpintis nefunkcinių kalbų programuotojai?
Funkcinio programavimo šalininkai yra linkę pasitelkti matematiką, ypač kategorijų teoriją, kad
paprastos idėjos atrodytų kur kas sudėtingesnės. Ir reikia nepamiršti, kad programavimas iš esmės yra praktinė veikla, todėl
jis negali būti toks sudėtingas kaip abstrakčioji matematika. Ir pasitaiko atvejų, kai funkcinis programavimas
atrodo tiesiog tinkamiausias pasirinkimas. O pajutus funkcinio programavimo įrankių teikiamą naudą, jų sunku atsisakyti.
Kalbant apie tam tikrus skaičiavimus, funkcinis požiūris išties pasiteisina, tačiau prireikus šalutinio poveikio
(side effects), geriau tinka nefunkcinis programavimas. Kuriant vartotojo sąsają, daug privalumų turi
į objektus orientuotas procedūrinis programavimas.
Taigi, ar verta mokytis funkcinio programavimo kalbos?
Taip. Juk tai niekuo nepakenks, o atlygiu taps galimybė naudoti uodeginę rekursiją.
Loginės kalbos
Jos neretai artimesnės deklaratyviam idealui nei funkcinės kalbos. Jose irgi neleidžiama funkcijoms ar
kintamiesiems keisti reikšmių, tačiau jos turi vieną papildomą ingredientą įrodymą.
Šiose kalbose galite užrašyti:
arsusiję(A,B);
ir bus gražinta true, jei A ir B susiję, o kitu atveju - false. Tai veikia netgi tada, kai A ir B yra
susiję ne tiesiogiai, o per daugelį tarpinių sąsajų. Kalboje realizuota rekursyvi paieškos priemonė, kuri įrodo
teiginius priklausomai nuo to, kas yra jos duomenų bazėje. Tarkim, galite užrašyti:
arsusiję(A,x);
Kur x yra nesurištas kintamasis, - ir bus paieška bandys surasti tokią x reikšmę, kuriai teiginys
būtų true. Galima užrašyti apibendrintai
arsusiję(A,B,C, x,y,z);
Kad būtų ieškoma x,y,z, kuriems predikatas yra true.
Prolog kalba buvo sukurta 1972 m. Ji yra bendros paskirties kalba su įvedimo/išvedimo priemonėmis,
grafika ir pan. Tačiau daugelis programuotojų mano, kad ji kiek dirbtina dėl šalutinių reiškinių dirbant su predikatais.
Tačiau kai kurie uždaviniai loginėse kalbose išsprendžiami labai paprastai. Vis tik neaišku, ar loginis programavimas nuves kur nors toliau.
Dinaminės kalbos
Truputį pasitrauksime į šalį nuo aptariamo klausimo. Dabar labai daug kalbama apie dinamines kalbas.
Tradicinės kalbos (Java, C++ ar C#) yra statinės kalbos. Tačiau ribą labai sunku nustatyti.
Anksčiau skirdavo pagal tai, ar kalbos kompiliuojamos, ar interpretuojamos. Šiuo metu didžiausias dėmesys skiriamas tipams.
Kuriant OO kalbą renkamasi, ar bus griežta tipų kontrolė. Griežtos tipų kontrolės kalbose griežtai
apibrėžiama,kokio tipo objektai gali būti naudojami. Jei metodo parametro tipas yra Obuolys, tai bus klaida,
jei pabandysime joje nurodyti Apelsinas tipo reikšmę. Alternatyva bet kurioje vietoje leisti bet kokio tipo
objektus ir pačiai kalbai pasirinkti, kaip geriausiai susitvarkyti tai silpna tipų kontrolė.
Statinėse kalbose galima pažiūrėti, kokia reikšmė bus priskirta kintamajam tiesiog skaitant programos
kodą. Dinaminėse to negalima, nes kas priskirta pasimato tik programos vykdymo metu. Silpna tipų kontrolė
ar dinaminės kalbos palengvina programavimą, tačiau neverčia laikytis programavimo disciplinos.
Ankstyvosiose kalbose nebuvo tikrojo duomenų ir kodo atskyrimo, kas leido programoms modifikuoti
savo kodą. Vėliau save modifikuojantis kodas įgavo reputaciją kaip pavojingas ir nesaugus. Kalbos atskyrė
duomenis ir kodą programoms leista manipuliuoti tik duomenimis, o ne savo kodu. Tačiau interpretuojamos kalbos,
pvz., Basic ar JavaScript
ištrynė tą skirtumą. Jose leidžiama modifikuoti kodą tad jos tapo dinaminėmis platesne prasme.
Dinaminės kalbos gali atrodyti žingsniu į ateitį, tačiau tai ir sugrįžimas į ankstyvuosius laukinius laikus.
Grafinės kalbos
Paskutinė paradigma yra grafinės kalbos, jei jas aplamai galima vadinti kalbomis. Tai keistas OO ir
deklaratyvaus aspektų mišinys su trupučiu procedūriškumo. Idėja tame, kad jei mėgdžiojame realaus
pasaulio objektus, tai programiniai objektai turi įgauti vaizdą. Tai įprasta vartotojo sąsajų srityje mygtukas,
kurį galime nutempti ir pasidėti norimoje vietoje yra fizinis mygtuko objekto programinio kodo
pavaizdavimas. Su juo galima ekgtis kaip su realiu objektu tempti, keisti dydį, spalvą ir pan.UI grafiniai
objektų komponentinis požiūris vis dar kuriamas kaip ActiveX, WPF, Widgets ir t.t.
O jei tą patį principą pritaikysime programavimui. Gaunama ciklo, sąlyginio vykdymo, modulio ir kt.
komponentus. Programą surenkame tarsi UI sutempdami ir susiedami komponentus į vykdymo grandinę.
Tereikia vos kelių eilučių procedūrinio kodo, nurodančio tikslesnius veiksmus, tačiau dauguma komponentų susijungs vien per savybes.
Toks stilius naudojamos tokiose kalbose kaip Scratch, skirtose vaikams.
O neseniai Google paskelbė apie grafinio programavimo aplinką, skirtą Android tačiau ji dar tebetestuojama.
Ir galiausiai
Yra dar daug kitų programavimo paradigmų, tačiau jos daugiausia yra nuošalyje ir specialios aplinkoms.
Pvz., sinchroninis, asinchroninis ar įvykiais valdomas programavimas, taip pat nuoseklus ir lygiagretusis skaičiavimas,
yra dirbtinio intelekto (AI) ir programavimo sąveika.
Pvz., naudojant genetinius algoritmus programa išsivysto, o ne parašoma.
Programavimas fantastikoje
Programavimo ateitis ginčytina, tačiau yra neginčytina, kad programavimas praeinantis reiškinys. Gal ne visiems,
bet bent jau DI entuziastams!
O ką apie tai sako fantastika?! Matyt įvairiai
Štai Žvaigždžių kelyje matome Spoką su planšete, tačiau juk
nemanote, kad jis programuoja, ar ne? Erdvėlaivyje yra ir kompiuterių, tačiau jiems niekas nieko neprogramuoja
su jais tiesiog kalbamasi tarsi draugiškais kolegomis, tačiau juk kalbėjimasis nėra programavimas, ar ne?
Pikaras paprašo karštos arbatos, ir kompiuteris nurodo replikatoriui ją paruošti; ir niekam nešauna į galvą,
kad reikėtų suprogramuoti arbatos aplikaciją kompiuteris pats pakankamai protingas, kad žinotų, ko kam
reikia. Argi tai ne realus AGI?!
O kaip reikalai su kompiuterių technikais? Azimovui
nereikė net kompiuterininkų jo kūrinių cikle apie robotus Suzana Kelvin vadinama robotų psichologe ir
ji bendrauja su DI agentais, spręsdama jų problemas ir priversdama juos atlikti tai,
ko nori žmonės. O štai minėtame Žvaigždžių kelyje arčiausiai IT yra Deistromo pažangios
robototechnikos instituto darbuotojai tačiau jie ne tiek susirūpinę algoritmais, kiek tobulų neuroninių tinklų kūrimu.
Kiti HOT.LT straipsniai:
Kompiuterių ištakos
Ką vadiname programuotoju?
Revoliucija su BASIC
Programavimo kalbų evoliucija
Dėl kompiuterinio raštingumo
Džonas Bakas FORTRAN tėvas
Unix ir C kalbos kiltis ir ... šachmatai
Technika: Nuo Paleolito laikų
Programavimo kalbų istorija
Lambda išraiškos Java į naują lygį
Optimali stulpelių eilės tvarka
Didelių duomenų analizės terminai
Kokia viena kodo eilutė sukrėtė programavimo pasaulį?
Intuicijos ribojimas matematikoje 19-me amžiuje
Pirmoji programuotoja: Ada Lovelace
Tikroji Interneto pabaiga
"Ruby" kalba ir RoR
Bilas Geitsas: kol dar nebuvo garsus
Danas Briklinas: skaičiuoklės autorius
P-NP: Ant sveiko proto svarstyklių
AWK kalba - sena ir nuolat aktuali
Ar mašina kada nors mąstys?
Programavimo kalbų klegesys
Mūšis kibernetiniame pasaulyje
Naujojo tipo mokslas
ARPANET istorija
Visata kaip kompiuteris
Kobolo motina
Haketonai
|