Programovanie

Funkčné programovanie pre vývojárov Java, 1. časť

Java 8 predstavila vývojárov Java funkčné programovanie pomocou výrazov lambda. Toto vydanie Java efektívne upozornilo vývojárov, že už nestačí uvažovať o programovaní v Jave iba z imperatívneho a objektovo orientovaného hľadiska. Vývojár v prostredí Java musí byť tiež schopný myslieť a kódovať pomocou deklaratívnej funkčnej paradigmy.

Tento tutoriál predstavuje základy funkčného programovania. Začnem terminológiou, potom sa budeme venovať koncepciám funkčného programovania. Na záver vám predstavím päť techník funkčného programovania. Príklady kódu v týchto častiach vám pomôžu začať s čistými funkciami, funkciami vyššieho rádu, lenivým hodnotením, uzávierkami a kari.

Funkčné programovanie je na vzostupe

Inštitút elektrotechnických a elektronických inžinierov (IEEE) zahrnul funkčné programovacie jazyky do svojich 25 najlepších programovacích jazykov pre rok 2018 a Google Trends v súčasnosti hodnotí funkčné programovanie ako populárnejšie ako objektovo orientované programovanie.

Je zrejmé, že funkčné programovanie nemožno ignorovať, ale prečo je čoraz populárnejšie? Funkčné programovanie okrem iného uľahčuje overenie správnosti programu. Tiež to zjednodušuje vytváranie súbežných programov. Súbežnosť (alebo paralelné spracovanie) je nevyhnutná pre zlepšenie výkonu aplikácie.

stiahnuť Získajte kód Stiahnite si zdrojový kód napríklad pre aplikácie v tejto príručke. Vytvoril Jeff Friesen pre JavaWorld.

Čo je funkčné programovanie?

Počítače zvyčajne implementujú architektúru Von Neumann, čo je široko používaná počítačová architektúra založená na opise matematika a fyzika Johna von Neumanna (a ďalších) z roku 1945. Táto architektúra je zaujatá smerom k imperatívne programovanie, čo je paradigma programovania, ktorá pomocou príkazov mení stav programu. C, C ++ a Java sú nevyhnutné programovacie jazyky.

V roku 1977 predniesol významný počítačový vedec John Backus (vynikajúci pre prácu na FORTRANE) prednášku s názvom „Môže byť programovanie oslobodené od von Neumannovho štýlu?“. Backus tvrdil, že architektúra Von Neumanna a s ňou spojené imperatívne jazyky sú zásadne chybné, a ako riešenie predstavil programovací jazyk na funkčnej úrovni (FP).

Objasnenie Backusa

Pretože prednáška Backus bola predstavená pred niekoľkými desaťročiami, môže byť ťažké pochopiť niektoré z jej myšlienok. Blogger Tomasz Jaskuła dodáva vo svojom blogovom príspevku od januára 2018 jasnosť a poznámky pod čiarou.

Koncepty a terminológia funkčného programovania

Funkčné programovanie je programovací štýl, v ktorom sú výpočty kodifikované ako funkčné programovacie funkcie. Jedná sa o matematické funkčné konštrukty (napr. Funkcie lambda), ktoré sa hodnotia v kontextoch výrazu.

Funkčné programovacie jazyky sú deklaratívny, čo znamená, že logika výpočtu je vyjadrená bez popisu jeho riadiaceho toku. V deklaratívnom programovaní neexistujú žiadne vyhlásenia. Namiesto toho programátori pomocou výrazov informujú počítač o tom, čo treba urobiť, ale nie o tom, ako úlohu splniť. Ak ovládate SQL alebo regulárne výrazy, máte nejaké skúsenosti s deklaratívnym štýlom; obaja používajú výrazy na opísanie toho, čo je potrebné urobiť, a nie na vyjadrenie toho, ako to urobiť.

A výpočet vo funkčnom programovaní je popísaný funkciami, ktoré sú hodnotené v kontextoch výrazu. Tieto funkcie nie sú rovnaké ako funkcie používané pri imperatívnom programovaní, napríklad metóda Java, ktorá vracia hodnotu. Namiesto toho a funkčné programovanie Funkcia je ako matematická funkcia, ktorá vytvára výstup, ktorý zvyčajne závisí iba od jej argumentov. Zakaždým, keď sa funkčná programovacia funkcia zavolá s rovnakými argumentmi, dosiahne sa rovnaký výsledok. Hovorí sa, že sú k dispozícii funkcie funkčného programovania referenčná transparentnosť. To znamená, že môžete nahradiť volanie funkcie výslednou hodnotou bez zmeny významu výpočtu.

Uprednostňovanie funkčného programovania nemennosť, čo znamená, že štát sa nemôže meniť. To zvyčajne neplatí v prípade imperatívneho programovania, kde môže byť imperatívna funkcia spojená so stavom (napríklad premenná inštancie jazyka Java). Volanie tejto funkcie v rôznych časoch s rovnakými argumentmi môže mať za následok rôzne návratové hodnoty, pretože v tomto prípade je stav premenlivé, čo znamená, že sa mení.

Vedľajšie účinky pri nevyhnutnom a funkčnom programovaní

Zmeny stavu sú vedľajším účinkom nevyhnutného programovania, ktoré bráni referenčnej transparentnosti. Existuje mnoho ďalších vedľajších účinkov, ktoré stojí za to vedieť, najmä keď hodnotíte, či sa vo vašich programoch používa imperatívny alebo funkčný štýl.

Jedným z bežných vedľajších účinkov pri imperatívnom programovaní je, keď príkaz na priradenie mutuje premennú zmenou jej uloženej hodnoty. Funkcie funkčného programovania nepodporujú priradenie premenných. Pretože sa počiatočná hodnota premennej nikdy nezmení, funkčné programovanie tento vedľajší efekt eliminuje.

Ďalším častým vedľajším účinkom je zmena správania funkcie imperatívnej funkcie na základe vyvolanej výnimky, ktorou je pozorovateľná interakcia s volajúcim. Ďalšie informácie nájdete v diskusii o pretečení zásobníka: „Prečo je vyvolanie výnimky vedľajším účinkom?“

Tretí častý vedľajší účinok nastáva, keď vstupno-výstupná operácia zadá text, ktorý sa nedá prečítať, alebo vydá text, ktorý sa nedá napísať. Pozrite si diskusiu o Stack Exchange „Ako môžu IO spôsobiť vedľajšie účinky vo funkčnom programovaní?“ Ak sa chcete dozvedieť viac o tomto vedľajšom účinku, obráťte sa na svojho lekára.

Eliminácia vedľajších účinkov výrazne uľahčuje pochopenie a predvídanie výpočtového správania. Pomáha tiež zvýšiť vhodnosť kódu na paralelné spracovanie, čo často zvyšuje výkon aplikácie. Aj keď vo funkčnom programovaní existujú vedľajšie účinky, vo všeobecnosti ich je menej ako v nevyhnutnom prípade. Používanie funkčného programovania vám môže pomôcť pri písaní kódu, ktorý je ľahšie pochopiteľný, udržiavateľný a testovateľný a ktorý je tiež opakovane použiteľný.

Počiatky (a pôvodcovia) funkčného programovania

Funkčné programovanie vzniklo z počtu lambda, ktorý zaviedol Alonzo Church. Ďalším pôvodom je kombinačná logika, ktorú predstavil Moses Schönfinkel a následne ju vyvinul Haskell Curry.

Objektovo orientované verzus funkčné programovanie

Vytvoril som aplikáciu Java, ktorá kontrastuje s imperatívne, objektovo orientované a deklaratívny, funkčný programovacie prístupy k písaniu kódu. Preštudujte si kód uvedený nižšie a potom poukážem na rozdiely medzi týmito dvoma príkladmi.

Zoznam 1. Zamestnanci.java

import java.util.ArrayList; import java.util.List; verejná trieda Zamestnanci {statická trieda Zamestnanec {súkromný názov reťazca; súkromný int vek; Zamestnanec (meno reťazca, vek) {this.name = meno; this.age = vek; } int getAge () {návratový vek; } @Override public String toString () {return name + ":" + age; }} public static void main (String [] args) {Zoznam zamestnancov = nový ArrayList (); zamestnanci.add (novy zamestnanec ("John Doe", 63)); zamestnanci.add (novy zamestnanec ("Sally Smith", 29)); zamestnanci.add (novy zamestnanec ("Bob Jone", 36)); zamestnanci.add (nova zamestnanec ("Margaret Foster", 53)); printEmployee1 (zamestnanci, 50); System.out.println (); printEmployee2 (zamestnanci, 50); } public static void printEmployee1 (Zoznam zamestnancov, vek int) {for (Zamestnanec emp: zamestnanci) if (emp.getAge () <age) System.out.println (emp); } public static void printEmployee2 (zoznam zamestnancov, vek) {zamestnanci.stream () .filter (emp -> emp.age System.out.println (emp)); }}

Zoznam 1 odhaľuje Zamestnanci aplikácia, ktorá ich vytvorí niekoľko Zamestnanec objects, potom vytlačí zoznam všetkých zamestnancov, ktorí majú menej ako 50 rokov. Tento kód demonštruje štýly objektového aj funkčného programovania.

The printEmployee1 () Táto metóda odhaľuje nevyhnutný prístup zameraný na vyhlásenie. Ako je uvedené, táto metóda iteruje so zoznamom zamestnancov, porovnáva vek každého zamestnanca s hodnotou argumentu a (ak je vek nižší ako argument), vytlačí podrobnosti zamestnanca.

The printEmployee2 () metóda odhaľuje deklaratívny, na výraz zameraný prístup, v tomto prípade implementovaný pomocou Streams API. Namiesto toho, aby ste museli nevyhnutne špecifikovať, ako tlačiť zamestnancov (krok za krokom), výraz špecifikuje požadovaný výsledok a podrobnosti o tom, ako to urobiť, ponechá na Javu. Rozmýšľať o filter () ako funkčný ekvivalent ak vyhlásenie a pre každý() ako funkčne ekvivalentný s pre vyhlásenie.

Zoznam 1 môžete zostaviť nasledovne:

javac Zamestnanci.java

Na spustenie výslednej aplikácie použite nasledujúci príkaz:

Zamestnanci Java

Výstup by mal vyzerať asi takto:

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

Príklady funkčného programovania

V ďalších častiach preskúmame päť základných techník používaných vo funkčnom programovaní: čisté funkcie, funkcie vyššieho rádu, lenivé hodnotenie, uzávery a kari. Príklady v tejto časti sú kódované v JavaScripte, pretože jeho jednoduchosť vo vzťahu k Jave nám umožní sústrediť sa na techniky. V časti 2 sa vrátime k rovnakým technikám pomocou kódu Java.

Výpis 2 predstavuje zdrojový kód RunScript, aplikácia Java, ktorá na uľahčenie spustenia kódu JavaScript používa skriptovacie rozhranie API Java. RunScript bude základným programom pre všetky nasledujúce príklady.

Zoznam 2. RunScript.java

import java.io.FileReader; import java.io.IOException; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; importovať statický java.lang.System. *; public class RunScript {public static void main (String [] args) {if (args.length! = 1) {err.println ("use: java RunScript script"); návrat; } Správca ScriptEngineManager = nový ScriptEngineManager (); ScriptEngine engine = manager.getEngineByName ("nashorn"); try {engine.eval (new FileReader (args [0])); } catch (ScriptException se) {err.println (se.getMessage ()); } catch (IOException ioe) {err.println (ioe.getMessage ()); }}}

The hlavný() metóda v tomto príklade najskôr overí, či bol zadaný jeden argument príkazového riadku (názov súboru skriptu). V opačnom prípade zobrazí informácie o použití a ukončí aplikáciu.

Za predpokladu, že bude existovať tento argument, hlavný() inštancuje javax.script.ScriptEngineManager trieda. ScriptEngineManager je vstupný bod do Java Scripting API.

Ďalej ScriptEngineManager objektu ScriptEngine getEngineByName (reťazec shortName) metóda sa volá na získanie skriptovacieho stroja zodpovedajúceho požadovanému shortName hodnotu. Java 10 podporuje skriptovací stroj Nashorn, ktorý sa získa prechodom "nashorn" do getEngineByName (). Trieda vráteného objektu implementuje javax.script.ScriptEngine rozhranie.

ScriptEngine vyhlasuje niekoľko eval () metódy hodnotenia skriptu. hlavný() sa odvoláva na Object eval (čítačka Reader) metóda na čítanie skriptu z jeho java.io.FileReader objektový argument a (za predpokladu, že java.io.IOException nie je vyhodený) potom vyhodnotiť skript. Táto metóda vráti akúkoľvek návratovú hodnotu skriptu, ktorú ignorujem. Aj táto metóda hodí javax.script.ScriptException keď sa v skripte vyskytne chyba.

Zostavte zoznam 2 nasledovne:

javac RunScript.java

Po predstavení prvého skriptu vám ukážem, ako spustiť túto aplikáciu.

Funkčné programovanie s čistými funkciami

A čistá funkcia je funkčná programovacia funkcia, ktorá závisí iba od jej vstupných argumentov a od žiadneho externého stavu. An nečistá funkcia je funkčná programovacia funkcia, ktorá porušuje niektorú z týchto požiadaviek. Pretože čisté funkcie nemajú žiadnu interakciu s vonkajším svetom (okrem volania iných čistých funkcií), čistá funkcia vždy vráti rovnaký výsledok pre rovnaké argumenty. Čisté funkcie tiež nemajú žiadne pozorovateľné vedľajšie účinky.

Môže čistá funkcia vykonávať I / O?

Ak je I / O vedľajší efekt, môže čistá funkcia vykonávať I / O? Odpoveď je áno. Haskell tento problém rieši pomocou monád. Viac informácií o čistých funkciách a I / O nájdete v časti „Čisté funkcie a I / O“.

Čisté funkcie verzus nečisté funkcie

JavaScript v zozname 3 kontrastuje s nečistým vypočítaťbonus () funkcia s čistým vypočítaťbonus2 () funkcia.

Zoznam 3. Porovnanie čistých a nečistých funkcií (script1.js)

// výpočet nečistého bonusu var limit = 100; funkcia countbonus (numSales) {return (numSales> limit)? 0,10 * numSales: 0} print (calcbonus (174)) // funkcia čistého bonusu CalculateBus2 (numSales) {return (numSales> 100)? 0,10 * numSales: 0} tlačiť (vypočítaťbonus2 (174))

vypočítaťbonus () je nečistý, pretože má prístup k externému zdroju limit premenná. Naproti tomu vypočítaťbonus2 () je čistý, pretože spĺňa obe požiadavky na čistotu. Bež script1.js nasledovne:

java RunScript script1.js

Tu by ste mali dodržiavať výstup:

17.400000000000002 17.400000000000002

Predpokladajme vypočítaťbonus2 () bol refaktorovaný na návrat vypočítaťbonus (numSales). By vypočítaťbonus2 () byť stále čistý? Odpoveď znie nie: keď čistá funkcia vyvolá nečistú funkciu, „čistá funkcia“ sa stane nečistou.

Ak medzi čistými funkciami neexistuje žiadna závislosť od údajov, môžu sa vyhodnotiť v akomkoľvek poradí bez ovplyvnenia výsledku, čo ich robí vhodnými na paralelné vykonávanie. Toto je jedna z výhod funkčného programovania.

Viac o nečistých funkciách

Nie všetky funkčné programovacie funkcie musia byť čisté. Ako vysvetľuje Functional Programming: Pure Functions, je možné (a niekedy aj želateľné) „oddeliť čisté, funkčné a na hodnote založené jadro vašej aplikácie od vonkajšieho, nevyhnutného shellu.“

Funkčné programovanie s funkciami vyššieho rádu

A funkcia vyššieho rádu je matematická funkcia, ktorá prijíma funkcie ako argumenty, vracia funkciu volajúcemu alebo obidve funkcie. Jedným príkladom je diferenciálny operátor počtu, d / dx, ktorá vráti deriváciu funkcie f.

Prvotriedne funkcie sú občanmi prvej triedy

S matematickým konceptom funkcií vyššieho rádu úzko súvisí prvotriedna funkcia, čo je funkcia funkčného programovania, ktorá berie ďalšie funkcie funkčného programovania ako argumenty a / alebo vracia funkciu funkčného programovania. Prvotriedne funkcie sú občania prvej triedy pretože sa môžu objaviť všade, kde môžu byť iné prvotriedne programové entity (napr. čísla), vrátane toho, že sú priradené k premennej alebo sú odovzdané ako argument alebo vrátené z funkcie.

JavaScript v zozname 4 demonštruje odovzdávanie anonymných porovnávacích funkcií prvotriednej funkcii triedenia.

Zoznam 4. Predávanie anonymných porovnávacích funkcií (script2.js)

function sort (a, cmp) {for (var pass = 0; pass  prejsť; i--) if (cmp (a [i], a [pass]) <0) {var temp = a [i] a [i] = a [pass] a [pass] = temp}} var a = [ 22, 91, 3, 45, 64, 67, -1] sort (a, function (i, j) {return i - j;}) a.forEach (function (entry) {print (entry)}) print ( '\ n') sort (a, function (i, j) {return j - i;}) a.forEach (function (entry) {print (entry)}) print ('\ n') a = ["X "," E "," Q "," A "," P "] zoradiť (a, funkcia (i, j) {návrat i  j; }) a.forEach (function (entry) {print (entry)}) print ('\ n') sort (a, function (i, j) {return i> j? -1: i <j;}) a .forEach (function (entry) {print (entry)})

V tomto príklade počiatočné sort () volanie prijme pole ako prvý argument, za ktorým nasleduje funkcia anonymného porovnania. Po zavolaní sa vykoná funkcia anonymného porovnania návrat i - j; dosiahnuť vzostupné triedenie. Cúvaním i a j, druhá porovnávacia funkcia dosahuje zostupné triedenie. Tretí a štvrtý sort () hovory prijímajú anonymné porovnávacie funkcie, ktoré sa mierne líšia, aby bolo možné správne porovnať hodnoty reťazcov.

Spustiť script2.js príklad takto:

java RunScript script2.js

Tu je očakávaný výstup:

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

Filtrujte a mapujte

Funkčné programovacie jazyky zvyčajne poskytujú niekoľko užitočných funkcií vyššieho rádu. Dva bežné príklady sú filter a mapa.

  • A filter spracuje zoznam v určitom poradí, aby vytvoril nový zoznam obsahujúci presne tie prvky pôvodného zoznamu, pre ktoré daný predikát (myslím Boolovský výraz) vráti hodnotu true.
  • A mapa použije danú funkciu na každý prvok zoznamu a vráti zoznam výsledkov v rovnakom poradí.

JavaScript podporuje filtrovanie a mapovanie pomocou filter () a mapa () funkcie vyššieho rádu. Zoznam 5 demonštruje tieto funkcie na filtrovanie nepárnych čísel a mapovanie čísel na ich kocky.

Zoznam 5. Filtrovanie a mapovanie (script3.js)

print ([1, 2, 3, 4, 5, 6] .filter (function (num) {return num% 2 == 0})) print ('\ n') print ([3, 13, 22]. mapa (funkcia (num) {return num * 3}))

Spustiť script3.js príklad takto:

java RunScript script3.js

Mali by ste dodržiavať nasledujúci výstup:

2,4,6 9,39,66

Znížiť

Ďalšou bežnou funkciou vyššieho rádu je zmenšiť, ktorý je viac obyčajne známy ako záhyb. Táto funkcia redukuje zoznam na jednu hodnotu.

Výpis 6 používa JavaScript znížiť () funkcia vyššieho rádu na zmenšenie poľa čísel na jedno číslo, ktoré sa potom vydelí dĺžkou poľa a získa sa priemer.

Zoznam 6. Zníženie počtu čísel na jedno číslo (script4.js)

var numbers = [22, 30, 43] print (numbers.reduce (function (acc, curval) {return acc + curval}) / numbers.length)

Spustiť skript výpisu 6 (v script4.js) nasledovne:

java RunScript script4.js

Mali by ste dodržiavať nasledujúci výstup:

31.666666666666668

Možno si myslíte, že funkcie filtra, mapy a redukcie vyššieho rádu eliminujú potrebu príkazov if-else a rôznych opakovaní a mali by ste pravdu. Ich interné implementácie sa starajú o rozhodnutia a iteráciu.

Funkcia vyššieho rádu používa na dosiahnutie iterácie rekurziu. Rekurzívna funkcia sa sama vyvolá a umožňuje operáciu opakovať, kým nedosiahne a základný prípad. Môžete tiež využiť rekurziu na dosiahnutie iterácie vo vašom funkčnom kóde.

Funkčné programovanie s lenivým hodnotením

Ďalšou dôležitou funkčnou funkciou programovania je lenivé hodnotenie (taktiež známy ako nonstrict hodnotenie), čo je odklad vyhodnotenia výrazu na čo najdlhší čas. Lenivé hodnotenie ponúka niekoľko výhod, vrátane týchto dvoch:

  • Drahé (časovo) výpočty je možné odložiť, kým to nie je nevyhnutne potrebné.
  • Možné sú neobmedzené zbierky. Budú dodávať prvky tak dlho, ako to bude vyžadované.

Lenivé hodnotenie je neoddeliteľnou súčasťou Haskella. Nevypočíta nič (vrátane argumentov funkcie pred volaním funkcie), pokiaľ to nie je nevyhnutne potrebné.

Rozhranie Java Streams API zúročuje lenivé hodnotenie. Stredné operácie streamu (napr. filter ()) sú vždy leniví; neurobia nič, kým nevykoná operáciu terminálu (napr. pre každý()) sa vykoná.

Aj keď je lenivé hodnotenie dôležitou súčasťou funkčných jazykov, dokonca aj mnoho nevyhnutných jazykov poskytuje zabudovanú podporu pre niektoré formy lenivosti. Napríklad väčšina programovacích jazykov podporuje vyhodnotenie skratov v kontexte logických operátorov AND a OR. Títo operátori sú leniví a odmietajú hodnotiť svoje pravé operandy, keď je ľavý operand nepravdivý (AND) alebo true (OR).

Výpis 7 je príkladom lenivého hodnotenia v skripte JavaScript.

Zoznam 7. Lenivé vyhodnotenie v JavaScripte (script5.js)

var a = false && expensiveFunction ("1") var b = true && expensiveFunction ("2") var c = false || expensiveFunction ("3") var d = true || drahá funkcia ("4") funkcia drahá funkcia (id) {print ("drahá funkcia () volaná s" + id)}

Spustite kód v script5.js nasledovne:

java RunScript script5.js

Mali by ste dodržiavať nasledujúci výstup:

drahá funkcia () volaná s 2 drahá funkcia () volaná s 3

Lenivé vyhodnotenie sa často kombinuje s memoizáciou, optimalizačnou technikou používanou predovšetkým na urýchlenie počítačových programov ukladaním výsledkov drahých volaní funkcií a vrátením výsledku z medzipamäte, keď sa znova vyskytnú rovnaké vstupy.

Pretože lenivé hodnotenie nefunguje s vedľajšími účinkami (napríklad s kódom, ktorý vytvára výnimky a I / O), používajú sa hlavne imperatívne jazyky nedočkavé hodnotenie (taktiež známy ako prísne hodnotenie), kde je výraz vyhodnotený, akonáhle je viazaný na premennú.

Viac o lenivom hodnotení a memoizácii

Vyhľadávanie Google odhalí veľa užitočných diskusií o lenivom hodnotení s memoizáciou alebo bez nej. Jedným z príkladov je „Optimalizácia kódu JavaScript pomocou funkčného programovania“.

Funkčné programovanie s uzávierkami

Prvotriedne funkcie sú spojené s konceptom a uzáver, čo je trvalý rozsah, ktorý sa drží lokálnych premenných aj potom, čo vykonávanie kódu opustilo blok, v ktorom boli lokálne premenné definované.

Remeselné uzávery

Prevádzkovo, a uzáver je záznam, ktorý ukladá funkciu a jej prostredie. Prostredie mapuje každú z voľných premenných funkcie (premenné používané lokálne, ale definované v ohraničujúcom rozsahu) s hodnotou alebo odkazom, na ktorý bol názov premennej viazaný pri vytváraní uzávierky. Umožňuje funkcii prístup k týmto zachyteným premenným prostredníctvom kópií ich hodnôt alebo odkazov uzávierky, aj keď je funkcia vyvolaná mimo ich rozsahu.

Na objasnenie tohto konceptu predstavuje zoznam 8 skript jazyka JavaScript, ktorý zavádza jednoduché uzavretie. Skript je založený na príklade, ktorý je tu uvedený.

Výpis 8. Jednoduché uzavretie (script6.js)

funkcia add (x) {function partialAdd (y) {return y + x} return partialAdd} var add10 = add (10) var add20 = add (20) print (add10 (5)) print (add20 (5))

Výpis 8 definuje prvotriednu funkciu s názvom pridať () s parametrom X a vnorená funkcia partialAdd (). Vnorená funkcia partialAdd () má prístup k X pretože X je v pridať ()lexikálny rozsah. Funkcia pridať () vráti uzávierku, ktorá obsahuje odkaz na partialAdd () a kópiu okolitého prostredia pridať (), v ktorom X má priradenú hodnotu pri konkrétnom vyvolaní pridať ().

Pretože pridať () vráti hodnotu typu funkcie, premenné pridať10 a pridať20 tiež majú typ funkcie. The add10 (5) priznania k vráteniu 15 pretože vzývanie priraďuje 5 k parametru r vo výzve na partialAdd (), pomocou uloženého prostredia pre partialAdd () kde X je 10. The add20 (5) priznania k vráteniu 25 pretože, aj keď tiež priraďuje 5 do r vo výzve na partialAdd (), teraz používa iné uložené prostredie pre partialAdd () kde X je 20. Teda zatiaľ čo add10 () a add20 () používať tú istú funkciu partialAdd (), príslušné prostredia sa líšia a vyvolanie uzáverov bude záväzné X na dve rôzne hodnoty v dvoch vyvolaní, čím sa funkcia vyhodnotí na dva rôzne výsledky.

Spustiť skript výpisu 8 (v script6.js) nasledovne:

java RunScript script6.js

Mali by ste dodržiavať nasledujúci výstup:

15 25

Funkčné programovanie s kari

Kari je spôsob, ako previesť hodnotenie funkcie s viacerými argumentmi na hodnotenie ekvivalentnej postupnosti funkcií s jedným argumentom. Napríklad funkcia má dva argumenty: X a r. Currying premieňa funkciu iba na branie X a vrátenie funkcie, ktorá berie iba r. Currying súvisí, ale nie je to isté ako čiastočné použitie, čo je proces fixovania množstva argumentov na funkciu, čím vzniká ďalšia funkcia s menšou aritídou.

Výpis 9 predstavuje skript JavaScript, ktorý demonštruje kari.

Zoznam 9. Currying v JavaScripte (script7.js)

funkcia multiplikácia (x, y) {návrat x * y} funkcia curried_multiply (x) {funkcia návratu (y) {návrat x * y}} print (násobenie (6, 7)) print (curried_multiply (6) (7)) var mul_by_4 = curried_multiply (4) tlač (mul_by_4 (2))

Skript predstavuje nevyslovený dvoj argument znásobiť () funkcie, za ktorou nasleduje prvotriedna curried_multiply () funkcia, ktorá prijíma multiplikačný argument X a vráti uzávierku obsahujúcu odkaz na anonymnú funkciu (ktorá prijíma argument multiplikátora r) a kópiu okolitého prostredia curried_multiply (), v ktorom X má priradenú hodnotu pri vyvolaní curried_multiply ().

Zvyšok scenára sa najskôr vyvolá znásobiť () s dvoma argumentmi a vytlačí výsledok. Potom sa vyvolá curried_multiply () dvoma spôsobmi:

  • curried_multiply (6) (7) výsledky v curried_multiply (6) vykonávajúci prvý. Vrátená uzávierka vykoná anonymnú funkciu s uloženou uzávierkou X hodnotu 6 vynásobený 7.
  • var mul_by_4 = curried_multiply (4) vykonáva curried_multiply (4) a priraďuje uzávierku k mul_by_4. mul_by_4 (2) vykonáva anonymnú funkciu uzávierkou 4 hodnota a odovzdaný argument 2.

Spustiť skript záznamu 9 (v script7.js) nasledovne:

java RunScript script7.js

Mali by ste dodržiavať nasledujúci výstup:

42 42 8

Prečo používať kari?

Hugh Jackson vo svojom blogu „Prečo pomáha kari“ poznamenáva, že „malé kúsky je možné ľahko nakonfigurovať a znova použiť bez zbytočných neporiadkov.“ Quora „Aké sú výhody kari vo funkčnom programovaní?“ popisuje kari ako „lacnú formu vkladania závislostí“, ktorá uľahčuje proces mapovania / filtrovania / skladania (a všeobecne funkcií vyššieho rádu). Toto Otázky a odpovede tiež poznamenáva, že kari program „nám pomáha vytvárať abstraktné funkcie“.

Na záver

V tomto tutoriáli ste sa naučili niektoré základy funkčného programovania. Použili sme príklady v JavaScripte na štúdium piatich základných techník funkčného programovania, ktoré budeme ďalej skúmať pomocou kódu Java v časti 2. Okrem prehliadky funkcií funkčného programovania v Java 8 vám druhá polovica tohto tutoriálu pomôže začať myslieť funkčneprevedením príkladu objektovo orientovaného kódu Java na jeho funkčný ekvivalent.

Získajte viac informácií o funkčnom programovaní

Kniha Úvod do funkčného programovania (Richard Bird a Philip Wadler, Prentice Hall International Series in Computing Science, 1992) mi pomohla pri osvojovaní základov funkčného programovania.

Tento príbeh, „Funkčné programovanie pre vývojárov Java, 1. časť“, bol pôvodne publikovaný spoločnosťou JavaWorld.

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