Currying einer Funktion, die unendliche Argumente akzeptiert

Lesezeit: 6 Minuten

Benutzer-Avatar
LaufzeitNull

Wie können Sie mit ES5 eine Funktion verwenden, die unendliche Argumente akzeptiert?

function add(a, b, c) {
    return a + b + c;
}

Die obige Funktion akzeptiert nur drei Argumente, aber wir möchten, dass unsere Curry-Version unendlich viele Argumente akzeptieren kann.

Daher sollten von allen folgenden Testfällen bestanden werden:

var test = add(1);

test(2);     //should return 3
test(2,3);   //should return 6
test(4,5,6); //should return 16

Hier ist die Lösung, die ich mir ausgedacht habe:

function add(a, b, c) {
    var args = Array.prototype.slice.call(arguments);

    return function () {
        var secondArgs = Array.prototype.slice.call(arguments);
        var totalArguments = secondArgs.concat(args);

        var sum = 0;

        for (i = 0; i < totalArguments.length; i++) {
            sum += totalArguments[0];
        }

        return sum;
    }
}

Mir wurde jedoch gesagt, dass es im Stil nicht sehr „funktional“ ist.

  • Vielleicht fehlt dir ein + im ersten Code-Chunk: by return a + b c; meinst du return a + b + c;?

    – unpollo

    27. Januar 2016 um 14:00 Uhr

  • @unpollo .. Tippfehler wurden behoben

    – LaufzeitNull

    27. Januar 2016 um 14:01 Uhr

  • Warum willst du das machen?

    – Aadit M Shah

    28. Januar 2016 um 14:54 Uhr

  • Besser umziehen haskell obwohl.

    – Kerzenhalter

    9. Februar 2018 um 8:31 Uhr

  • test(2); Bei Verwendung Ihres Codes wurde 4 zurückgegeben。 sum += totalArguments[0]; 0 sollte sein i

    – Schleifen

    8. März 2018 um 13:40 Uhr

Benutzer-Avatar
Aadit M Shah

Methode 1: Verwenden partial

Eine einfache Lösung wäre die Verwendung partial folgendermaßen:

Function.prototype.partial = function () {
    var args = Array.prototype.concat.apply([null], arguments);
    return Function.prototype.bind.apply(this, args);
};

var test = add.partial(1);

alert(test(2));     // 3
alert(test(2,3));   // 6
alert(test(4,5,6)); // 16

function add() {
    var sum = 0;
    var length = arguments.length;
    for (var i = 0; i < length; i++)
        sum += arguments[i];
    return sum;
}

Methode 2: Currying auf einer Ebene

Wenn Sie nur eine Curry-Stufe wünschen, würde ich Folgendes tun:

var test = add(1);

alert(test(2));     // 3
alert(test(2,3));   // 6
alert(test(4,5,6)); // 16

function add() {
    var runningTotal = 0;
    var length = arguments.length;
    for (var i = 0; i < length; i++)
        runningTotal += arguments[i];

    return function () {
        var sum = runningTotal;
        var length = arguments.length;
        for (var i = 0; i < length; i++)
            sum += arguments[i];
        return sum;
    };
}

Methode 3: Currying auf unendlichem Niveau

Hier ist eine allgemeinere Lösung mit unendlich vielen Curry-Stufen:

var add = running(0);

var test = add(1);

alert(+test(2));     // 3
alert(+test(2,3));   // 6
alert(+test(4,5,6)); // 16

function running(total) {
    var summation = function () {
        var sum = total;
        var length = arguments.length;
        for (var i = 0; i < length; i++)
            sum += arguments[i];
        return running(sum);
    }

    summation.valueOf = function () {
        return total;
    };

    return summation;
}

EIN laufende Summe ist das Zwischenergebnis von a Summe. Das running function gibt eine andere Funktion zurück, die als Zahl behandelt werden kann (z. B. you can do 2 * running(21)). Da es sich jedoch auch um eine Funktion handelt, können Sie sie anwenden (zB you can do running(21)(21)). Es funktioniert, weil JavaScript die verwendet valueOf Methode, um Objekte automatisch in Primitive umzuwandeln.

Außerdem wird die Funktion erzeugt durch running ist rekursiv kuriert, sodass Sie es so oft auf so viele Argumente anwenden können, wie Sie möchten.

var resultA = running(0);
var resultB = resultA(1,2);
var resultC = resultB(3,4,5);
var resultD = resultC(6,7,8,9);

alert(resultD + resultD(10)); // 100

function running(total) {
    var summation = function () {
        var sum = total;
        var length = arguments.length;
        for (var i = 0; i < length; i++)
            sum += arguments[i];
        return running(sum);
    }

    summation.valueOf = function () {
        return total;
    };

    return summation;
}

Das einzige, was Sie beachten müssen, ist, dass Sie manchmal das Ergebnis manuell erzwingen müssen running in eine Zahl entweder durch Anwenden der Unärer Plusoperator zu ihm oder Aufruf seiner valueOf Methode direkt.

  • Die Anforderung besteht darin, keine Array-Eingabe zu übergeben. Ich suche auch nicht nach jemandem, der das Problem neu definiert. Sondern eher nach einer geeigneten Lösung. Danke für den Versuch.

    – LaufzeitNull

    28. Januar 2016 um 15:48 Uhr

  • @SMV Ich habe meine Antwort aktualisiert. Ist das eine passende Lösung für Ihr Problem?

    – Aadit M Shah

    28. Januar 2016 um 16:45 Uhr

Benutzer-Avatar
KevBot

Ein Teil des Grundes Ihre add Die Funktion ist nicht sehr “funktional”, weil sie versucht, mehr zu tun, als nur die an sie übergebenen Zahlen zu addieren. Es wäre für andere Entwickler verwirrend, sich Ihren Code anzusehen, siehe an add Funktion, und wenn sie sie aufrufen, erhalten sie anstelle der Summe eine Funktion zurück.

Zum Beispiel:

//Using your add function, I'm expecting 6
add(1,2,3) //Returns another function = confusing!

Der funktionale Ansatz

Der funktionale Ansatz wäre, eine Funktion zu erstellen, mit der Sie alle anderen Funktionen ausführen können, und vereinfachen Sie Ihre add function:

function curry(fn) {
    var args = Array.prototype.slice.call(arguments, 1);

    return function () {
        return fn.apply(this, args.concat(
                Array.prototype.slice.call(arguments, 0)
        ));
    }
}

function add() {
    var args = Array.prototype.slice.call(arguments);

    return args.reduce(function (previousValue, currentValue) {
        return previousValue + currentValue;
    });
}

Wenn Sie diese Funktion jetzt ausführen möchten, tun Sie einfach Folgendes:

var curry1 = curry(add, 1);
console.log(
        curry1(2), // Logs 3
        curry1(2, 3), // Logs 6
        curry1(4, 5, 6) // Logs 16
);

//You can do this with as many arguments as you want
var curry15 = curry(add, 1,2,3,4,5);
console.log(curry15(6,7,8,9)); // Logs 45

Wenn ich noch ergänzen möchte 1, 2, 3 Ich kann nur:

add(1,2,3) //Returns 6, AWESOME!

Fortsetzung des funktionalen Ansatzes

Dieser Code wird nun überall wiederverwendbar.

Sie können diese Curry-Funktion verwenden, um andere Curry-Funktionsreferenzen ohne zusätzlichen Aufwand zu erstellen.

Um beim mathematischen Thema zu bleiben, nehmen wir an, wir hatten eine Multiplikationsfunktion, die alle an sie übergebenen Zahlen multipliziert:

function multiply() {
    var args = Array.prototype.slice.call(arguments);

    return args.reduce(function (previousValue, currentValue) {
        return previousValue * currentValue;
    });
}

multiply(2,4,8) // Returns 64

var curryMultiply2 = curry(multiply, 2);
curryMultiply2(4,8) // Returns 64

Dieser funktionale Currying-Ansatz ermöglicht es Ihnen, diesen Ansatz für jede Funktion zu verwenden, nicht nur für mathematische. Obwohl die mitgelieferte curry Funktion nicht alle Randfälle unterstützt, bietet sie eine funktionale, einfache Lösung für Ihr Problem, auf der leicht aufgebaut werden kann.

Benutzer-Avatar
Sindhu

Ähnlich wie bei obigem Problem. Summe des Currys der n-ten Ebene durch Rekursion

Trick: Um die Rekursion zu stoppen übergebe ich last() als blank**

function sum(num1) {
        return (num2) => {
            if(!num2) {
                return num1;
            }
            return sum(num1 + num2);
        }
    }
console.log('Sum :', sum(1)(2)(3)(4)(5)(6)(7)(8)())

Benutzer-Avatar
jJ’

Es gibt einen allgemeineren Ansatz, indem eine Curry-Funktion definiert wird, die eine minimale Anzahl von Argumenten benötigt, wenn sie die innere Funktion auswertet. Lassen Sie mich zuerst ES6 verwenden (ES5 später), da es transparenter wird:

var curry = (n, f, ...a) => a.length >= n
    ? f(...a)
    : (...ia) => curry(n, f, ...[...a, ...ia]);

Definieren Sie dann eine Funktion, die alle Argumente summiert:

var sum = (...args) => args.reduce((a, b) => a + b);

dann können wir es kurieren und sagen, dass es warten soll, bis mindestens 2 Argumente vorhanden sind:

var add = curry(2, sum);

Dann passt alles:

add(1, 2, 3) // returns 6
var add1 = add(1);
add1(2) // returns 3
add1(2,3) // returns 6
add1(4,5,6) // returns 16

Sie können sogar das Erstellen überspringen add indem Sie das/die erste(n) Argument(e) angeben:

var add1 = curry(2, sum, 1);

Die ES5-Version von Curry ist mangels nicht so hübsch ... Operator:

function curry(n, f) {
    var a = [].slice.call(arguments, 2);
    return a.length >= n
        ? f.apply(null, a)
        : function () {
            var ia = [].slice.call(arguments);
            return curry.apply(null, [n, f].concat(a).concat(ia));
        };
}

function sum() {
    return [].slice.call(arguments).reduce(function (a, b) {
        return a + b;
    });
};

Der Rest ist gleich…

Hinweis: Wenn die Effizienz ein Problem darstellt, möchten Sie möglicherweise nicht verwenden slice an argumentsaber kopieren Sie es explizit in ein neues Array.

Etwas spät in diesem Spiel, aber hier sind meine zwei Cent. Im Grunde macht man sich dabei die Tatsache zunutze, dass auch Funktionen in JavaScript Objekte sind.

function add(x) {
  if (x === undefined) {
    return add.numbers.reduce((acc, elem) => acc + elem, 0);
  } else {
    if (add.numbers) {
      add.numbers.push(x);
    } else {
      add.numbers = [x];
    }
  }
  return add;
}

Benutzer-Avatar
ouflak

Unendliche Summe mit Currying, Sie können einen einzelnen Parameter oder mehrere bis unendlich übergeben:

function adding(...arg) {

    return function clousureReturn(...arg1) {
        if (!arguments.length) {
            let finalArr = [...arg, ...arg1];
            let total = finalArr.reduce((sum, ele) => sum + ele);
            return total;
        }

        return adding(...arg, ...arg1)
    }
}

1015350cookie-checkCurrying einer Funktion, die unendliche Argumente akzeptiert

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy