Toiminnallinen ohjelmointi on paradigma tietokoneohjelmien rakentamisesta lausekkeita ja toimintoja käyttämättä tilaa ja tietoja mutaatiota.
Noudattamalla näitä rajoituksia, toiminnallinen ohjelmointi pyrkii kirjoittamaan koodin, joka on ymmärrettävämpi ja virhekestävämpi. Tämä saavutetaan välttämällä virtauksenohjauslausekkeiden (for
, while
, break
, continue
, goto
) käyttöä, jotka vaikeuttavat koodin seuraamista. Myös toiminnallinen ohjelmointi vaatii meitä kirjoittamaan puhtaita, deterministisiä toimintoja, jotka ovat vähemmän todennäköisesti viallisia.
Tässä artikkelissa puhumme toiminnallisen ohjelmoinnin suorittamisesta JavaScriptin avulla. Tutkimme myös erilaisia JavaScript-menetelmiä ja ominaisuuksia, jotka mahdollistavat sen. Loppujen lopuksi tutkitaan erilaisia toiminnalliseen ohjelmointiin liittyviä käsitteitä ja selvitetään, miksi ne ovat niin tehokkaita.
Ennen kuin aloitat toiminnallisen ohjelmoinnin, on kuitenkin ymmärrettävä ero puhtaiden ja epäpuhtaiden toimintojen välillä.
Puhtaat toiminnot vievät jonkin verran tuloa ja antavat kiinteän lähdön. Ne eivät myöskään aiheuta sivuvaikutuksia ulkomaailmassa.
const add = (a, b) => a + b;
Täällä, add
on puhdas toiminto. Tämä johtuu siitä, että kiinteälle arvolle a
ja b
, tulos on aina sama.
const SECRET = 42; const getId = (a) => SECRET * a;
getId
ei ole puhdas toiminto. Syynä on, että se käyttää globaalia muuttujaa SECRET
tuotoksen laskemiseksi. Jos SECRET
piti muuttua, getId
toiminto palauttaa eri arvon samalle tulolle. Siksi se ei ole puhdas toiminto.
let id_count = 0; const getId = () => ++id_count;
Tämä on myös epäpuhdas toiminto, ja myös se muutamasta syystä - (1) se käyttää ei-paikallista muuttujaa tuotoksensa laskemiseen ja (2) se luo sivuvaikutuksen ulkomaailmaan muuttamalla muuttujaa siinä maailman.
Tämä voi olla hankalaa, jos meidän on korjattava tämä koodi.
Mikä on id_count
-arvon nykyinen arvo? Mitkä muut toiminnot muokkaavat id_count
? Onko muita toimintoja, joihin luotetaan id_count
?
Näistä syistä käytämme vain puhtaita toimintoja toiminnallisessa ohjelmoinnissa.
Puhtaiden toimintojen toinen etu on, että ne voidaan rinnastaa ja muistuttaa. Tutustu kahteen edelliseen toimintoon. Niitä on mahdotonta rinnastaa tai muistaa. Tämä auttaa esittäjän koodin luomisessa.
Toistaiseksi olemme oppineet, että toiminnallinen ohjelmointi riippuu muutamasta säännöstä. Ne ovat seuraavat.
Kun täytämme nämä ehdot, voimme sanoa, että koodi on toimiva.
JavaScriptillä on jo joitain toimintoja, jotka mahdollistavat toiminnallisen ohjelmoinnin. Esimerkki: Merkkijono.prototyyppi.leike , Array.protoype.filter , Array.prototype.liity .
Toisaalta, Array.prototype.for jokaiselle , Array.prototype.push ovat epäpuhtaita toimintoja.
Voidaan väittää, että Array.prototype.forEach
ei ole suunnittelun mukaan epäpuhdas toiminto, mutta ajattele sitä - sen kanssa ei ole mahdollista tehdä mitään muuta kuin muuntamalla ei-paikallista dataa tai tekemällä sivuvaikutuksia. Siksi on hyvä laittaa se epäpuhtaiden toimintojen luokkaan.
Lisäksi JavaScriptillä on vakio ilmoitus, joka on täydellinen toiminnalliseen ohjelmointiin, koska emme mutatoi mitään tietoja.
Tarkastellaan joitain JavaScriptin puhtaita toimintoja (menetelmiä).
Kuten nimestä voi päätellä, tämä suodattaa matriisin.
array.filter(condition);
Ehto on funktio, joka saa matriisin kukin alkio, ja sen tulisi päättää, pidetäänkö kohde vai ei, ja palauta tosi totuusarvo.
const filterEven = x => x%2 === 0; [1, 2, 3].filter(filterEven); // [2]
Huomaa, että filterEven
on puhdas toiminto. Jos se olisi ollut epäpuhdas, se olisi tehnyt koko suodatinkutsun epäpuhtaaksi.
map
kartoittaa kaikki matriisin kohteet funktioon ja luo uuden taulukon funktiokutsujen paluuarvojen perusteella.
array.map(mapper)
mapper
on funktio, joka ottaa matriisin kohteen syötteeksi ja palauttaa lähdön.
const double = x => 2 * x; [1, 2, 3].map(double); // [2, 4, 6]
reduce
vähentää matriisin yhteen arvoon.
array.reduce(reducer);
reducer
on funktio, joka ottaa kertyneen arvon ja taulukon seuraavan kohteen ja palauttaa uuden arvon. Sitä kutsutaan näin kaikille matriisin arvoille yksi toisensa jälkeen.
const sum = (accumulatedSum, arrayItem) => accumulatedSum + arrayItem [1, 2, 3].reduce(sum); // 6
concat
lisää uusia kohteita olemassa olevaan taulukoon uuden taulukon luomiseksi. Se eroaa push()
: sta siinä mielessä, että push()
mutatoi tietoja, mikä tekee niistä epäpuhtaita.
[1, 2].concat([3, 4]) // [1, 2, 3, 4]
Voit tehdä saman myös käyttämällä levitän operaattori.
[1, 2, ...[3, 4]]
Object.assign
kopioi arvot toimitetusta objektista uuteen objektiin. Koska toiminnallinen ohjelmointi perustuu muuttumattomiin tietoihin, käytämme sitä uusien objektien tekemiseen olemassa olevien objektien perusteella.
const obj = {a : 2}; const newObj = Object.assign({}, obj); newObj.a = 3; obj.a; // 2
Kynnyksellä ES6 , tämä voidaan tehdä myös levitysoperaattorilla.
const newObj = {...obj};
Voimme luoda myös puhtaan toiminnon. Tehdään yksi merkkijonon kopioimiseksi n
useita kertoja.
const duplicate = (str, n) => n <1 ? '' : str + duplicate(str, n-1);
Tämä toiminto kopioi merkkijonon n
kertaa ja palauttaa uuden merkkijonon.
duplicate('hooray!', 3) // hooray!hooray!hooray!
Korkeamman asteen funktiot ovat toimintoja, jotka hyväksyvät funktion argumenttina ja palauttavat funktion. Usein niitä käytetään lisäämään funktion toiminnallisuutta.
const withLog = (fn) => { return (...args) => { console.log(`calling ${fn.name}`); return fn(...args); }; };
Yllä olevassa esimerkissä luomme withLog
korkeamman asteen funktio, joka ottaa toiminnon ja palauttaa toiminnon, joka kirjaa viestin ennen käärityn toiminnon suorittamista.
const add = (a, b) => a + b; const addWithLogging = withLog(add); addWithLogging(3, 4); // calling add // 7
withLog
HOF: ää voidaan käyttää myös muiden toimintojen kanssa ja se toimii ilman ristiriitoja tai ylimääräisen koodin kirjoittamista. Tämä on HOF: n kauneus.
const addWithLogging = withLog(add); const hype = s => s + '!!!'; const hypeWithLogging = withLog(hype); hypeWithLogging('Sale'); // calling hype // Sale!!!
Sitä voidaan kutsua myös määrittelemättä yhdistämistoimintoa.
withLog(hype)('Sale'); // calling hype // Sale!!!
Curry-toiminnolla tarkoitetaan funktion hajottamista, joka vie useita argumentteja yhdeksi tai useammaksi korkeamman tason funktioiden tasoksi.
Otetaan add
toiminto.
const add = (a, b) => a + b;
Kun pidämme sitä curryna, kirjoitamme sen uudelleen jakamalla argumentit useille tasoille seuraavasti.
const add = a => { return b => { return a + b; }; }; add(3)(4); // 7
Curryn etu on muistiinpano. Voimme nyt muistaa tietyt argumentit funktiokutsussa, jotta niitä voidaan käyttää myöhemmin uudelleen ilman päällekkäisyyksiä ja uudelleenlaskentaa.
// assume getOffsetNumer() call is expensive const addOffset = add(getOffsetNumber()); addOffset(4); // 4 + getOffsetNumber() addOffset(6);
Tämä on varmasti parempi kuin käyttää molempia argumentteja kaikkialla.
// (X) DON'T DO THIS add(4, getOffsetNumber()); add(6, getOffsetNumber()); add(10, getOffsetNumber());
Voimme myös muotoilla currytoimintomme näyttämään ytimekkäältä. Tämä johtuu siitä, että curry-funktion kutsun kukin taso on yhden rivin paluulauseke. Siksi voimme käyttää nuolitoiminnot ES6: ssa refraktoida se seuraavasti.
const add = a => b => a + b;
Matematiikassa sommittelu määritellään välittämällä yhden funktion ulostulo toisen syötteeksi yhdistetyn tuotoksen luomiseksi. Sama on mahdollista toiminnallisessa ohjelmoinnissa, koska käytämme puhtaita toimintoja.
Luodaan esimerkki luomalla joitain toimintoja.
Ensimmäinen funktio on alue, joka saa alkunumeron a
ja loppunumero b
ja luo taulukon, joka koostuu luvuista a
kohteeseen b
const range = (a, b) => a > b ? [] : [a, ...range(a+1, b)];
Sitten meillä on funktiokerroin, joka vie matriisin ja kertoo kaikki siinä olevat numerot.
const multiply = arr => arr.reduce((p, a) => p * a);
Käytämme näitä funktioita yhdessä laskettaessa kerroin.
const factorial = n => multiply(range(1, n)); factorial(5); // 120 factorial(6); // 720
Edellä oleva funktionaalisen laskennan funktio on samanlainen kuin f(x) = g(h(x))
, mikä osoittaa koostumuksen ominaisuuden.
Kävimme läpi puhtaat ja epäpuhtaat toiminnot, toiminnallisen ohjelmoinnin, uudet JavaScript-ominaisuudet, jotka auttavat sitä, ja muutama keskeinen käsite toiminnallisessa ohjelmoinnissa.
Toivomme, että tämä pala herättää kiinnostuksesi toiminnalliseen ohjelmointiin ja mahdollisesti motivoi sinua kokeilemaan sitä koodissasi. Olemme vakuuttuneita siitä, että siitä tulee oppimiskokemus ja virstanpylväs ohjelmistokehitysmatkallasi.
Toiminnallinen ohjelmointi on a hyvin - tutkittu ja kestävä paradigma tietokoneohjelmien kirjoittamiseen. Kanssa ES6: n käyttöönotto , JavaScript mahdollistaa paljon paremman toiminnallisen ohjelmointikokemuksen kuin koskaan ennen.
Toiminnallinen ohjelmointi on paradigma tietokoneohjelmien rakentamiseen käyttämällä ilmoituksia ja lausekkeita.
ES6: n uuden kehityksen ansiosta voimme sanoa, että JavaScript on sekä toiminnallinen että olio-ohjelmoitu kieli sen tarjoamien ensiluokkaisten ominaisuuksien vuoksi.
Toiminnallinen ohjelmointi varmistaa yksinkertaisemman virtauksen hallinnan koodissamme ja välttää yllätyksiä muuttujien ja tilan muutosten muodossa. Kaikki tämä auttaa meitä välttämään virheitä ja ymmärtämään koodimme helposti.
Lisp, Erlang, Haskell, Closure ja Python ovat muita toiminnallisia ohjelmointikieliä. Näissä Haskell on puhtaasti toiminnallinen ohjelmointikieli siinä mielessä, että se ei salli muuta ohjelmointiparadigmaa.
ES6 tai ECMAScript 6 on uusi JavaScript-versio, joka sisältää monia uusia ominaisuuksia, kuten nuolitoiminnot, vakiot ja hajautusoperaattorit.