ugrás a tartalomhoz

Számítás önmagukat felüldefiniáló és memoizációt használó JavaScript függvényekkel

Poetro · 2011. Jún. 30. (Cs), 17.41

Ádám majd úgy is ki fog javítani, hogy ezeket hogyan lehetne magyarul írni: self defining function és memoization. Mindenesetre felmerült a probléma, hogy hogyan lehetne optimalizálni rekurzív függvényeket, úgy hogy egyfajta iterátorként is működhessenek. Ehhez írtam egy nagyszerű kis függvényt, ami a Fibonacci-sort számítja ki, de kicsit érdekesen. Ami meglepő lehet, hogy a függvény az első sorban felüldefiniálja és bővíti magát pár tulajdonsággal és metódussal. Például gyorstárazza a már eddig kiszámolt értékeket, mindezt egy hordozható formában.

Várom róla a véleményeket:

  1. /** 
  2.  * Kiszámolja az n. Fibonacci-számot. 
  3.  *  
  4.  * @param {Number} n 
  5.  *   Az a természetes szám, amelyik elemre a Fibonacci-sorban szükségünk van. 
  6.  * @return {Number} 
  7.  *   Az n. Fibonacci-szám. 
  8.  */  
  9. function fib(n) {  
  10.   /** 
  11.    * Felüldefiniáljuk a függvényünket, mely visszaadja az n. Fibonacci-számot. 
  12.    */  
  13.   fib = function (n) {  
  14.     if (fib.cache[n] === undefined) {  
  15.       fib.cache[n] = fib(n - 1) + fib(n - 2);  
  16.     }  
  17.     return fib.cache[n];  
  18.   }  
  19.     
  20.   // Bővítjük saját tulajdonsággal és metódussal.  
  21.   fib.cache = [0, 1];  
  22.   
  23.   /** 
  24.    * A következő nem gyorsítótárazott Fibonacci-számot számolja ki. 
  25.    *  
  26.    * @return {Number} 
  27.    *   A következő még ki nem számított Fibonacci-szám. 
  28.    */  
  29.   fib.next = function () {  
  30.     return fib(fib.cache.length);  
  31.   }  
  32.     
  33.   return fib(n);  
  34. }  
  35.   
  36. (function(n) {  
  37.   // Lekérdezzük a 0. elemet.  
  38.   fib(0);  
  39.     
  40.   while (n--) {  
  41.     // Meghívjuk a next metódust 10-szer.  
  42.     console.log(fib.next());  
  43.   }  
  44.     
  45.   console.log('A 20. Fibonacci-szám:', fib(20));  
  46. }(10));  

Kimenet:

1
2
3
5
8
13
21
34
55
89
A 20. Fibonacci szám: 6765
 
1

Ezeket általában úgy szokás,

Joó Ádám · 2011. Jún. 30. (Cs), 21.02
Ezeket általában úgy szokás, hogy a függvény minden futáskor leellenőrzi, létezik-e már a gyorsítótár. Ezt a feltételvizsgálatot akarod megspórolni az újradefiniálással?

Egyébként én mindegyszerűen öníró (nektek öndefiniáló) függvényeknek nevezném őket. A memoizációra akadémiai körökben lehet, hogy létezik már valamilyen szép magyar fordítás, én hirtelenjében mondjuk az emlékező függvényt adnám nekik a keresztségben.
3

Gyorsítás

Poetro · 2011. Jún. 30. (Cs), 21.40
Ezeket általában úgy szokás, hogy a függvény minden futáskor leellenőrzi, létezik-e már a gyorsítótár. Ezt a feltételvizsgálatot akarod megspórolni az újradefiniálással?

Hát igazából ez egy tervezési minta, amivel az inicializálást és ellenőrzést lehet elkerülni. A lényeg, hogy egészen addig nem történik meg a függvény előkészítése, amíg meg nem hívódik. Ezzel memóriát, illetve amennyiben az inicializáció költséges akkor teljesítményt javíthatunk. Amennyiben a függvényünket nem hívják meg, az inicializáció se fut le feleslegesen. És igen a függvényben minden egyes futáskor nem kell ellenőrizni, hogy megtörtént-e már az inicializáció, mert ebben biztosak lehetünk.
Ugyanakkor vannak más tényezők is. Például idegen kód törölheti a gyorsítótárunkat, de erre felkészülhetünk, ha hozzáadunk a függvényhez egy init metódust, ami elvégzi az újbóli inicializációt, és így konzisztens maradhat a működés, és a gyorsítótár is üríthető, amennyiben a működés azt megkívánja.
Például:
  1. function fib(n) {  
  2.   var cache;  
  3.   fib = function (n) {  
  4.     if (cache[n] === undefined) {  
  5.       cache[n] = fib(n - 1) + fib(n - 2);  
  6.     }  
  7.     return cache[n];  
  8.   }  
  9.     
  10.   fib.init = function () {  
  11.     // Valami intenzív tevékenység.  
  12.     cache = [0, 1];  
  13.   };  
  14.   fib.next = function () {  
  15.     return fib(cache.length);  
  16.   }  
  17.   
  18.   return fib.init(), fib(n);  
  19. }  
2

Príma

joed · 2011. Jún. 30. (Cs), 21.24
Príma, most kérnék szépen egy életszerű példát! :)
4

Böngésző független eseménykezelés

Poetro · 2011. Jún. 30. (Cs), 21.56
Például tegyük fel, hogy meg akarsz valósítani egy böngésző független eseménykezelést.
  1. function addEvent(element, event, callback) {  
  2.   addEvent = window.addEventListener ?  
  3.     // W3C szabványos eseménykezelés  
  4.     function (element, event, callback) {  
  5.       element.addEventListener(event, callback);  
  6.     } :  
  7.     window.attachEvent ?  
  8.     // MS eseménykezelés  
  9.     function (element, event, callback) {  
  10.       element.attachEvent('on' + event, callback, false);  
  11.     } :  
  12.     // DOM0 eseménykezelés   
  13.     function (element, event, callback) {  
  14.       element['on' + event] = callback;  
  15.     };  
  16.   addEvent(element, event, callback);  
  17. }  
Ekkor nem kell minden egyes futáskor megnézni, a böngésző milyen eseménykezelési modellt támogat, hanem elég egyszer, a függvény első futásakor.
6

Érdekes megoldás, én ezt alap

inf · 2011. Jún. 30. (Cs), 23.12
Érdekes megoldás, én ezt alapból closure-ban csinálnám, vagy csinálnék külön ie és w3c fájlokat, de ez is egész jó.
5

Ezért jó nevet adni a

Karvaly84 · 2011. Jún. 30. (Cs), 22.52
Ezért jó nevet adni a függvényeinknek, plusz lett pár ötletem!
7

A memoizálásban az a szép,

tgr · 2011. Júl. 1. (P), 01.18
A memoizálásban az a szép, hogy nem kell minden függvényhez kézzel megírni:
  1. function fib(n) {  
  2.     return (n < 2) ? n : fib(n-1) + fib(n-2);  
  3. }  
  4.   
  5. Function.prototype.memoize = function(fun) {  
  6.     var cache = {};  
  7.     return function() {  
  8.          var args = Array.prototype.slice.call(arguments);  
  9.          return (args in cache) ? cache[args] : (cache[args] = fun.apply(this, arguments));  
  10.     }  
  11. }  
  12.   
  13. fib = fib.memoize();  
8

fun

T.G · 2011. Júl. 1. (P), 08.20
A fun változó nem paraméterből jön, hanem az a this lenne, nemdebár?
11

De, persze.

tgr · 2011. Júl. 1. (P), 09.14
De, persze.
12

Yepp, innentől meg ugyanott

inf · 2011. Júl. 1. (P), 11.50
Yepp, innentől meg ugyanott vagyunk, mint a bind, meg a hasonlók. Én pl anno csináltam automatikus típusellenőrzést is metódusokhoz hasonló elven. Valahogy így működött:
  1. Color=Class(  
  2. {  
  3.     red: Number,  
  4.     green: Number,  
  5.     blue: Number,  
  6.     rgb: Null  
  7. },  
  8. {  
  9.     rgb: function (red,green,blue)  
  10.     {  
  11.         //...  
  12.     },  
  13.     //...  
  14. });  
Nagyon sok dolgot lehet automatizálni beágyazott függvényekkel...
9

Inicializálás…

presidento · 2011. Júl. 1. (P), 08.23
Nekem csak az nem szimpatikus benne, hogy indirekt hivatkozás esetén minden alkalommal lefut az inicializálás…
  1. var myFun = fib;  
  2. myFun(10); // inicializál  
  3. myFun(10); // inicializál  
13

var myFun = (fib(0), fib);

Poetro · 2011. Júl. 1. (P), 11.52
  1. var myFun = (fib(0), fib);  // Előre inicializálunk  
  2. myFun(10); // nem inicializál    
  3. myFun(10); // nem inicializál    
10

init csak egyszer

T.G · 2011. Júl. 1. (P), 08.47
Nálam gyakori a következő megoldás:
  1. new Button({  
  2.     handler: function () {  
  3.         this.initHandler();  
  4.         this.handler = this.doHandler;  
  5.         this.doHandler();  
  6.     }  
  7. });  
Az előkészítést csak akkor indítom el, amikor valóban szükség van rá, a későbbiekben meg ahelyett, hogy ellenőrizném, hogy már ez lefutott-e, egyszerűen kihagyom.
14

Hasznos cucc, már fel is

inf · 2011. Júl. 1. (P), 23.13
Hasznos cucc, már fel is használtam a gyakorlatban :-)