Kuidas kasutada funktsiooni Memoize JavaScripti funktsiooni tulemuste vahemällu talletamiseks ja koodi kiirendamiseks

Funktsioonid on programmeerimise lahutamatu osa. Need aitavad lisada meie koodile modulaarsust ja korduvkasutatavust .

On üsna tavaline, et jagame oma programmi tükkideks, kasutades funktsioone, millele saame hiljem mõne kasuliku toimingu tegemiseks helistada.

Mõnikord võib funktsiooni mitu korda helistamine kalliks minna (näiteks funktsioon, mis arvutab faktoori). Kuid on olemas viis, kuidas saame selliseid funktsioone optimeerida ja panna need palju kiiremini täitma: vahemällu salvestamine .

Oletame näiteks, et meil on functionarvu faktori tagastamine:

function factorial(n) { // Calculations: n * (n-1) * (n-2) * ... (2) * (1) return factorial }

Suurepärane, nüüd leiame factorial(50). Arvuti teeb arvutused ja annab meile lõpliku vastuse, armas!

Kui see on tehtud, leiame üles factorial(51). Arvuti teeb jälle mitmeid arvutusi ja saab meile tulemuse, kuid võite olla märganud, et kordame juba mitmeid samme, mida oleks saanud vältida. Optimeeritud viis oleks:

factorial(51) = factorial(50) * 51

Kuid meie functionteostame arvutused nullist iga kord, kui seda nimetatakse:

factorial(51) = 51 * 50 * 49 * ... * 2 * 1

Kas poleks lahe, kui kuidagi factorialsuudaks meie funktsioon oma eelmiste arvutuste väärtused meelde jätta ja neid täitmise kiirendamiseks kasutada?

Tulemuseks on memodefunction lisamine , viis, kuidas tulemused tulemusi meelde jätta (vahemällu salvestada). Nüüd, kui olete põhiteadmised sellest, mida me püüame saavutada, on siin ametlik määratlus:

Memodeerimine on optimeerimistehnika, mida kasutatakse peamiselt arvutiprogrammide kiirendamiseks, salvestades kallite funktsioonikõnede tulemused ja tagastades vahemällu salvestatud tulemuse, kui samad sisendid ilmuvad uuesti

Lihtsalt meelde jätmine tähendab meelde jätmist või mällu salvestamist. Memodeeritud funktsioon on tavaliselt kiirem, sest kui funktsiooni kutsutakse hiljem eelmise (te) väärtusega, tooksime funktsiooni täitmise asemel tulemuse vahemälust.

Nii võib lihtne memodeeritud funktsioon välja näha (ja siin on CodePen juhuks, kui soovite sellega suhelda) :

// a simple function to add something const add = (n) => (n + 10); add(9); // a simple memoized function to add something const memoizedAdd = () => { let cache = {}; return (n) => { if (n in cache) { console.log('Fetching from cache'); return cache[n]; } else { console.log('Calculating result'); let result = n + 10; cache[n] = result; return result; } } } // returned function from memoizedAdd const newAdd = memoizedAdd(); console.log(newAdd(9)); // calculated console.log(newAdd(9)); // cached

Memode kaasavõtmine

Mõned ülaltoodud koodist eemaldamised on:

  • memoizedAddtagastab a, functionmillele hiljem viidatakse. See on võimalik, kuna JavaScripti funktsioonid on esmaklassilised objektid, mis võimaldab meil neid kasutada kõrgema järgu funktsioonidena ja tagastada teise funktsiooni.
  • cachesuudab oma väärtusi meelde jätta , kuna tagastatud funktsioonil on see kinni.
  • On hädavajalik, et memodeeritud funktsioon oleks puhas. Puhas funktsioon tagastab konkreetse sisendi jaoks sama väljundi, kui mitu korda seda kutsutakse, mis muudab cachetöö ootuspäraseks.

Oma memoizefunktsiooni kirjutamine

Eelmine kood töötab hästi, kuid mis siis, kui me tahaksime muuta mis tahes funktsiooni memo-funktsiooniks?

Oma memoize-funktsiooni (koodisulg) kirjutamiseks toimige järgmiselt.

// a simple pure function to get a value adding 10 const add = (n) => (n + 10); console.log('Simple call', add(3)); // a simple memoize function that takes in a function // and returns a memoized function const memoize = (fn) => { let cache = {}; return (...args) => { let n = args[0]; // just taking one argument here if (n in cache) { console.log('Fetching from cache'); return cache[n]; } else { console.log('Calculating result'); let result = fn(n); cache[n] = result; return result; } } } // creating a memoized function for the 'add' pure function const memoizedAdd = memoize(add); console.log(memoizedAdd(3)); // calculated console.log(memoizedAdd(3)); // cached console.log(memoizedAdd(4)); // calculated console.log(memoizedAdd(4)); // cached

Nüüd on see suurepärane! See lihtne memoizefunktsioon ümbritseb kõik lihtsad functionmemodeeritud ekvivalendiks. Kood töötab suurepäraselt lihtsate funktsioonide jaoks ja seda saab hõlpsasti kohandada, et argumentsteie vajadustele vastavalt suvalist arvu käsitleda . Teine võimalus on kasutada mõnda de-facto teeki, näiteks:

  • Lodashi oma _.memoize(func, [resolver])
  • ES7 @memoizedekoraatorid dekolt

Rekursiivsete funktsioonide meelde jätmine

Kui proovite rekursiivse funktsiooni sisestada Lodashi memoizekohal _.memoizeasuvale või sealt väljuvale funktsioonile , pole tulemused ootuspärased, kuna selle järgnevate kõnede rekursiivne funktsioon helistab memodeeritud funktsiooni asemel iseendale ja ei kasuta funktsiooni cache.

Lihtsalt veenduge, et teie rekursiivne funktsioon kutsub üles memode funktsiooni. Siit saate teada, kuidas saate õpiku faktori näidet (koodnõel) muuta:

// same memoize function from before const memoize = (fn) => { let cache = {}; return (...args) => { let n = args[0]; if (n in cache) { console.log('Fetching from cache', n); return cache[n]; } else { console.log('Calculating result', n); let result = fn(n); cache[n] = result; return result; } } } const factorial = memoize( (x) => { if (x === 0) { return 1; } else { return x * factorial(x - 1); } } ); console.log(factorial(5)); // calculated console.log(factorial(6)); // calculated for 6 and cached for 5

Mõned punktid, mida sellest koodist märkida:

  • factorialFunktsiooni rekursiivselt helistades memoized versiooni ise.
  • Memodeeritud funktsioon salvestab vahemälu eelmiste faktooriumide väärtused, mis parandab oluliselt arvutusi, kuna neid saab uuesti kasutada factorial(6) = 6 * factorial(5)

Kas memode salvestamine on sama mis vahemällu salvestamine?

Jah, selline. Memode salvestamine on tegelikult spetsiifiline vahemälu salvestamise tüüp. Kuigi vahemällu võib viidata üldiselt ükskõik salvestamise tehnikat (nagu HTTP vahemälu) tulevikus kasutamiseks memoizing konkreetselt hõlmab vahemälustab tagastamise väärtusi function.

Millal oma funktsioone memodeerida

Kuigi võib tunduda, et memosid saab kasutada kõigi funktsioonidega, on sellel tegelikult piiratud kasutusjuhtumeid:

  • Funktsiooni memodeerimiseks peaks see olema puhas, nii et tagastusväärtused oleksid iga kord samade sisendite jaoks samad
  • Mälestamine on kompromiss lisatud ruumi ja kiiruse vahel ning on seega oluline ainult piiratud sisendivahemikuga funktsioonide jaoks, nii et vahemälu väärtusi saab sagedamini kasutada
  • Võib tunduda, et peaksite oma API-kõned meelde jätma, kuid see pole vajalik, kuna brauser salvestab need teie jaoks automaatselt vahemällu. Lisateavet leiate jaotisest HTTP vahemälu
  • Parim kasutatav juhtum, mille leidsin memodeeritud funktsioonide jaoks, on raskete arvutusfunktsioonide jaoks, mis võivad jõudlust märkimisväärselt parandada (faktori- ja fibonacci pole tegelikult head näited reaalses maailmas)
  • Kui tegelete React / Reduxiga, saate vaadata uuesti valimist, mis kasutab memodeeritud valijat , et arvutused toimuksid ainult siis, kui olekupuu seotud osas toimus muutus.

Lisalugemist

Järgmised lingid võivad olla kasulikud, kui soovite selle artikli mõnede teemade kohta rohkem teada saada:

  • Kõrgema järjekorra funktsioonid JavaScriptis
  • Sulgemised JavaScriptis
  • Puhtad funktsioonid
  • Lodashi _.memoizedokumendid ja lähtekood
  • Rohkem memo näiteid siin ja siin
  • reageerida / uuesti valida

Loodan, et see artikkel oli teile kasulik ja olete JavaScripti memode lisamisest paremini aru saanud :)

Viimaste värskenduste saamiseks võite mind jälgida twitteris. Olen hakanud oma isiklikusse ajaveebi ka värskemaid postitusi postitama.