Programavimas

Funkcinis programavimas „Java“ kūrėjams, 1 dalis

„Java 8“ supažindino „Java“ kūrėjus su funkciniu programavimu su „lambda“ išraiškomis. Šis „Java“ leidimas iš tikrųjų pranešė kūrėjams, kad nebepakanka galvoti apie „Java“ programavimą tik iš imperatyvios, į objektą orientuotos perspektyvos. „Java“ kūrėjas taip pat turi sugebėti mąstyti ir koduoti naudodamasis deklaratyvia funkcine paradigma.

Šioje pamokoje pateikiami funkcinio programavimo pagrindai. Pradėsiu nuo terminologijos, tada įsigilinsime į funkcinio programavimo koncepcijas. Baigsiu supažindindamas jus su penkiomis funkcinėmis programavimo technikomis. Šių skyrių kodų pavyzdžiai padės jums pradėti nuo grynų funkcijų, aukštesnės eilės funkcijų, tingaus įvertinimo, uždarymo ir karijavimo.

Funkcinis programavimas auga

Elektros ir elektronikos inžinierių institutas (IEEE) įtraukė funkcines programavimo kalbas į 25 populiariausias programavimo kalbas 2018 m., O „Google Trends“ funkcinį programavimą šiuo metu vertina kaip populiaresnį nei į objektą.

Aišku, negalima ignoruoti funkcinio programavimo, tačiau kodėl jis populiarėja? Be kita ko, funkcinis programavimas palengvina programos teisingumo patikrinimą. Tai taip pat supaprastina lygiagrečių programų kūrimą. Lygiagretumas (arba lygiagretus apdorojimas) yra gyvybiškai svarbus norint pagerinti programos našumą.

atsisiųsti Gauti kodą Atsisiųskite šaltinį, pvz., programas, šioje pamokoje. Sukūrė Jeffas Friesenas, skirtas „JavaWorld“.

Kas yra funkcinis programavimas?

Kompiuteriai paprastai įgyvendina Von Neumanno architektūrą, kuri yra plačiai naudojama kompiuterinė architektūra, paremta matematiko ir fiziko Johno von Neumanno (ir kitų) 1945 m. Aprašymu. Ši architektūra yra tendencinga imperatyvus programavimas, kuri yra programavimo paradigma, kuri naudoja teiginius programos būsenai pakeisti. C, C ++ ir Java yra visos būtinos programavimo kalbos.

1977 m. Išskirtinis informatikas Johnas Backusas (pasižymėjęs darbu FORTRAN) skaitė paskaitą „Ar programavimą galima išlaisvinti iš fon Neumanno stiliaus?“. Backusas tvirtino, kad Von Neumanno architektūra ir su ja susijusios imperatyvios kalbos yra iš esmės ydingos, ir pateikė funkcinio lygio programavimo kalbą (FP) kaip sprendimą.

Aiškinamasis Backusas

Kadangi „Backus“ paskaita buvo pristatyta prieš kelis dešimtmečius, kai kurias jos idėjas gali būti sunku suvokti. Tinklaraštininkas Tomaszas Jaskuła prideda aiškumo ir išnašų savo tinklaraščio įraše nuo 2018 m. Sausio mėn.

Funkcinės programavimo sąvokos ir terminologija

Funkcinis programavimas yra programavimo stilius, kuriame skaičiavimai koduojami kaip funkcinės programavimo funkcijos. Tai yra matematines funkcijas primenantys konstruktai (pvz., Lambda funkcijos), kurie vertinami išraiškos kontekstuose.

Funkcinės programavimo kalbos yra deklaratyvus, tai reiškia, kad skaičiavimo logika išreikšta neapibūdinant jo valdymo srauto. Deklaraciniame programavime nėra jokių teiginių. Vietoj to, programuotojai naudoja išraiškas, norėdami pasakyti kompiuteriui, ką reikia daryti, bet ne tai, kaip atlikti užduotį. Jei esate susipažinę su SQL arba reguliariomis išraiškomis, turite patirties su deklaratyviniu stiliumi; abu naudoja posakius, kad apibūdintų, ką reikia padaryti, o ne naudoja teiginius, apibūdinančius, kaip tai padaryti.

A skaičiavimas funkciniame programavime apibūdinamas funkcijomis, kurios vertinamos išraiškos kontekstuose. Šios funkcijos nėra tos pačios kaip funkcijos, naudojamos imperatyviajame programavime, pavyzdžiui, „Java“ metodas, kuris grąžina vertę. Vietoj to, a funkcinis programavimas funkcija yra tarsi matematinė funkcija, kuri sukuria išvestį, kuri paprastai priklauso tik nuo jos argumentų. Kiekvieną kartą, kai funkcinė programavimo funkcija iškviečiama su tais pačiais argumentais, pasiekiamas tas pats rezultatas. Teigiama, kad funkcinio programavimo funkcijos pasireiškia referencinis skaidrumas. Tai reiškia, kad jūs galite pakeisti funkcijos iškvietimą gautąja verte nekeisdami skaičiavimo reikšmės.

Funkcinis programavimas palaiko nekintamumas, o tai reiškia, kad valstybė negali pasikeisti. Tai paprastai nėra atvejis, kai privaloma programuoti, kai imperatyvioji funkcija gali būti susieta su būsena (pvz., „Java“ egzemplioriaus kintamuoju). Skambinus šia funkcija skirtingais laikais su tais pačiais argumentais, gali būti skirtingos grąžinimo vertės, nes šiuo atveju būsena yra kintamas, tai reiškia, kad jis keičiasi.

Nepageidaujamas poveikis vykdant imperatyvų ir funkcinį programavimą

Būsenos pokyčiai yra privalomo programavimo šalutinis poveikis, užkertantis kelią referenciniam skaidrumui. Yra daugybė kitų šalutinių poveikių, apie kuriuos verta žinoti, ypač vertinant, ar programose naudoti imperatyvų, ar funkcinį stilių.

Vienas dažnas privalomas programavimo šalutinis poveikis yra tai, kai priskyrimo sakinys keičia kintamąjį pakeisdamas jo saugomą vertę. Funkcinės programavimo funkcijos nepalaiko kintamųjų priskyrimų. Kadangi kintamojo pradinė vertė niekada nesikeičia, funkcinis programavimas pašalina šį šalutinį poveikį.

Kitas dažnas šalutinis poveikis pasireiškia modifikuojant imperatyvios funkcijos elgesį, remiantis išmetama išimtimi, kuri yra stebima sąveika su skambinančiuoju. Norėdami gauti daugiau informacijos, žiūrėkite „Stack Overflow“ diskusiją „Kodėl išimties iškėlimas yra šalutinis poveikis?“

Trečias dažnas šalutinis poveikis pasireiškia, kai įvesties / išvesties operacija įveda tekstą, kurio negalima skaityti, arba išvedžioja tekstą, kurio negalima nerašyti. Žr. „Stack Exchange“ diskusiją „Kaip IO gali sukelti šalutinį poveikį programuojant funkciją?“ sužinoti daugiau apie šį šalutinį poveikį.

Pašalinus šalutinį poveikį, daug lengviau suprasti ir nuspėti skaičiavimo elgesį. Tai taip pat padeda padaryti kodą tinkamesnį lygiagrečiam apdorojimui, o tai dažnai pagerina programos našumą. Nors funkciniame programavime yra šalutinių poveikių, jų paprastai būna mažiau nei privalomuose programavimuose. Funkcinio programavimo naudojimas gali padėti parašyti kodą, kuris yra lengviau suprantamas, prižiūrimas ir išbandomas, taip pat yra daugkartinio naudojimo.

Funkcinio programavimo ištakos (ir kūrėjai)

Funkcinis programavimas atsirado iš lambda skaičiavimo, kurį pristatė Alonzo bažnyčia. Kita kilmė yra kombinacinė logika, kurią įvedė Mosesas Schönfinkelis, o vėliau sukūrė Haskellas Curry.

Objektyvus, palyginti su funkciniu programavimu

Aš sukūriau „Java“ programą, kuri skiriasi nuo imperatyvus, orientuotas į objektą ir deklaratyvus, funkcionalus programavimo metodai rašant kodą. Išstudijuokite žemiau pateiktą kodą ir tada atkreipsiu dėmesį į šių dviejų pavyzdžių skirtumus.

Sąrašas 1. Darbuotojai.java

importuoti java.util.ArrayList; importuoti java.util.List; viešoji klasė Darbuotojai {statinė klasė Darbuotojas {privatus Stygos pavadinimas; privatus int amžius; Darbuotojas (eilutės vardas, int amžius) {this.name = vardas; tai. amžius = amžius; } int getAge () {grįžimo amžius; } @Paisyti viešosios eilutės toString () {return name + ":" + age; }} public static void main (String [] argumentuoja) {Sąrašas darbuotojų = naujas ArrayList (); darbuotojai.add (naujas darbuotojas („John Doe“, 63 m.)); darbuotojai.add (naujas darbuotojas („Sally Smith“, 29)); darbuotojai.add (naujas darbuotojas („Bobas Jone“, 36 m.)); darbuotojai.add (nauja darbuotoja („Margaret Foster“, 53 m.)); printEmployee1 (darbuotojai, 50); System.out.println (); printEmployee2 (darbuotojai, 50); } public static void printEmployee1 (Darbuotojų sąrašas, int amžiaus) {for (Darbuotojo emp: darbuotojai) if (emp.getAge () <amžius) System.out.println (emp); } public static void printEmployee2 (Darbuotojų sąrašas, int amžiaus) {darbuotojai.stream () .filter (emp -> emp.age System.out.println (emp)); }}

1 sąrašas atskleidžia Darbuotojai programa, kuri sukuria keletą Darbuotojas objektus, tada išspausdina visų darbuotojų, kurie yra jaunesni nei 50 metų, sąrašą. Šis kodas parodo tiek objektinio, tiek funkcinio programavimo stilius.

printEmployee1 () metodas atskleidžia imperatyvų, į teiginius orientuotą požiūrį. Kaip nurodyta, šis metodas kartoja darbuotojų sąrašą, palygina kiekvieno darbuotojo amžių su argumento reikšme ir (jei amžius yra mažesnis nei argumentas) išspausdina darbuotojo duomenis.

„printEmployee2“ () metodas atskleidžia deklaratyvų, į išraišką orientuotą požiūrį, šiuo atveju įgyvendintą naudojant „Streams“ API. Užuot būtinai nurodžius, kaip spausdinti darbuotojus (žingsnis po žingsnio), išraiška nurodo norimą rezultatą ir palieka išsamią informaciją, kaip tai padaryti, „Java“. Galvoti apie filtras() kaip funkcinis atitikmuo jei pareiškimas ir kiekvienam() kaip funkciškai lygiavertis dėl pareiškimas.

1 sąrašą galite sudaryti taip:

javac Darbuotojai.java

Norėdami paleisti gautą programą, naudokite šią komandą:

java Darbuotojai

Išvestis turėtų atrodyti maždaug taip:

Sally Smith: 29 Bobas Jone'as: 36 Sally Smithas: 29 Bobas Jone'as: 36

Funkciniai programavimo pavyzdžiai

Kituose skyriuose mes išnagrinėsime penkis pagrindinius metodus, naudojamus funkciniame programavime: grynosios funkcijos, aukštesnės eilės funkcijos, tingus vertinimas, uždarymai ir karijavimas. Šio skyriaus pavyzdžiai yra užkoduoti „JavaScript“, nes jo paprastumas, palyginti su „Java“, leis mums sutelkti dėmesį į metodus. 2 dalyje apžvelgsime tuos pačius metodus naudodami „Java“ kodą.

2 sąraše pateikiamas šaltinio kodas „RunScript“, „Java“ programa, kuri naudoja „Java“ scenarijų API palengvinti „JavaScript“ kodo paleidimą. „RunScript“ bus pagrindinė visų būsimų pavyzdžių programa.

Sąrašas 2. RunScript.java

importuoti java.io.FileReader; importuoti java.io.IOException; importuoti javax.script.ScriptEngine; importuoti javax.script.ScriptEngineManager; importuoti javax.script.ScriptException; importuoti statinę java.lang.System. *; public class RunScript {public static void main (String [] args) {if (args.length! = 1) {err.println ("naudojimas: java RunScript scenarijus"); grįžti; } ScriptEngineManager manager = naujas ScriptEngineManager (); „ScriptEngine“ variklis = manager.getEngineByName („nashorn“); pabandykite {engine.eval (naujas FileReader (args [0])); } pagauti (ScriptException se) {err.println (se.getMessage ()); } gaudyti (IOException ioe) {err.println (ioe.getMessage ()); }}}

pagrindinis () metodas šiame pavyzdyje pirmiausia patikrina, ar nurodytas vienas komandinės eilutės argumentas (scenarijaus failo pavadinimas). Kitu atveju rodoma naudojimo informacija ir programa nutraukiama.

Darant prielaidą, kad yra šis argumentas, pagrindinis () akimirksniu javax.script.ScriptEngineManager klasė. „ScriptEngineManager“ yra įvesties taškas į „Java“ scenarijų API.

Kitas, „ScriptEngineManager“ objekto ScriptEngine getEngineByName (String shortName) metodas yra naudojamas norint gauti norimą scenarijų variklį trumpas vardas vertė. „Java 10“ palaiko „Nashorn“ scenarijų variklį, kuris gaunamas perduodant "nashorn" į „getEngineByName“ (). Grąžinto objekto klasė įgyvendina javax.script.ScriptEngine sąsaja.

„ScriptEngine“ pareiškia keli eval () scenarijaus vertinimo metodai. pagrindinis () iškviečia Object eval (Skaitytuvo skaitytuvas) metodas nuskaityti scenarijų iš jo java.io.FileReader objekto argumentas ir (darant prielaidą, kad java.io.IOException nemetamas), tada įvertinkite scenarijų. Šis metodas pateikia bet kokią scenarijaus grąžinimo vertę, kurios aš nepaisau. Be to, šis metodas meta javax.script.ScriptException kai scenarijuje įvyksta klaida.

Sudarykite 2 sąrašą taip:

javac RunScript.java

Aš jums parodysiu, kaip paleisti šią programą, kai pateiksiu pirmąjį scenarijų.

Funkcinis programavimas su grynomis funkcijomis

A grynoji funkcija yra funkcinė programavimo funkcija, kuri priklauso tik nuo jos įvesties argumentų ir jokios išorinės būsenos. An nešvari funkcija yra funkcinė programavimo funkcija, pažeidžianti bet kurį iš šių reikalavimų. Kadangi grynosios funkcijos neturi sąveikos su išoriniu pasauliu (išskyrus kitų grynųjų funkcijų iškvietimą), gryna funkcija visada pateikia tą patį rezultatą tiems patiems argumentams. Grynos funkcijos taip pat neturi pastebimo šalutinio poveikio.

Ar gryna funkcija gali atlikti įvestį / išėjimą?

Jei I / O yra šalutinis poveikis, ar gryna funkcija gali atlikti I / O? Atsakymas yra teigiamas. Haskell naudoja monadas šiai problemai spręsti. Norėdami sužinoti daugiau apie grynąsias funkcijas ir įvestį / išvestį, žr.

Grynos funkcijos, palyginti su nešvariomis funkcijomis

3 sąraše esantis „JavaScript“ prieštarauja nešvariam apskaičiuoti premiją () funkcija su gryna apskaičiuoti premiją2 () funkcija.

3 sąrašas. Grynų ir nešvarių funkcijų palyginimas (scenarijus1.js)

// nešvarus premijos apskaičiavimas var limit = 100; funkcija calcbonus (numSales) {return (numSales> limit)? 0,10 * numSales: 0} spausdinti (apskaičiuoti premiją (174)) // grynosios premijos skaičiavimo funkcija apskaičiuoti premiją2 (numSales) {grąžinti (numSales> 100)? 0,10 * numSales: 0} spausdinti (apskaičiuoti premiją2 (174))

apskaičiuoti premiją () yra nešvarus, nes prieina prie išorės riba kintamasis. Priešingai, apskaičiuoti premiją2 () yra grynas, nes laikosi abiejų grynumo reikalavimų. Bėk scenarijus1.js taip:

java RunScript script1.js

Štai išvestis, kurią turėtumėte stebėti:

17.400000000000002 17.400000000000002

Tarkim apskaičiuoti premiją2 () buvo refraktuotas grąžos apskaičiavimo premija (numSales). Norėčiau apskaičiuoti premiją2 () vis tiek būti grynas? Atsakymas yra ne: kai gryna funkcija iškviečia nešvarią funkciją, „gryna funkcija“ tampa nešvari.

Kai nėra grynųjų funkcijų priklausomybės nuo duomenų, jas galima įvertinti bet kokia tvarka, nedarant įtakos rezultatui, todėl jos yra tinkamos vykdyti lygiagrečiai. Tai yra vienas iš funkcinio programavimo privalumų.

Daugiau apie nešvarias funkcijas

Ne visos funkcinės programavimo funkcijos turi būti grynos. Kaip paaiškina funkcinis programavimas: grynosios funkcijos, galima (o kartais ir pageidautina) „atskirti gryną, funkcionalų, vertybėmis pagrįstą jūsų programos šerdį nuo išorinio, būtino apvalkalo“.

Funkcinis programavimas su aukštesnio lygio funkcijomis

A aukštesnės eilės funkcija yra matematinė funkcija, kuri gauna funkcijas kaip argumentus, grąžina funkciją skambinančiajam arba abu. Vienas iš pavyzdžių yra skaičiavimo diferencialinis operatorius, d / dx, kuris grąžina funkcijos išvestinę f.

Pirmos klasės funkcijos yra pirmos klasės piliečiai

Su matematine aukštesnės eilės funkcijos samprata glaudžiai susijusi pirmos klasės funkcija, kuri yra funkcinė programavimo funkcija, kuri argumentais laiko kitas funkcines programavimo funkcijas ir (arba) pateikia funkcinę programavimo funkciją. Pirmos klasės funkcijos yra pirmos klasės piliečių nes jie gali atsirasti visur, kur tik gali kiti pirmos klasės programos objektai (pvz., skaičiai), įskaitant priskyrimą kintamajam arba perdavimą kaip argumentą funkcijai ar jos grąžinimą.

4 sąraše esanti „JavaScript“ rodo anoniminių palyginimo funkcijų perdavimą pirmos klasės rūšiavimo funkcijai.

Anoniminių palyginimo funkcijų perdavimas (scenarijus2.js)

funkcija rūšiuoti (a, cmp) {for (var pass = 0; pass  praeiti; i--) if (cmp (a [i], a [perdavimas]) <0) {var temp = a [i] a [i] = a [perduoti] a [perduoti] = temp}} var a = [ 22, 91, 3, 45, 64, 67, -1] rūšiuoti (a, funkcija (i, j) {grąžinti i - j;}) a.forEch (funkcija (įrašas) {spausdinti (įrašas)}) spausdinti ( '\ n') rūšiuoti (a, funkcija (i, j) {grąžinti j - i;}) a.forEch (funkcija (įrašas) {spausdinti (įrašas)}) spausdinti ('\ n') a = ["X "," E "," Q "," A "," P "] rūšiuoti (a, funkcija (i, j) {grąžinti i  j; }) a. kiekvienam (funkcija (įrašas) {spausdinti (įrašas)}) spausdinti ('\ n') rūšiuoti (a, funkcija (i, j) {grąžinti i> j? -1: i <j;}) a .forEach (funkcija (įrašas) {spausdinti (įrašas)})

Šiame pavyzdyje pradinis rūšiuoti () skambutis gauna masyvą kaip pirmąjį argumentą, po kurio eina anoniminė palyginimo funkcija. Kai iškviečiama, vykdoma anoniminė palyginimo funkcija grįžti i - j; pasiekti kylančią rūšį. Atbuline eiga i ir j, antroji palyginimo funkcija pasiekia mažėjančią rūšiavimą. Trečias ir ketvirtas rūšiuoti () skambučiai gauna anonimines palyginimo funkcijas, kurios šiek tiek skiriasi, kad būtų galima tinkamai palyginti eilutės reikšmes.

Paleiskite scenarijus2.js pavyzdys:

java RunScript script2.js

Štai laukiama produkcija:

-1 3 22 45 64 67 91 91 67 64 45 22 3 -1 A E P Q X X Q P E A

Filtruoti ir žemėlapį

Funkcinės programavimo kalbos paprastai teikia kelias naudingas aukštesnės eilės funkcijas. Du įprasti pavyzdžiai yra filtras ir žemėlapis.

  • A filtras tam tikra tvarka apdoroja sąrašą, kad būtų sukurtas naujas sąrašas, kuriame būtų tiksliai tie pirminio sąrašo elementai, kuriems nurodytas predikatas (pagalvokite apie loginę išraiška) grąžina tikrovę.
  • A žemėlapis taiko tam tikrą funkciją kiekvienam sąrašo elementui, grąžindama rezultatų sąrašą ta pačia tvarka.

„JavaScript“ palaiko filtravimo ir susiejimo funkcijas per filtras() ir žemėlapis () aukštesnės eilės funkcijos. 5 sąrašas rodo šias nelyginių skaičių filtravimo ir skaičių susiejimo su jų kubeliais funkcijas.

5. Filtravimas ir susiejimas (scenarijus3.js)

spausdinti ([1, 2, 3, 4, 5, 6] .filter (function (num) {return num% 2 == 0})) spausdinti ('\ n') spausdinti ([3, 13, 22]. žemėlapis (funkcija (skaičius) {grąžinimo numeris * 3}))

Paleiskite scenarijus3.js pavyzdys:

java RunScript script3.js

Turėtumėte stebėti šį rezultatą:

2,4,6 9,39,66

Sumažinti

Kita įprasta aukštesnės eilės funkcija yra sumažinti, kuris dažniau žinomas kaip klostė. Ši funkcija sumažina sąrašą iki vienos vertės.

6 sąraše naudojami „JavaScript“ sumažinti () aukštesnės eilės funkcija, sumažinanti skaičių masyvą iki vieno skaičiaus, kuris tada padalinamas iš masyvo ilgio, kad gautų vidurkį.

Sąrašas 6. Sumažinti skaičių masyvą iki vieno skaičiaus (script4.js)

var numeriai = [22, 30, 43] spausdinti (numeriai. reduce (funkcija (acc, curval) {return acc + curval}) / skaiciai.length)

Paleiskite „Listing 6“ scenarijų ( script4.js) taip:

java RunScript script4.js

Turėtumėte stebėti šį rezultatą:

31.666666666666668

Galite pagalvoti, kad filtrai, žemėlapiai ir mažesnės aukštesnės eilės funkcijos pašalina if-else ir įvairių ciklo sakinių poreikį, ir jūs būsite teisūs. Jų vidinis įgyvendinimas rūpinasi sprendimais ir kartojimu.

Aukštesnės eilės funkcija naudoja rekursiją iteracijai pasiekti. Rekursyvioji funkcija iškviečia save ir leidžia operaciją kartoti tol, kol ji pasiekia a bazinis atvejis. Taip pat galite pasinaudoti rekursija, kad pasiektumėte funkcinio kodo iteraciją.

Funkcinis programavimas su tingiu vertinimu

Kita svarbi funkcinė programavimo funkcija yra tingus vertinimas (taip pat žinomas kaip neprivalomas vertinimas), o tai yra ekspresijos vertinimo atidėjimas kuo ilgiau. Tingi vertinimas siūlo keletą privalumų, įskaitant šiuos du:

  • Brangius (laiko atžvilgiu) skaičiavimus galima atidėti, kol jų tikrai nereikia.
  • Galimos neribotos kolekcijos. Jie tieks elementus tol, kol to paprašys.

Tingus vertinimas yra neatsiejama nuo Haskello savybių. Jis nieko neapskaičiuos (įskaitant funkcijos argumentus prieš iškviečiant funkciją), nebent tai padaryti būtina.

„Java“ srautų API išnaudoja tingų vertinimą. Tarpinės srauto operacijos (pvz., filtras()) visada tingisi; jie nieko nedaro iki terminalo operacijos (pvz., kiekvienam()) vykdoma.

Nors tingus vertinimas yra svarbi funkcinių kalbų dalis, net daugelis imperatyviųjų kalbų teikia tvirtą palaikymą kai kurioms tinginystės formoms. Pvz., Dauguma programavimo kalbų palaiko trumpojo jungimo vertinimą Boolean AND ir OR operatorių kontekste. Šie operatoriai tingi, atsisako vertinti savo dešinės rankos operandus, kai kairysis operandas yra melagingas (IR) arba teisingas (ARBA).

7 sąrašas yra tinginio vertinimo „JavaScript“ scenarijuje pavyzdys.

7. tingus vertinimas „JavaScript“ (script5.js)

var a = klaidinga && brangi funkcija ("1") var b = tikra && brangi funkcija ("2") var c = klaidinga || dearFunction ("3") var d = true || brangios funkcijos ("4") funkcija brangios funkcijos (id) {spausdinimo ("brangios funkcijos () iškvietimas su" + id)}}

Paleiskite kodą script5.js taip:

java RunScript script5.js

Turėtumėte stebėti šį rezultatą:

brangus Funkcija () iškviečiamas su 2 brangus Funkcija () iškviečiamas su 3

Tingus vertinimas dažnai derinamas su atmintimi - optimizavimo technika, pirmiausia naudojama kompiuterių programoms pagreitinti, saugant brangių funkcijų iškvietimų rezultatus ir grąžinant talpykloje esantį rezultatą, kai pasikartoja tos pačios įvestys.

Kadangi tingus vertinimas neveikia su šalutiniais poveikiais (pvz., Kodu, kuriame pateikiamos išimtys ir įvesties / išvesties išvestimis), dažniausiai naudojamos imperatyvios kalbos nekantrus vertinimas (taip pat žinomas kaip griežtas vertinimas), kur išraiška įvertinama, kai tik ji susiejama su kintamuoju.

Daugiau apie tingų vertinimą ir atmintines

„Google“ paieška atskleis daug naudingų diskusijų apie tingų vertinimą su atmintinėmis ar be jų. Vienas pavyzdžių yra „„ JavaScript “optimizavimas naudojant funkcinį programavimą“.

Funkcinis programavimas su uždarikliais

Pirmos klasės funkcijos siejamos su a samprata uždarymas, kuri yra nuolatinė taikymo sritis, kuri laikosi vietinių kintamųjų net ir tada, kai kodo vykdymas palieka bloką, kuriame buvo apibrėžti vietiniai kintamieji.

Amatų uždarymai

Veiklos požiūriu a uždarymas yra įrašas, kuriame saugoma funkcija ir jos aplinka. Aplinka susieja kiekvieną laisvą funkcijos kintamąjį (kintamieji, naudojami vietoje, bet apibrėžti uždaroje srityje) su verte ar nuoroda, prie kurios buvo susietas kintamojo vardas, kai buvo sukurtas uždarymas. Tai leidžia funkcijai pasiekti tuos užfiksuotus kintamuosius per uždaromas jų verčių ar nuorodų kopijas, net jei funkcija yra iškviečiama už jų taikymo srities ribų.

Kad būtų lengviau išsiaiškinti šią sąvoką, „8 sąraše“ pateikiamas „JavaScript“ scenarijus, įvedantis paprastą uždarymą. Scenarijus pagrįstas čia pateiktu pavyzdžiu.

8 sąrašas. Paprastas uždarymas (script6.js)

funkcija pridėti (x) {funkcija dalinispridėti (y) {grąžinti y + x} grąžinti dalinįpridėti} var add10 = pridėti (10) var pridėti20 = pridėti (20) spausdinti (pridėti10 (5)) spausdinti (pridėti20 (5))

8 sąrašas apibrėžia pirmos klasės funkciją, pavadintą papildyti() su parametru x ir įdėta funkcija Dalinis Pridėti (). Įdėta funkcija Dalinis Pridėti () turi prieigą prie x nes x yra papildyti()leksinę sritį. Funkcija papildyti() pateikia uždarymą, kuriame yra nuoroda į Dalinis Pridėti () ir aplinkinės aplinkos kopija papildyti(), kuriame x turi vertę, priskirtą konkrečiam iškvietimui papildyti().

Nes papildyti() pateikia funkcijos tipo, kintamųjų reikšmę pridėti10 ir pridėti20 taip pat turi funkcijos tipą. pridėti10 (5) iškvietimas grįžta 15 nes iškvietimas priskiria 5 į parametrą y kvietime Dalinis Pridėti (), naudodami išsaugotą aplinką Dalinis Pridėti () kur x yra 10. pridėti20 (5) iškvietimas grįžta 25 nes, nors ir priskiria 5 į y kvietime Dalinis Pridėti (), dabar ji naudoja kitą išsaugotą aplinką Dalinis Pridėti () kur x yra 20. Taigi, nors pridėti10 () ir pridėti20 () naudoti tą pačią funkciją Dalinis Pridėti (), susietos aplinkos skiriasi ir iškvietimai bus surišti x į dvi skirtingas vertes dviejuose iškvietimuose, įvertinant funkciją į du skirtingus rezultatus.

Paleiskite „Listing 8“ scenarijų ( script6.js) taip:

java RunScript script6.js

Turėtumėte stebėti šį rezultatą:

15 25

Funkcinis programavimas su kariu

Karis yra būdas kelių argumentų funkcijos vertimą paversti lygiaverčio vieno argumento funkcijų sekos vertinimu. Pavyzdžiui, funkcijai reikia dviejų argumentų: x ir y. Karijavimas paverčia funkciją tik paėmimu x ir grąžinti tik tam reikalingą funkciją y. Karijavimas yra susijęs su daliniu taikymu, bet nėra tas pats, kas yra procesas, kai funkcijai pritvirtinama keletas argumentų, sukuriant kitą mažesnio ariteto funkciją.

9 sąraše pateikiamas „JavaScript“ scenarijus, parodantis karijavimą.

9. „Currying“ naudojimas „JavaScript“ (scenarijus7.js)

funkcija padauginti (x, y) {return x * y} funkcija curried_multiply (x) {return funkcija (y) {return x * y}} print (dauginti (6, 7)) print (curried_multiply (6) (7)) var mul_by_4 = curried_multiply (4) print (mul_by_4 (2))

Scenarijuje pateikiami neskubantys dviejų argumentų argumentai padauginti () funkcija, po kurios seka pirmos klasės curried_multiply () funkcija, gaunanti daugybinių argumentų x ir grąžina uždarymą, kuriame yra nuoroda į anoniminę funkciją (kuri gauna daugiklio argumentą y) ir aplinkinės aplinkos kopija curried_multiply (), kuriame x turi reikšmę, priskirtą jai curried_multiply ().

Pirmiausia iškviečiama likusi scenarijaus dalis padauginti () dviem argumentais ir atspausdina rezultatą. Tada jis pasikviečia curried_multiply () dviem būdais:

  • curried_multiply (6) (7) rezultatai trumpas_padauginti (6) įvykdęs pirmas. Grąžintas uždarymas vykdo anoniminę funkciją su uždarymo išsaugotu x vertė 6 padauginta iš 7.
  • var mul_by_4 = curried_multiply (4) vykdo trumpas (4) ir priskiria uždarymą mul_by_4. mul_by_4 (2) vykdo anoniminę funkciją su uždarymo funkcija 4 reikšmė ir pateiktas argumentas 2.

Paleiskite „Listing 9“ scenarijų ( scenarijus7.js) taip:

java RunScript script7.js

Turėtumėte stebėti šį rezultatą:

42 42 8

Kodėl verta naudoti karį?

Savo tinklaraščio įraše „Kodėl padeda karis“ Hughas Jacksonas pastebi, kad „mažus gabalėlius galima lengvai sukonfigūruoti ir pakartotinai panaudoti be netvarkos“. „Quora“ „Kokie yra kario pranašumai funkciniame programavime?“ apibūdina karį kaip „pigią priklausomybės įpurškimo formą“, kuri palengvina kartografavimo / filtravimo / lankstymo procesą (ir apskritai aukštesnės eilės funkcijas). Šiame klausime ir atsakyme taip pat pažymima, kad karijavimas „padeda mums sukurti abstrakčias funkcijas“.

Apibendrinant

Šioje pamokoje išmokote keletą funkcinio programavimo pagrindų. Mes naudojome „JavaScript“ pavyzdžius, kad ištirtume penkis pagrindinius funkcinio programavimo metodus, kuriuos toliau nagrinėsime naudodami „Java“ kodą 2 dalyje. Be to, kad apžiūrėsite „Java 8“ funkcines programavimo galimybes, antroji šios mokymo programos pusė padės jums pradėti mąstyti funkcionaliai, konvertuojant į objektą orientuoto Java kodo pavyzdį į jo funkcinį atitikmenį.

Sužinokite daugiau apie funkcinį programavimą

Knyga „Funkcinio programavimo įvadas“ (Richardas Birdas ir Philipas Wadleris, „Prentice Hall“ tarptautinė skaičiavimo mokslo serija, 1992) man pasirodė naudinga mokantis funkcinio programavimo pagrindų.

Šią istoriją „Funkcinis programavimas„ Java “kūrėjams, 1 dalis“ iš pradžių paskelbė „JavaWorld“.

$config[zx-auto] not found$config[zx-overlay] not found