2015-03-08 14 views
14

Sto provando a scrivere una funzione di aggiunta che funzionerà in molti scenari.Funzione "aggiungi" che funziona con diverse combinazioni di concatenamento/argomenti

add(2,2,2) //6 
add(2,2,2,2) //8 
add(2)(2)(2) // 6 
add(2)(2)(2,2).value() //8 
add(2,2)(2) + 2 //8 
add(2).add(2) //4 
add(2,2,2).add(2).add(2,2).value() //12 
add(2,2,2).add(2).value() //8 

Questo è quello che ho finora:

function add(){ 
 
    var sum = 0; 
 
    for(var i in arguments){ 
 
     sum += arguments[i]; 
 
    } 
 

 
    var ret = add.bind(null, sum); 
 

 
    ret.value = function() { 
 
     return sum; 
 
    } 
 

 
    ret.add = function() { 
 
     for(var i in arguments){ 
 
     sum += arguments[i]; 
 
     } 
 
     return sum; 
 
    } 
 

 
    ret.valueOf = function(){ return sum; }; 
 
    
 
    return ret; 
 
} 
 

 
console.log(add(2,2,2)); 
 
console.log(add(2,2,2,2)); 
 
console.log(add(2)(2)(2)); 
 
console.log(add(2)(2)(2,2).value()); 
 
console.log(add(2,2)(2) + 2); 
 
console.log(add(2).add(2)); 
 
console.log(add(2,2,2).add(2).value()); 
 
console.log(add(2,2,2).add(2).add(2,2).value());

Sto avendo un problema con gli ultimi due casi:

add(2,2,2).add(2).add(2,2).value() //12 
    add(2,2,2).add(2).value() //8 

Sembra che io dovrebbe continuare a nidificare le funzioni di aggiunta se volessi fare catena più di due insieme e aggiungiamo anche la funzione valore a ciascuno di essi, ma ovviamente mi manca qualcosa di semplice che mi permetta di incatenarli quanto mi piace e di chiamare il valore su ognuno di essi.

Inoltre devono sempre restituire int (non stringhe) e sembra che a volte lo facciano e altre volte no?

+0

btw, non utilizzare "al" loop per scorrere array, poiché il ciclo for-in non ha ordine di iterazione garantita e può anche un'iterazione sulle proprietà non interi. Usa invece un ciclo regolare 'for (i = 0, i hugomg

+12

Stai provando a fare ** way ** a molto qui. Come esercizio di apprendimento potrebbe essere divertente, ma spero davvero che tu non stia cercando di usarlo al di fuori della sperimentazione inattiva. Questo tipo di ridicolo ingannevole sovraccarico di sintassi non ha spazio nel codice del mondo reale. – meagar

+0

Se proprio ne hai bisogno, puoi semplicemente aggiungere la funzione al prototipo Number. Oppure creare un wrapper personalizzato che estenda l'oggetto Number in modo da non sporcare il prototipo originale. Non sono proprio sicuro del motivo per cui non puoi usare '+' anche se .. – SmokeyPHP

risposta

3

Dal momento che si restituisce la somma in thats funzione ret.add il motivo per cui l'errore è venuta provare qualcosa di simile, la speranza che possa risolvere il problema

function add(){ 
    var sum = 0; 
    for(var i in arguments){ 
     sum += arguments[i]; 
    } 

    var ret = add.bind(null, sum); 

    ret.value = function() { 
     return sum; 
    } 

    ret.add = function() { 
     for(var i in arguments){ 
     sum += arguments[i]; 
     } 
     return ret; 
    } 

    ret.valueOf = function(){ return sum; }; 

    return ret; 
} 
+1

L'implementazione del metodo 'add' qui introduce incoerenza perché ha effetti collaterali: https://jsfiddle.net/hjrj7mnq/1/ – JLRishe

3

Inoltre hanno bisogno di tornare sempre int (non stringhe), e sembra che a volte lo fanno e altre volte no?

Sì, questo è sicuramente un problema concettuale. Queste due cose che vuoi non sono compatibili. add(2,2,2) un numero o qualcosa con un metodo add?

add(2,2,2) //6 
add(2,2,2).add(2).value() //8 

Anche se c'è un modo elegante per aggiungere i metodi per nubmers, consiglio vivamente mantenere le cose semplici e che richiedono sempre un ".value()" chiamata per terminare la catena. In questo modo tutte le chiamate a ".add" restituiscono un "oggetto adder" e tutte le chiamate a ".value" restituiscono un numero regolare.

sembra che avrei dovuto continuare a nidificare le funzioni di aggiunta se volevo concatenare più di due insieme e anche aggiungere la funzione valore a ciascuno di essi, ma ovviamente mi manca qualcosa di semplice che mi permetterà per incatenarli quanto mi piace e chiamare il valore su ognuno di essi.

La risposta è utilizzare le funzioni ricorsive. Ecco una funzione che crea l'oggetto "vipera" Ho già detto in precedenza:

function sumArray(arr){ 
    var s = 0; 
    for(var i=0; i<arr.length; i++){ 
     s += arr[i]; 
    } 
    return s; 
} 

function mkAdder(currSum){ 
    return { 
     value: function(){ 
      return currSum; 
     }, 
     valueOf: function(){ 
      return currSum; 
     }, 
     add: function(/**/){ 
      return mkAdder(currSum + sumArray(arguments)); 
     } 
    } 
} 

Allora la vostra funzione iniziale aggiuntivo sarebbe simile a questa:

function add(/**/){ 
    return mkAdder(sumArray(arguments)); 
} 
15

Osservando il modo in cui si sta utilizzando arguments in simili modi in due luoghi diversi, è chiaro che si sta duplicando la funzionalità ed è per questo che si sta imbattendo in questo problema con dover "annidare infinitamente" il metodo .value().

La cosa fondamentale da riconoscere è che add() può restituire una funzione che si riferisce a se stessa come proprietà add.Ciò consentirà add(1,2)(3) di comportarsi esattamente come add(1,2).add(3). Questo può essere fatto in questo modo:

function add() { 
 
    var sum = Array.prototype.reduce.call(arguments, function(l, r) { 
 
    return l + r; 
 
    }, 0); 
 

 
    var ret = add.bind(null, sum); 
 
    ret.add = ret; 
 

 
    ret.value = ret.valueOf = Number.prototype.valueOf.bind(sum);  
 
    ret.toString = Number.prototype.toString.bind(sum); 
 

 
    return ret; 
 
} 
 

 
snippet.log(add(2,2,2)); 
 
snippet.log(add(2,2,2,2)); 
 
snippet.log(add(2)(2)(2)); 
 
snippet.log(add(2)(2)(2,2).value()); 
 
snippet.log(add(2,2)(2) + 2); 
 
snippet.log(add(2).add(2)); 
 
snippet.log(add(2,2,2).add(2).value()); 
 
snippet.log(add(2,2,2).add(2).add(2,2).value()); 
 

 
snippet.log(add(1, 2, 3)(4, 5).add(6, 7)(8).add(9, 10)); 
 
snippet.log(add(5,4)(3).add(2)(1) * 10);
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 --> 
 
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>


ci sono ancora due potenziali problemi con l'approccio di cui sopra, un minore e una un po 'meno minore:

  • Ci sono riferimenti di proprietà e definizioni di funzioni che vengono rieseguiti ogni volta che viene utilizzata la funzione add (incluso durante il concatenamento)
  • Se qualcuno sovrascrive l'identificatore add, causerebbe l'intera implementazione di rompere:

function add() { 
 
    var sum = Array.prototype.reduce.call(arguments, function(l, r) { 
 
    return l + r; 
 
    }, 0); 
 

 
    var ret = add.bind(null, sum); 
 
    ret.add = ret; 
 

 
    ret.value = ret.valueOf = Number.prototype.valueOf.bind(sum); 
 
    ret.toString = Number.prototype.toString.bind(sum); 
 

 
    return ret; 
 
} 
 

 
var myAdd = add; 
 
add = "boom!"; 
 
myAdd(1, 2, 3); // TypeError: add.bind is not a function

Entrambi questi si può rimediare con un'IIFE:

var add = (function() { 
 
    var reduce = Array.prototype.reduce, 
 
     np = Number.prototype, 
 
     valueOf = np.valueOf, 
 
     toString = np.toString, 
 
     plus = function (l, r) { return l + r; }; 
 

 
    return function add() { 
 
     var sum = reduce.call(arguments, plus, 0); 
 

 
     var ret = add.bind(null, sum); 
 
     ret.add = ret; 
 

 
     ret.value = ret.valueOf = valueOf.bind(sum);  
 
     ret.toString = toString.bind(sum); 
 

 
     return ret; 
 
    } 
 
})(); 
 

 
var myAdd = add; 
 
add = "U Can't Touch This"; // hammertime 
 

 
snippet.log(myAdd(1, 2, 3)(4, 5).add(6, 7)(8).add(9, 10)); 
 
snippet.log(myAdd(5,4)(3).add(2)(1) * 10);
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 --> 
 
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

+0

' sum.toString.bind (somma); '? Intelligente.Mi chiedo perché tu non abbia fatto la stessa cosa con 'valueOf' ... – Bergi

+0

@Bergi Perché richiederebbe un'operazione di boxing inutile, ma forse è solo un'ottimizzazione non necessaria, e' sum.toString.bind (sum) 'comporta anche il pugilato inutile . – JLRishe

4

Ho provato ad improvvisare con l'uso di this. Funziona per tutti i casi.

function add(){ 
    var sum = this instanceof Number?this: 0; 
    for(var i in arguments){ 
     sum += arguments[i]; 
    } 

    var ret = add.bind(sum); 
    ret.add = ret; 
    ret.value = ret.valueOf = function() { return sum; }; 

    ret.toString = sum.toString.bind(sum); 

    return ret; 
} 

JS-Fiddle

Problemi correlati