Ohjelmointi

Toimiva ohjelmointi Java-kehittäjille, osa 1

Java 8 esitteli Java-kehittäjät toiminnalliseen ohjelmointiin lambda-lausekkeilla. Tämä Java-julkaisu ilmoitti kehittäjille tehokkaasti, että ei enää riitä ajattelemaan Java-ohjelmointia vain välttämättömästä, olio-orientoidusta näkökulmasta. Java-kehittäjän on myös kyettävä ajattelemaan ja koodaamaan käyttäen deklaratiivista toiminnallista paradigmaa.

Tämä opetusohjelma esittelee toiminnallisen ohjelmoinnin perusteet. Aloitan terminologialla, sitten kaivetaan toiminnallisiin ohjelmointikonsepteihin. Lopuksi esitän sinulle viisi toiminnallista ohjelmointitekniikkaa. Näiden osien koodiesimerkit auttavat sinua aloittamaan puhtaat toiminnot, korkeamman tason toiminnot, laiskan arvioinnin, sulkemiset ja curryn.

Toiminnallinen ohjelmointi on kasvussa

Sähkö- ja elektroniikkasuunnittelijoiden instituutti (IEEE) sisälsi toiminnalliset ohjelmointikielet 25 parhaana ohjelmointikielenä vuodelle 2018, ja Google Trends luokittelee toiminnallisen ohjelmoinnin tällä hetkellä suositummaksi kuin olio-ohjelmointi.

Toiminnallista ohjelmointia ei selvästikään voida sivuuttaa, mutta miksi siitä tulee yhä suositumpi? Toiminnallinen ohjelmointi helpottaa muun muassa ohjelman oikeellisuuden tarkistamista. Se myös yksinkertaistaa samanaikaisten ohjelmien luomista. Samanaikaisuus (tai rinnakkaiskäsittely) on elintärkeää sovellusten suorituskyvyn parantamiseksi.

lataa Hanki koodi Lataa lähdekoodi esimerkiksi sovelluksiin tässä opetusohjelmassa. Luonut Jeff Friesen JavaWorldille.

Mikä on toiminnallinen ohjelmointi?

Tietokoneet käyttävät tyypillisesti Von Neumannin arkkitehtuuria, joka on laajalti käytetty tietokonearkkitehtuuri, joka perustuu matemaatikon ja fyysikon John von Neumannin (ja muiden) 1945 kuvaukseen. Tämä arkkitehtuuri on puolueellinen välttämätön ohjelmointi, joka on ohjelmointiparadigma, joka käyttää lauseita ohjelman tilan muuttamiseen. C, C ++ ja Java ovat kaikki välttämättömiä ohjelmointikieliä.

Vuonna 1977 arvostettu tietojenkäsittelytieteen tutkija John Backus (merkittävä FORTRAN-työstään) piti luennon "Voiko ohjelmointi vapautua von Neumannin tyylistä?" Backus väitti, että Von Neumannin arkkitehtuuri ja siihen liittyvät välttämättömät kielet ovat pohjimmiltaan puutteellisia, ja esitteli toiminnallisena ohjelmointikielenä (FP) ratkaisuna.

Selkeyttävä Backus

Koska Backus-luento esiteltiin useita vuosikymmeniä sitten, joitain sen ideoita saattaa olla vaikea ymmärtää. Bloggaaja Tomasz Jaskuła lisää selkeyttä ja alaviitteitä tammikuun 2018 blogikirjoituksessaan.

Toiminnalliset ohjelmointikonseptit ja terminologia

Toiminnallinen ohjelmointi on ohjelmointityyli, jossa laskelmat on koodattu toiminnalliset ohjelmointitoiminnot. Nämä ovat matemaattisten funktioiden kaltaisia ​​rakenteita (esim. Lambda-funktiot), jotka arvioidaan ilmentymiskonteksteissa.

Toiminnalliset ohjelmointikielet ovat vakuuttava, mikä tarkoittaa, että laskennan logiikka ilmaistaan ​​kuvaamatta sen ohjausvirtaa. Deklaratiivisessa ohjelmoinnissa ei ole lausekkeita. Sen sijaan ohjelmoijat käyttävät ilmaisuja kertomaan tietokoneelle, mitä on tehtävä, mutta eivät miten tehtävä suoritetaan. Jos olet perehtynyt SQL: ään tai säännöllisiin lausekkeisiin, sinulla on jonkin verran kokemusta deklaratiivisesta tyylistä; molemmat käyttävät lausekkeita kuvaamaan mitä on tehtävä, sen sijaan, että käyttäisivät lauseita sen kuvaamiseksi, miten se tehdään.

A laskenta toiminnallisessa ohjelmoinnissa kuvataan funktioilla, joita arvioidaan lausekekonteksteissa. Nämä toiminnot eivät ole samat kuin pakollisessa ohjelmoinnissa käytettävät toiminnot, kuten Java-menetelmä, joka palauttaa arvon. Sen sijaan a toiminnallinen ohjelmointi funktio on kuin matemaattinen funktio, joka tuottaa lähdön, joka riippuu yleensä vain sen argumenteista. Joka kerta kun toiminnallinen ohjelmointitoiminto kutsutaan samoilla argumenteilla, saavutetaan sama tulos. Funktionaalisen ohjelmoinnin toimintojen sanotaan olevan esillä viitteellinen avoimuus. Tämä tarkoittaa, että voit korvata funktion kutsun tuloksena olevalla arvolla muuttamatta laskennan merkitystä.

Toiminnallinen ohjelmointi suosii muuttumattomuus, mikä tarkoittaa, että tila ei voi muuttua. Tämä ei tyypillisesti ole välttämätöntä pakollisessa ohjelmoinnissa, jossa pakollinen funktio saattaa liittyä tilaan (kuten Java-instanssimuuttuja). Tämän funktion kutsuminen eri aikoina samoilla argumenteilla saattaa johtaa erilaisiin paluuarvoihin, koska tässä tapauksessa tila on vaihteleva, eli se muuttuu.

Pakollisen ja toiminnallisen ohjelmoinnin sivuvaikutukset

Tilamuutokset ovat pakollisen ohjelmoinnin sivuvaikutus, joka estää viittausten läpinäkyvyyden. On monia muita sivuvaikutuksia, joista kannattaa tietää, varsinkin kun arvioit, käytetäänkö pakollista vai toiminnallista tyyliä ohjelmissasi.

Yksi yleinen sivuvaikutus pakollisessa ohjelmoinnissa on, kun tehtävälauseke mutatoi muuttujan muuttamalla sen tallennettua arvoa. Funktionaalisen ohjelmoinnin toiminnot eivät tue muuttujien määrityksiä. Koska muuttujan alkuarvo ei koskaan muutu, toiminnallinen ohjelmointi eliminoi tämän sivuvaikutuksen.

Toinen yleinen sivuvaikutus tapahtuu, kun pakollisen toiminnon käyttäytymistä muokataan heitetyn poikkeuksen perusteella, mikä on havaittavaa vuorovaikutusta soittajan kanssa. Lisätietoja on pinon ylivuotokeskustelussa "Miksi poikkeuksen nostaminen on sivuvaikutus?"

Kolmas yleinen sivuvaikutus tapahtuu, kun I / O-toiminto syöttää tekstiä, jota ei voi lukea, tai tulostaa tekstiä, jota ei voi kirjoittaa. Katso Stack Exchange -keskustelu "Kuinka IO voi aiheuttaa sivuvaikutuksia toiminnallisessa ohjelmoinnissa?" oppia lisää tästä sivuvaikutuksesta.

Haittavaikutusten poistaminen helpottaa laskennallisen käyttäytymisen ymmärtämistä ja ennustamista. Se auttaa myös tekemään koodista sopivampaa rinnakkaiskäsittelyyn, mikä parantaa usein sovelluksen suorituskykyä. Vaikka toiminnallisessa ohjelmoinnissa on sivuvaikutuksia, niitä on yleensä vähemmän kuin pakollisessa ohjelmoinnissa. Toiminnallisen ohjelmoinnin avulla voit kirjoittaa koodia, joka on helpommin ymmärrettävissä, ylläpidettävissä ja testattavissa sekä uudelleenkäytettävissä.

Toiminnallisen ohjelmoinnin lähteet (ja alullepanijat)

Toiminnallinen ohjelmointi sai alkunsa lambda-laskennasta, jonka Alonzo Church esitteli. Toinen alkuperä on yhdistelmälogiikka, jonka Moses Schönfinkel esitteli ja jonka myöhemmin kehitti Haskell Curry.

Kohdistettu vs. toiminnallinen ohjelmointi

Olen luonut Java-sovelluksen, joka erottaa välttämätöntä, olio-suuntautunutta ja vakuuttava, toimiva ohjelmointitavat koodin kirjoittamiseen. Tutki alla olevaa koodia ja sitten tuon esiin näiden kahden esimerkin väliset erot.

Listaus 1. Työntekijät.java

tuo java.util.ArrayList; tuo java.util.List; julkinen luokka Työntekijät {staattinen luokka Työntekijä {yksityinen Merkkijono nimi; yksityinen ikä; Työntekijä (merkkijonon nimi, ikä) {this.name = nimi; tämä. ikä = ikä; } int getAge () {paluuikä; } @Override public String toString () {return name + ":" + age; }} public static void main (Merkkijono [] argumentit) {Luettelo työntekijöistä = uusi ArrayList (); työntekijät.add (uusi työntekijä ("John Doe", 63)); työntekijät.add (uusi työntekijä ("Sally Smith", 29)); työntekijät.add (uusi työntekijä ("Bob Jone", 36)); työntekijät.add (uusi työntekijä ("Margaret Foster", 53)); printEmployee1 (työntekijät, 50); System.out.println (); printEmployee2 (työntekijät, 50); } public static void printEmployee1 (Luettelo työntekijöistä, ikä) {for (Employee emp: työntekijät) if (emp.getAge () <ikä) System.out.println (emp); } public static void printEmployee2 (List työntekijät, ikä) {työntekijät.virta (). suodatin (emp -> emp.age System.out.println (emp)); }}

Listaus 1 paljastaa Työntekijät sovellus, joka luo muutaman Työntekijä objektit, tulostaa sitten luettelon kaikista alle 50-vuotiaista työntekijöistä. Tämä koodi osoittaa sekä olio- että toiminnalliset ohjelmointityylit.

printEmployee1 () menetelmä paljastaa välttämättömän, lausuntokeskeisen lähestymistavan. Kuten on määritelty, tämä menetelmä toistaa työntekijäluettelon, vertaa kunkin työntekijän ikää argumenttiarvoon ja (jos ikä on pienempi kuin argumentti), tulostaa työntekijän tiedot.

printEmployee2 () menetelmä paljastaa deklaratiivisen, lausekekeskeisen lähestymistavan, joka on tässä tapauksessa toteutettu Streams API: n kanssa. Sen sijaan, että määritettäisiin pakollisesti työntekijöiden tulostaminen (vaihe vaiheelta), lauseke määrittää halutun lopputuloksen ja jättää yksityiskohdat sen tekemiselle Java: lle. Ajatella suodattaa() funktionaalisena ekvivalenttina jos lausunto ja jokaiselle() toiminnallisesti vastaavia varten lausunto.

Voit koota luettelon 1 seuraavasti:

javac Employees.java

Suorita tuloksena oleva sovellus seuraavalla komennolla:

java Työntekijät

Tuloksen pitäisi näyttää tältä:

Sally Smith: 29 Bob Jone: 36 Sally Smith: 29 Bob Jone: 36

Toiminnalliset ohjelmointiesimerkit

Seuraavissa osioissa tutkitaan viittä toiminnallisessa ohjelmoinnissa käytettyä ydintekniikkaa: puhtaat toiminnot, korkeamman asteen toiminnot, laiska arviointi, sulkemiset ja curry. Tämän osan esimerkit on koodattu JavaScriptiin, koska sen yksinkertaisuus verrattuna Java-toimintaan antaa meille mahdollisuuden keskittyä tekniikoihin. Osassa 2 tarkastelemme näitä samoja tekniikoita Java-koodilla.

Luettelossa 2 esitetään lähdekoodi RunScript, Java-sovellus, joka käyttää Java-komentosovellusliittymää helpottamaan JavaScript-koodin suorittamista. RunScript on kaikkien tulevien esimerkkien perusohjelma.

Listaus 2. RunScript.java

tuo java.io.FileReader; tuo java.io.IOException; tuo javax.script.ScriptEngine; tuo javax.script.ScriptEngineManager; tuo javax.script.ScriptException; tuo staattinen java.lang.System. *; public class RunScript {public static void main (String [] args) {if (args.pituus! = 1) {err.println ("käyttö: java RunScript-komentosarja"); palata; } ScriptEngineManager manager = uusi ScriptEngineManager (); ScriptEngine-moottori = manager.getEngineByName ("nashorn"); kokeile {engine.eval (uusi FileReader (args [0])); } catch (ScriptException se) {err.println (se.getMessage ()); } catch (IOException ioe) {err.println (ioe.getMessage ()); }}}

main () Tämän esimerkin menetelmä varmistaa ensin, että yksi komentoriviargumentti (komentotiedoston nimi) on määritetty. Muussa tapauksessa se näyttää käyttötiedot ja sulkee sovelluksen.

Olettaen, että tämä väite on läsnä, main () instantisoi javax.script.ScriptEngineManager luokassa. ScriptEngineManager on lähtökohta Java: n komentosovellusliittymään.

Seuraavaksi ScriptEngineManager esine ScriptEngine getEngineByName (String shortName) menetelmä kutsutaan haluttua komentosarjamoottoria varten lyhyt nimi arvo. Java 10 tukee Nashorn-komentosarjamoottoria, joka saadaan ohittamalla "nashorn" että getEngineByName (). Palautetun objektin luokka toteuttaa javax.script.ScriptEngine käyttöliittymä.

ScriptEngine julistaa useita eval () menetelmät komentosarjan arvioimiseksi. main () vetoaa Object eval (lukijan lukija) tapa lukea komentosarja sen java.io.FileReader objektiargumentti ja (olettaen että java.io.IOException ei heitetä) ja arvioi sitten komentosarja. Tämä menetelmä palauttaa kaikki komentosarjan palautusarvot, jotka jätän huomiotta. Myös tämä menetelmä heittää javax.script.ScriptException kun komentosarjassa tapahtuu virhe.

Koosta Listaus 2 seuraavasti:

javac RunScript.java

Näytän sinulle, kuinka tämä sovellus suoritetaan, kun olen esittänyt ensimmäisen komentosarjan.

Toiminnallinen ohjelmointi puhtailla toiminnoilla

A puhdas toiminto on toiminnallinen ohjelmointitoiminto, joka riippuu vain sen syöttö argumenteista eikä ulkoisesta tilasta. An epäpuhdas toiminto on toiminnallinen ohjelmointitoiminto, joka rikkoo kumpaakin näistä vaatimuksista. Koska puhtailla funktioilla ei ole vuorovaikutusta ulkomaailman kanssa (lukuun ottamatta muiden puhtaiden toimintojen kutsumista), puhdas funktio palauttaa aina saman tuloksen samoille argumenteille. Puhtailla toiminnoilla ei myöskään ole havaittavia sivuvaikutuksia.

Voiko puhdas toiminto suorittaa I / O: n?

Jos I / O on sivuvaikutus, voiko puhdas toiminto suorittaa I / O: n? Vastaus on kyllä. Haskell käyttää monadeja tämän ongelman ratkaisemiseen. Katso "Puhtaat toiminnot ja I / O" saadaksesi lisätietoja puhtaista toiminnoista ja I / O: sta.

Puhtaat toiminnot verrattuna epäpuhtaisiin toimintoihin

Listauksen 3 JavaScript erottaa epäpuhtauden Laske bonus () toimii puhtaalla Laske bonus2 () toiminto.

Luettelo 3. Puhtaiden ja epäpuhtaiden toimintojen vertailu (script1.js)

// epäpuhdas bonuksen laskentaraja = 100; funktio calcbonus (numSales) {return (numSales> raja)? 0,10 * numSales: 0} tulosta (calcbonus (174)) // puhdas bonuksen laskutoiminto calcbonus2 (numSales) {return (numSales> 100)? 0,10 * numSales: 0} tulosta (lasketa bonus2 (174))

Laske bonus () on epäpuhdas, koska se pääsee ulkoiseen raja muuttuja. Verrattuna, Laske bonus2 () on puhdas, koska se täyttää molemmat puhtauden vaatimukset. Juosta script1.js seuraavasti:

java RunScript script1.js

Seuraavassa on huomioitava tulos:

17.400000000000002 17.400000000000002

Olettaa Laske bonus2 () oli refactored tuoton laskentabonus (numSales). Olisi Laske bonus2 () silti olla puhdas? Vastaus on ei: kun puhdas funktio käynnistää epäpuhtaan funktion, "puhdas funktio" muuttuu epäpuhtaaksi.

Kun puhtaiden toimintojen välillä ei ole datariippuvuutta, ne voidaan arvioida missä tahansa järjestyksessä vaikuttamatta lopputulokseen, jolloin ne soveltuvat rinnakkaiseen suoritukseen. Tämä on yksi toiminnallisen ohjelmoinnin eduista.

Lisätietoja epäpuhtaista toiminnoista

Kaikkien toiminnallisten ohjelmointitoimintojen ei tarvitse olla puhtaita. Kuten toiminnallinen ohjelmointi: Pure Functions selittää, on mahdollista (ja joskus toivottavaa) "erottaa sovelluksen puhdas, toiminnallinen, arvopohjainen ydin ulkoisesta, välttämättömästä kuoresta".

Toiminnallinen ohjelmointi korkeamman tason toiminnoilla

A korkeamman asteen toiminto on matemaattinen funktio, joka vastaanottaa funktiot argumentteina, palauttaa toiminnon soittajalle tai molemmat. Yksi esimerkki on laskennan differentiaalioperaattori, d / dx, joka palauttaa funktion derivaatin f.

Ensimmäisen luokan toiminnot ovat ensiluokkaisia ​​kansalaisia

Matemaattiseen korkeamman asteen funktiokäsitteeseen liittyy läheisesti ensiluokkainen toiminto, joka on toiminnallinen ohjelmointitoiminto, joka ottaa muut toiminnalliset ohjelmointitoiminnot argumentteina ja / tai palauttaa toiminnallisen ohjelmointitoiminnon. Ensiluokkaiset toiminnot ovat ensiluokkaiset kansalaiset koska ne voivat näkyä missä vain muut ensiluokkaiset ohjelmakokonaisuudet (esim. numerot) voivat, mukaan lukien niiden määrääminen muuttujalle tai välittäminen argumenttina funktiolle tai palauttaminen siitä.

Listing 4: n JavaScript osoittaa, että nimettömät vertailutoiminnot välitetään ensimmäisen luokan lajittelutoiminnolle.

Listaus 4. Anonyymien vertailutoimintojen välittäminen (script2.js)

funktion lajittelu (a, cmp) {for (var pass = 0; pass  kulkea; i--) if (cmp (a [i], a [läpäisy]) <0) {var temp = a [i] a [i] = a [läpäise] a [läpäistä] = temp}} var a = [ 22, 91, 3, 45, 64, 67, -1] lajittelu (a, funktio (i, j) {return i - j;}) a. Jokaiselle (funktio (merkintä) {tulosta (merkintä)}) tulosta ( '\ n') lajittelu (a, function (i, j) {return j - i;}) a.forEach (function (entry) {print (entry)}) print ('\ n') a = ["X "," E "," Q "," A "," P "] lajittelu (a, funktio (i, j) {return i  j; }) a. jokaiselle (funktio (merkintä) {tulosta (merkintä)}) tulosta ('\ n') lajittelu (a, funktio (i, j) {palauta i> j? -1: i <j;}) a .forEach (toiminto (merkintä) {tulosta (merkintä)})

Tässä esimerkissä alkukirjain järjestellä() kutsu vastaanottaa matriisin ensimmäisenä argumenttinaan, jota seuraa anonyymi vertailutoiminto. Kun kutsutaan, anonyymi vertailutoiminto suoritetaan paluu i - j; saavuttaa nouseva lajittelu. Peruuttamalla i ja j, toinen vertailutoiminto saavuttaa laskevan lajittelun. Kolmas ja neljäs järjestellä() puhelut saavat nimettömiä vertailutoimintoja, jotka ovat hieman erilaisia, jotta merkkijonoarvot voidaan verrata oikein.

Suorita script2.js esimerkki seuraavasti:

java RunScript script2.js

Tässä on odotettu tuotos:

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

Suodata ja kartoita

Toiminnalliset ohjelmointikielet tarjoavat tyypillisesti useita hyödyllisiä korkeamman tason toimintoja. Kaksi yleistä esimerkkiä ovat suodatin ja kartta.

  • A suodattaa käsittelee luettelon jossain järjestyksessä tuottamaan uuden luettelon, joka sisältää täsmälleen ne alkuperäisen luettelon elementit, joille annettu predikaatti (ajattele Boolen lauseketta) palauttaa tosi.
  • A kartta käyttää tiettyä toimintoa luettelon jokaiseen elementtiin palauttamalla luettelon tuloksista samassa järjestyksessä.

JavaScript tukee suodatusta ja kartoitusta suodattaa() ja kartta() korkeamman asteen toiminnot. Listaus 5 osoittaa nämä toiminnot parittomien numeroiden suodattamiseksi ja numeroiden liittämiseksi kuutioihin.

Listaus 5. Suodatus ja kartoitus (script3.js)

tulosta ([1, 2, 3, 4, 5, 6]. suodatin (funktio (numero) {palauta numero% 2 == 0})) tulosta ('\ n') tulosta ([3, 13, 22]. kartta (funktio (num) {return num * 3}))

Suorita script3.js esimerkki seuraavasti:

java RunScript script3.js

Ota huomioon seuraava tulos:

2,4,6 9,39,66

Vähentää

Toinen yleinen korkeamman asteen toiminto on vähentää, joka tunnetaan yleisemmin taitoksena. Tämä toiminto pienentää luettelon yhteen arvoon.

Listaus 6 käyttää JavaScriptiä vähentää() korkeamman kertaluvun funktio, jolla pienennetään numeroryhmä yhdeksi numeroksi, joka jaetaan sitten taulukon pituudella keskiarvon saamiseksi.

Luettelo 6. Lukujoukon pienentäminen yhdeksi numeroksi (script4.js)

var numerot = [22, 30, 43] tulosta (numerot. pienennä (funktio (acc, käyrä) {return acc + käyrä}) / numerot. pituus)

Suorita Listing 6: n komentosarja (in script4.js) seuraavasti:

java RunScript script4.js

Ota huomioon seuraava tulos:

31.666666666666668

Saatat ajatella, että suodatin, kartoitus ja korkeamman asteen funktioiden vähentäminen sulkevat pois if-else- ja erilaiset looping-lauseet, ja olisit oikeassa. Heidän sisäinen toteutus huolehtii päätöksistä ja iteroinnista.

Korkeamman asteen funktio käyttää rekursiota iteroinnin saavuttamiseksi. Rekursiivinen toiminto kutsuu itseään, jolloin operaatio voidaan toistaa, kunnes se saavuttaa a perustapaus. Voit myös käyttää rekursiota iteroinnin saavuttamiseksi toiminnallisessa koodissasi.

Toimiva ohjelmointi ja laiska arviointi

Toinen tärkeä toiminnallinen ohjelmointiominaisuus on laiska arviointi (tunnetaan myös rajoittamaton arviointi), joka on ilmentämisen arvioinnin lykkäämistä niin kauan kuin mahdollista. Laiska arviointi tarjoaa useita etuja, mukaan lukien nämä kaksi:

  • Kalliita (ajallisia) laskelmia voidaan lykätä, kunnes ne ovat ehdottoman välttämättömiä.
  • Rajoittamattomat kokoelmat ovat mahdollisia. He toimittavat elementtejä niin kauan kuin niitä pyydetään tekemään niin.

Laiska arviointi on olennainen osa Haskellia. Se ei laske mitään (mukaan lukien funktion argumentit ennen funktion kutsumista), ellei se ole ehdottoman välttämätöntä.

Java's Streams -sovellusliittymä hyödyntää laiskaa arviointia. Streamin välitoiminnot (esim. suodattaa()) ovat aina laiskoja; he eivät tee mitään ennen päätelaitteen toimintaa (esim. jokaiselle()) suoritetaan.

Vaikka laiska arviointi on tärkeä osa toiminnallisia kieliä, jopa monet välttämättömät kielet tarjoavat sisäänrakennettua tukea joillekin laiskuuden muodoille. Esimerkiksi useimmat ohjelmointikielet tukevat oikosulun arviointia Boolen AND- ja OR-operaattoreiden yhteydessä. Nämä operaattorit ovat laiskoja kieltäytyessään arvioimasta oikeanpuoleisia operandejaan, kun vasemmanpuoleinen operandi on väärä (AND) tai tosi (OR).

Listaus 7 on esimerkki laiskasta arvioinnista JavaScript-komentosarjassa.

Listaus 7. Laiska arviointi JavaScriptissä (script5.js)

var a = väärä && kallis toiminto ("1") var b = tosi && kallis funktio ("2") var c = väärä || kallisfunktio ("3") var d = tosi || kallisFunction ("4") -funktio kallisFunction (id) {tulosta ("kallisFunction () kutsuttu" + id)}

Suorita koodi sisään script5.js seuraavasti:

java RunScript script5.js

Ota huomioon seuraava tulos:

kallisfunktio () kutsutaan kahdella kalliilla funktiolla () kutsutaan 3: lla

Laiska arviointi yhdistetään usein muistiinpanoon, optimointitekniikkaan, jota käytetään ensisijaisesti tietokoneohjelmien nopeuttamiseen tallentamalla kalliiden toimintokutsujen tulokset ja palauttamalla välimuistissa oleva tulos, kun samat syötteet toistuvat.

Koska laiska arviointi ei toimi sivuvaikutusten kanssa (kuten koodi, joka tuottaa poikkeuksia ja I / O), välttämättömät kielet käyttävät pääasiassa innokas arviointi (tunnetaan myös tiukka arviointi), jossa lauseke arvioidaan heti, kun se on sidottu muuttujaan.

Lisää laiskasta arvioinnista ja muistiinpanosta

Google-haku paljastaa monia hyödyllisiä keskusteluja laiskasta arvioinnista muistiinpanojen kanssa tai ilman. Yksi esimerkki on "JavaScriptin optimointi toiminnallisella ohjelmoinnilla".

Toimiva ohjelmointi sulkimilla

Ensimmäisen luokan toiminnot liittyvät käsitteeseen a päättäminen, joka on pysyvä laajuus, joka pitää kiinni paikallisista muuttujista myös sen jälkeen, kun koodin suoritus on jättänyt lohkon, johon paikalliset muuttujat määritettiin.

Käsityön sulkimet

Operatiivisesti a päättäminen on tietue, joka tallentaa toiminnon ja sen ympäristön. Ympäristö kartoittaa toiminnon vapaat muuttujat (muuttujat, joita käytetään paikallisesti, mutta määritetään oheisessa laajuudessa) arvolla tai viitteellä, johon muuttujan nimi oli sidottu sulkemisen yhteydessä. Sen avulla toiminto pääsee käsiksi siepattuihin muuttujiin niiden arvojen tai viitteiden sulkukopioiden kautta, vaikka toimintoa kutsutaan niiden soveltamisalan ulkopuolella.

Tämän käsitteen selventämiseksi Listing 8 tarjoaa JavaScript-komentosarjan, joka esittelee yksinkertaisen sulkemisen. Käsikirjoitus perustuu tässä esitettyyn esimerkkiin.

Listaus 8. Yksinkertainen sulkeminen (script6.js)

funktio lisää (x) {funktio osittain lisää (y) {palauta y + x} palaa osittain lisää} var add10 = lisää (10) var add20 = lisää (20) tulosta (lisää10 (5)) tulosta (lisää20 (5))

Listaus 8 määrittelee ensimmäisen luokan funktion lisätä() parametrilla x ja sisäkkäinen toiminto osittainenLisää (). Sisäkkäinen toiminto osittainenLisää () on pääsy x koska x on sisään lisätä()sanallinen laajuus. Toiminto lisätä() palauttaa sulkemisen, joka sisältää viittauksen osittainenLisää () ja kopio ympäröivästä ympäristöstä lisätä(), jossa x on sille määritetty arvo tietyssä kutsussa lisätä().

Koska lisätä() palauttaa funktiotyypin arvon muuttujat lisää10 ja lisää20 myös toiminnon tyyppi. lisää10 (5) kutsu palaa 15 koska kutsu määrittää 5 parametriin y puhelussa partsAdd (), käyttämällä tallennettua ympäristöä osittainenLisää () missä x On 10. lisää20 (5) kutsu palaa 25 koska vaikka se myös antaa 5 että y puhelussa partsAdd (), se käyttää nyt toista tallennettua ympäristöä osittainenLisää () missä x On 20. Siten vaikka lisää10 () ja lisää20 () käytä samaa toimintoa partsAdd (), siihen liittyvät ympäristöt eroavat toisistaan ​​ja sulkemisten kutsuminen sitoo x kahteen eri arvoon kahdessa kutsussa arvioimalla funktio kahteen eri tulokseen.

Suorita Listing 8: n komentosarja (in script6.js) seuraavasti:

java RunScript script6.js

Ota huomioon seuraava tulos:

15 25

Toimiva ohjelmointi currylla

Curry on tapa kääntää usean argumentin funktion arviointi vastaavan yhden argumentin funktioiden sarjan arvioinniksi. Esimerkiksi funktiossa on kaksi argumenttia: x ja y. Currying muuttaa toiminnon vain ottamiseen x ja palautetaan vain vievä toiminto y. Curry-toiminto liittyy osittaiseen sovellukseen, mutta ei ole sama kuin osittainen sovellus, joka on prosessi, jolla kiinnitetään joukko argumentteja funktioon ja tuotetaan toinen pienemmällä ariteetilla varustettu funktio.

Luettelossa 9 on JavaScript-komentosarja, joka osoittaa curry-toiminnon.

Listaus 9. Currying in JavaScript (script7.js)

funktion kerroin (x, y) {return x * y} funktio curried_multiply (x) {return function (y) {return x * y}} print (multiply (6, 7)) print (curried_multiply (6) (7)) var mul_by_4 = curried_multiply (4) tulosta (mul_by_4 (2))

Käsikirjoitus esittää kiistämättömän kahden argumentin kertoa () toiminto, jota seuraa ensimmäisen luokan kirsikka_multiply () funktio, joka vastaanottaa moninkertaisen argumentin x ja palauttaa sulkemisen, joka sisältää viittauksen nimettömään funktioon (joka saa kertoimen argumentin y) ja kopio ympäröivästä ympäristöstä kirsikka_multiply (), jossa x on sille määritetty arvo kutsussa kirsikka_multiply ().

Loput käsikirjoituksesta käynnistävät ensin kertoa () kahdella argumentilla ja tulostaa tuloksen. Sitten se vetoaa kirsikka_multiply () kahdella tavalla:

  • kirsikka_multiply (6) (7) johtaa kirsikka_multiply (6) suorittaa ensin. Palautettu sulku suorittaa nimettömän toiminnon sulkimen ollessa tallennettu x arvo 6 kerrotaan 7.
  • var mul_by_4 = curried_multiply (4) suorittaa kirsikka_multiply (4) ja määrittää sulkemisen hannu_4. hannu (2) suorittaa nimettömän toiminnon sulkimen kanssa 4 arvo ja välitetty argumentti 2.

Suorita Listing 9: n komentosarja (in script7.js) seuraavasti:

java RunScript script7.js

Ota huomioon seuraava tulos:

42 42 8

Miksi käyttää currya?

Hugh Jackson huomauttaa blogikirjoituksessaan "Miksi curry auttaa", että "pienet palat voidaan konfiguroida ja käyttää uudelleen helposti, ilman sotkua". Quoran "Mitkä ovat curryn edut toiminnallisessa ohjelmoinnissa?" kuvaa currya "halpana riippuvuusinjektion muotona", joka helpottaa kartoitus- / suodatus- / taittoprosessia (ja yleensä korkeamman asteen toimintoja). Tässä kysymyksessä ja vastauksessa todetaan myös, että curry "auttaa meitä luomaan abstrakteja toimintoja".

Tiivistettynä

Tässä opetusohjelmassa olet oppinut joitain toiminnallisen ohjelmoinnin perusteita. Olemme käyttäneet esimerkkejä JavaScriptistä opiskellaksemme viittä keskeistä toiminnallista ohjelmointitekniikkaa, joita tutkimme tarkemmin Java-koodin avulla osassa 2. Tämän opetusohjelman toinen puoli auttaa tutustumaan Java 8: n toiminnallisiin ohjelmointikykyihin. ajatella toiminnallisestimuuntamalla esimerkki olio-Java-koodista sen toiminnalliseksi vastaavaksi.

Lisätietoja toiminnallisesta ohjelmoinnista

Löysin kirjan Johdatus toiminnalliseen ohjelmointiin (Richard Bird ja Philip Wadler, Prentice Hall International Series in Computing Science, 1992) hyödyllisenä toiminnallisen ohjelmoinnin perusteiden oppimisessa.

Tämän tarinan "Funktionaalinen ohjelmointi Java-kehittäjille, osa 1" julkaisi alun perin JavaWorld.

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