2016-06-28 4 views
7

Ich arbeite an das Erstellen meiner eigenen Callback-Funktionen und höherer Ordnung Funktionsgruppen. Ich bin fest auf die Replikation der Unterstreichung reduzieren Funktion oder ._Reduce-Funktion. Kann mir jemand helfen, zu verstehen, wie es unter der Haube funktioniert hat es ein paar Tage für mich und ich bin ratlos. Hier ist was ich bisher habe. Bitte verstehe, dass ich die Unterstrich-Bibliothek nicht verwende, sondern versuche, sie zu replizieren, so dass ich mein Verständnis von Funktionen höherer Ordnung verbessern kann. Vielen Dank.Erstellen der Unterstreichung reduzieren Funktion von Grund auf

var reduce = function(collection, iterator, accumulator) { 

    var iterator = function(startPoint, combiner){ 
     for(var i = 0; i <combiner.length; i++){ 
     startPoint += combiner[i]; 
    } 
    return iterator(accumulator, collection); 
} 
+0

Nun, das ist nicht vollständiger Code und hat einige Probleme. Wie sieht deine "Reduce" -Funktion aus? –

+0

Sie überschreiben 'Iterator' - warum? Woher kommt 'startPoint'? Was bedeutet die Variable "Combiner" und warum haben Sie sie so benannt? –

Antwort

-1

In den Kommentaren dieser Antworten hat es eine Menge Verwirrung zwischen reduce und Array.prototype.reduce der Unders gewesen. Zwei Anmerkungen:

  1. Underscore reduce ermöglicht eine leere Sammlung und keinen Startwert. In diesem Fall wird kein Fehler ausgegeben, sondern undefined zurückgegeben. naomik hat mich davon überzeugt, dass das nicht sicher ist. Zum Beispiel _([]).reduce(function(a, b) { return a + b}); sollte entweder einen Fehler werfen oder eine leere Liste zurückgeben.
  2. Underscore reduce funktioniert auf beiden Objekten und Arrays.

Nun zu meinem ursprünglichen Beitrag:


Ich habe eigentlich die gleiche implementierende die wichtigsten Funktionen von Unterstrichen aus kratz einer Weile zurück, und reduce war wohl der heikelste. Ich denke, reduce einfacher ist, mit einem nicht-funktionalen reduce (Kredit naomik für diesen) grok:

function reduce(arr, func, seed) { 
    var result = seed, 
     len = arr.length, 
     i = 0; 
    for (; i < len; i++) { 
     result = func(result, arr[i]) 
    } 
    return result 
} 

Unders Implementierung ist ein wenig komplexe, Handhabung Objekte und Arrays, leere Sammlungen und einen optionalen Startwertes. Es verwendet auch each anstelle einer for-Schleife, da das in Stil mehr funktional ist. Dies ist meine Implementierung von Unders der reduce:

var reduce = function(coll, func, seed) { 
    // `isEmpty` (not shown) handles empty arrays, strings, and objects. 
    // Underscore accepts an optional seed value and does not 
    // throw an error if given an empty collection and no seed. 
    if (isEmpty(coll)) { 
     return coll; 
    } 
    var noSeed = arguments.length < 3; 

    // `each` (not shown) should treat arrays and objects 
    // in the same way. 
    each(coll, function(item, i) { 
     if (noSeed) { 
      // This condition passes at most once. If it passes, 
      // this means the user did not provide a seed value. 
      // Default to the first item in the list. 
      noSeed = false; 
      seed = item; 
     } else { 
      seed = func(seed, item, i); 
     } 
    }); 

    return seed; 
}; 
+0

Reduktion ohne Anfangswert ist grundsätzlich unsicher. Es ermöglicht Ihnen, schlechten Code zu schreiben. – naomik

+0

Ich verstehe nicht. Können Sie ein Szenario bereitstellen, in dem unerwartetes Verhalten auftritt? – gwg

+0

Angenommen, wir haben eine 'sum'-Funktion, die die' Number'-Summe eines Arrays von 'Number'-Werten zurückgibt; zB 'sum ([1,2,3]) // => 6'. Wenn wir 'sum' mit der' reduce' Methode und dem * no * "seed" Wert implementieren, gibt 'sum ([])' jetzt 'undefined' zurück, was keine' Nummer' ist. Wenn wir 'sum' auf diese Weise implementiert haben, können wir keine Annahmen über den Rückgabewert treffen. Nun, wenn wir es benutzen, müssen wir 'var s = sum (arr); if (s === undefiniert) {console.log ('schlechte Summe')} else {console.log (s)} 'was ist Müll. Wenn Sie diese Prüfung nicht durchführen, haben Sie die falsche Annahme gemacht, dass "sum" Ihnen ein "Zahl" -Ergebnis gegeben hat. – naomik

0

Es ist so etwas wie es:

function reduce(array, combine, start) { 
    for (var i = 0; i < array.length; i++) 
    start = combine(start, array[i]); 
    return start; 
} 

console.log(reduce([1, 2, 3, 4], function(a, b) { 
    return a + b; 
}, 0)); 

Link-Referenz: http://eloquentjavascript.net/05_higher_order.html

+0

Warum führen Sie eine neue Variable 'current' ein? –

+0

Es wurde eingeführt, weil, wenn der Startparameter hinzugefügt wurde, die 'current'-Variable den Startwert in der ersten Iteration der Schleife benötigt. – msantos

+0

Aber Sie können einfach 'current' eliminieren und einfach' start' verwenden, wo immer Sie 'current' verwenden. –

4

Eine einfache rekursive Funktion funktioniert der Trick

// arr - some array of values 
 
// f - the reducing function 
 
// acc - initial value for the accumulator 
 
function reduce(arr, f, acc) { 
 
    if (arr.length === 0) 
 
    return acc 
 
    else 
 
    return reduce(arr.slice(1), f, f(acc, arr[0])) 
 
} 
 

 
// -------------------------------------------------- 
 
    
 
// example 1: 
 
// reduce an array of numbers using an adding function 
 

 
var ex1 = reduce([1,2,3], function(acc, x) { return acc + x }, 0) 
 

 
console.log(ex1) 
 
//=> 6 
 

 
// -------------------------------------------------- 
 

 
// example 2: 
 
// reduce an array of pairs to a mapping object 
 

 
var ex2 = reduce([['a', 1], ['b', 2], ['c', 3]], function(acc, pair) { 
 
    var key = pair[0] 
 
    var value = pair[1] 
 
    acc[key] = value 
 
    return acc 
 
}, {}) 
 

 
console.log(ex2) 
 
//=> { a: 1, b: 2, c: 3 }


Wie @torazaburo in einem Kommentar weist darauf hin, wenn Sie können Verwenden Sie ES6, Destrukturierungsaufgabe räumt die Implementierung noch mehr auf

// ES6 
function reduce([x, ...xs], f, acc) { 
    if (x === undefined) 
    return acc 
    else 
    return reduce(xs, f, f(acc, x)) 
} 

Oder es wird Super zuckersüß mit Pfeil Funktionen

// ES6, same as above but using arrow function and ternary expression 
const reduce = ([x, ...xs], f, acc)=> 
    x === undefined ? acc : reduce(xs, f, f(acc, x)) 

Die Unders Implementierung einige andere Annehmlichkeiten tut bieten, obwohl ich vermute, das sind hier die Kompatibilität mit nativem Array.prototype.reduce zu halten. Ich persönlich würde das nicht auf diese Weise reduzieren, aber das ist nebensächlich.

  1. Underscore übergibt einen Iterator und eine Arr-Referenz an die Callback-Funktion.
  2. Unders ermöglicht es Ihnen, den Kontext für die Callback-Funktion ist zu ändern

Hier eine überarbeitete Implementierung, die

// our reduce version 2.0 
function reduce(collection, iterator, memo, context) { 
    function loop(memo, i) { 
    if (collection.length === i) 
     return memo 
    else 
     return loop(iterator.call(context, memo, collection[i], i, collection), i + 1) 
    } 
    return loop(memo, 0) 
} 

diese Funktionen unterstützt, können Sie es gleich verwenden, wie erst jetzt darüber mehr Informationen liefert an die Callback

HINWEIS

habe beschlossen, ich gezielt nicht, um ein Verhalten von Underscore reduzieren zu implementieren, das es Ihnen ermöglicht, eine Reduzierung ohne einen Anfangswert durchzuführen. Die Unterstützung dieses Verhaltens führt zu unsicheren Code und sollte eigentlich nie zu Underscore geführt haben.

+0

Odd dass Downvote mich, aber dann Referenz Underscore tatsächlichen Implementierung, die ein Benutzer * nicht * bietet einen Ausgangswert: http://underscorejs.org/docs/underscore.html#section-22 – gwg

+0

Wenn Sie sich beziehen der Code auf der Unterseite, das ist * not * Unterstrich Implementierung. Das ist meins und es erfordert einen Anfangswert. – naomik

+0

Ich verbinde mich buchstäblich mit der Quelle von Underscore in meinem Kommentar. Es hat einen Haken (f + ctrl dafür) 'if (Argumente.Länge <3) {...}'. Es erlaubt keinen Samen. – gwg

Verwandte Themen