Domanda

Non credo di essermi ancora crogiolato al curry. Capisco cosa fa e come farlo. Non riesco proprio a pensare a una situazione che la userei.

Dove stai usando il curry in JavaScript (o dove lo usano le principali librerie)? Manipolazione DOM o esempi generali di sviluppo di applicazioni sono benvenuti.

Una delle risposte menziona l'animazione. Funzioni come slideUp , fadeIn accettano un elemento come argomento e sono normalmente una funzione curry che restituisce la funzione di ordine superiore con la funzione di animazione & # 8220; predefinita & # 8221; built-in. Perché è meglio che semplicemente applicare la funzione di rialzo con alcune impostazioni predefinite?

Ci sono degli svantaggi nell'usarlo?

Come richiesto qui ci sono alcune buone risorse sul curriculum JavaScript:

Aggiungerò altro man mano che spuntano nei commenti.


Quindi, secondo le risposte, il curry e l'applicazione parziale in generale sono tecniche di convenienza.

Se stai frequentemente & # 8220; raffinazione & # 8221; una funzione di alto livello chiamandola con la stessa configurazione, puoi curry (o utilizzare Resig) una funzione di livello superiore per creare metodi di supporto semplici e concisi.

È stato utile?

Soluzione

@Hank Gay

In risposta al commento di EmbiggensTheMind:

Non riesco a pensare a un'istanza in cui currying — di per sé — è utile in JavaScript ; è una tecnica per convertire le chiamate di funzione con più argomenti in catene di chiamate di funzione con un singolo argomento per ogni chiamata, ma JavaScript supporta più argomenti in una singola chiamata di funzione.

In JavaScript — e presumo che la maggior parte delle altre lingue effettive (non il calcolo lambda) — sia comunemente associato con un'applicazione parziale. John Resig lo spiega meglio , ma l'essenza è che abbia un po 'di logica che verrà applicato a due o più argomenti e si conoscono i valori solo per alcuni di tali argomenti.

È possibile utilizzare l'applicazione / curry parziale per correggere quei valori noti e restituire una funzione che accetta solo gli incogniti, da invocare in seguito quando si hanno effettivamente i valori che si desidera passare. Ciò fornisce un modo elegante per evitare di ripetersi quando avresti chiamato più volte gli stessi built-in JavaScript con gli stessi valori tranne uno. Per rubare l'esempio di John:

String.prototype.csv = String.prototype.split.partial(/,\s*/);
var results = "John, Resig, Boston".csv();
alert( (results[1] == "Resig") + " The text values were split properly" );

Altri suggerimenti

Ecco un uso interessante e pratico del curry in JavaScript che utilizza chiusure :

function converter(toUnit, factor, offset, input) {
    offset = offset || 0;
    return [((offset + input) * factor).toFixed(2), toUnit].join(" ");
}

var milesToKm = converter.curry('km', 1.60936, undefined);
var poundsToKg = converter.curry('kg', 0.45460, undefined);
var farenheitToCelsius = converter.curry('degrees C', 0.5556, -32);

milesToKm(10);            // returns "16.09 km"
poundsToKg(2.5);          // returns "1.14 kg"
farenheitToCelsius(98);   // returns "36.67 degrees C"

Questo si basa su un'estensione curry di Function , anche se come puoi vedere usa solo apply (niente di troppo fantasia):

Function.prototype.curry = function() {
    if (arguments.length < 1) {
        return this; //nothing to curry with - return function
    }
    var __method = this;
    var args = toArray(arguments);
    return function() {
        return __method.apply(this, args.concat([].slice.apply(null, arguments)));
    }
}

Ho trovato le funzioni che assomigliano al functools.partial di Python più utili in JavaScript:

function partial(fn) {
  return partialWithScope.apply(this,
    Array.prototype.concat.apply([fn, this],
      Array.prototype.slice.call(arguments, 1)));
}

function partialWithScope(fn, scope) {
  var args = Array.prototype.slice.call(arguments, 2);
  return function() {
    return fn.apply(scope, Array.prototype.concat.apply(args, arguments));
  };
}

Perché dovresti usarlo? Una situazione comune in cui si desidera utilizzarlo è quando si desidera associare this in una funzione a un valore:

var callback = partialWithScope(Object.function, obj);

Ora quando viene chiamato il callback, this punta a obj . Ciò è utile in situazioni di eventi o per risparmiare un po 'di spazio perché in genere riduce il codice.

Currying è simile al parziale con la differenza che la funzione restituita dal curry accetta solo un argomento (per quanto io lo capisca).

Accordarsi con Hank Gay - È estremamente utile in alcuni veri linguaggi di programmazione funzionale - perché è una parte necessaria. Ad esempio, in Haskell semplicemente non è possibile applicare più parametri a una funzione, non è possibile farlo nella pura programmazione funzionale. Prendi un parametro alla volta e costruisci la tua funzione. In JavaScript è semplicemente superfluo, nonostante esempi inventati come " converter " ;. Ecco lo stesso codice del convertitore, senza la necessità di curry:

var converter = function(ratio, symbol, input) {
    return (input*ratio).toFixed(2) + " " + symbol;
}

var kilosToPoundsRatio = 2.2;
var litersToUKPintsRatio = 1.75;
var litersToUSPintsRatio = 1.98;
var milesToKilometersRatio = 1.62;

converter(kilosToPoundsRatio, "lbs", 4); //8.80 lbs
converter(litersToUKPintsRatio, "imperial pints", 2.4); //4.20 imperial pints
converter(litersToUSPintsRatio, "US pints", 2.4); //4.75 US pints
converter(milesToKilometersRatio, "km", 34); //55.08 km

Vorrei tanto che Douglas Crockford, in "JavaScript: The Good Parts", avesse menzionato la storia e l'uso effettivo del curry piuttosto che le sue osservazioni sbrigative. Per molto tempo dopo averlo letto, sono rimasto sbalordito, fino a quando non ho studiato la programmazione funzionale e ho capito da dove veniva.

Dopo aver riflettuto un po 'di più, credo che esista un caso d'uso valido per il curriculum in JavaScript: se stai cercando di scrivere usando tecniche di programmazione funzionale pura usando JavaScript. Sembra un caso d'uso raro però.

Ecco un esempio.

Sto strumentando un sacco di campi con JQuery in modo da poter vedere cosa stanno facendo gli utenti. Il codice è simile al seguente:

$('#foo').focus(trackActivity);
$('#foo').blur(trackActivity);
$('#bar').focus(trackActivity);
$('#bar').blur(trackActivity);

(Per gli utenti non JQuery, sto dicendo che ogni volta che un paio di campi ottengono o perdono il focus, voglio che venga chiamata la funzione trackActivity (). Potrei anche usare una funzione anonima, ma avrei per duplicarlo 4 volte, quindi l'ho estratto e chiamato.)

Ora si scopre che uno di quei campi deve essere gestito in modo diverso. Vorrei poter trasmettere un parametro su una di quelle chiamate da passare alla nostra infrastruttura di monitoraggio. Con il curry, posso.

Non è magia o altro ... solo una piacevole scorciatoia per funzioni anonime.

parziale (avviso, " FOO! ") equivale a function(){alert("FOO!");}

parziale (Math.max, 0) corrisponde alla funzione (x) {return Math.max (0, x);}

Le chiamate alla terminologia parziale ( MochiKit . Penso che alcune altre librerie forniscano alle funzioni un metodo .curry che fa lo stesso cosa) sembrano leggermente più belli e meno rumorosi delle funzioni anonime.

Per quanto riguarda le biblioteche che lo usano, c'è sempre Funzionale .

Quando è utile in JS? Probabilmente le stesse volte è utile in altre lingue moderne, ma l'unica volta che riesco a vedermi usarlo è in congiunzione con un'applicazione parziale.

Direi che, molto probabilmente, tutta la libreria di animazione in JS sta usando il curry. Piuttosto che dover passare per ogni chiamata un insieme di elementi interessati e una funzione, descrivendo come dovrebbe comportarsi l'elemento, a una funzione di ordine superiore che garantirà tutte le cose di temporizzazione, è generalmente più facile per il cliente rilasciare, come API pubblica alcuni funziona come " slideUp " ;, " fadeIn " che accetta solo elementi come argomenti e che sono solo alcune funzioni al curry che restituiscono la funzione di ordine superiore con la funzione di animazione "quotata di default" built-in.

Le funzioni JavaScript sono chiamate lamda in un altro linguaggio funzionale. Può essere utilizzato per comporre un nuovo api (funzione più potente o complext) in base al semplice input di un altro sviluppatore. Il curry è solo una delle tecniche. Puoi usarlo per creare un'API semplificata per chiamare un'API complessa. Se sei lo sviluppatore che utilizza l'API semplificato (ad esempio, usi jQuery per fare semplici manipolazioni), non è necessario usare curry. Ma se vuoi creare l'API semplificata, il curry è tuo amico. Devi scrivere un framework javascript (come jQuery, mootools) o una libreria, quindi puoi apprezzarne il potere. Ho scritto una funzione di curry avanzata, all'indirizzo http://blog.semanticsworks.com /2011/03/enhanced-curry-method.html . Non è necessario il metodo curry per eseguire il curry, è solo utile per eseguire il curry, ma puoi sempre farlo manualmente scrivendo una funzione A () {} per restituire un'altra funzione B () {}. Per renderlo più interessante, utilizzare la funzione B () per restituire un'altra funzione C ().

Conosco il suo vecchio thread ma dovrò mostrare come viene utilizzato nelle librerie javascript:

Userò la libreria lodash.js per descrivere concretamente questi concetti.

Esempio:

var fn = function(a,b,c){ 
return a+b+c+(this.greet || ‘'); 
}

Applicazione parziale:

var partialFnA = _.partial(fn, 1,3);

Currying:

var curriedFn = _.curry(fn);

Rilegatura:

var boundFn = _.bind(fn,object,1,3 );//object= {greet: ’!'}

utilizzo:

curriedFn(1)(3)(5); // gives 9 
or 
curriedFn(1,3)(5); // gives 9 
or 
curriedFn(1)(_,3)(2); //gives 9


partialFnA(5); //gives 9

boundFn(5); //gives 9!

differenza:

dopo il curry otteniamo una nuova funzione senza parametri pre associati.

dopo un'applicazione parziale otteniamo una funzione che è associata ad alcuni parametri prebound.

in associazione possiamo associare un contesto che verrà utilizzato per sostituire & # 8216; questo & # 8217; se non viene associato il valore predefinito di nessuna funzione sarà l'ambito della finestra.

Avviso: non è necessario reinventare la ruota. L'applicazione parziale / rilegatura / curry sono molto correlate. Puoi vedere la differenza sopra. Usa questo significato ovunque e le persone riconosceranno ciò che stai facendo senza problemi di comprensione e dovrai usare meno codice.

Sono d'accordo che a volte ti piacerebbe far rotolare la palla creando una pseudo-funzione che avrà sempre il valore del primo argomento compilato. Fortunatamente, mi sono imbattuto in una nuova libreria JavaScript chiamata jPaq (h < a href = "http://jpaq.org/" rel = "nofollow"> ttp: //jpaq.org/ ) che fornisce questa funzionalità. La cosa migliore della libreria è il fatto che puoi scaricare la tua build che contiene solo il codice di cui avrai bisogno.

Ho appena scritto un esempio di jPaq che mostra alcune interessanti applicazioni della funzione curry. Dai un'occhiata qui: Currying Up String Functions

Volevo solo aggiungere alcune risorse per Functional.js:

Conferenza / conferenza che spiega alcune applicazioni http://www.youtube.com/watch?v=HAcN3JyQoyY

Libreria Functional.js aggiornata: https://github.com/loop-recur/FunctionalJS Alcuni simpatici aiutanti (mi dispiace nuovo qui, nessuna reputazione: p): / Loop-RECUR / PreludeJS

Ho usato questa libreria molto di recente per ridurre la ripetizione in una libreria helper di client IRC js. È roba fantastica: aiuta davvero a ripulire e semplificare il codice.

Inoltre, se le prestazioni diventano un problema (ma questa libreria è piuttosto leggera), è facile riscrivere semplicemente usando una funzione nativa.

Puoi utilizzare il bind nativo per una soluzione rapida a una riga

function clampAngle(min, max, angle) {
    var result, delta;
    delta = max - min;
    result = (angle - min) % delta;
    if (result < 0) {
        result += delta;
    }
    return min + result;
};

var clamp0To360 = clampAngle.bind(null, 0, 360);

console.log(clamp0To360(405)) // 45

Un'altra pugnalata, dal lavorare con le promesse.

(Dichiarazione di non responsabilità: JS noob, proveniente dal mondo Python. Anche lì, curry non è molto utilizzato, ma può tornare utile in alcune occasioni. Quindi ho paralizzato il curry funzione - vedi link)

In primo luogo, sto iniziando con una chiamata Ajax. Ho qualche elaborazione specifica da fare in caso di successo, ma in caso di fallimento, voglio solo dare all'utente il feedback che chiamare qualcosa ha provocato qualche errore . Nel mio codice attuale, visualizzo il feedback degli errori in un pannello di bootstrap, ma sto solo usando la registrazione qui.

Ho modificato il mio URL live per non riuscire.

function ajax_batch(e){
    var url = $(e.target).data("url");

    //induce error
    url = "x" + url;

    var promise_details = $.ajax(
        url,
        {
            headers: { Accept : "application/json" },
            // accepts : "application/json",
            beforeSend: function (request) {
                if (!this.crossDomain) {
                    request.setRequestHeader("X-CSRFToken", csrf_token);
                }
        },
        dataType : "json",
        type : "POST"}
    );
    promise_details.then(notify_batch_success, fail_status_specific_to_batch);
}

Ora, qui per dire all'utente che un batch non è riuscito, devo scrivere quelle informazioni nel gestore degli errori, perché tutto ciò che sta ottenendo è una risposta dal server.

Ho ancora le informazioni disponibili solo al momento della codifica - nel mio caso ho un numero di possibili batch, ma non so quale sia fallito w.o. analizzare la risposta del server sull'URL non riuscito.

function fail_status_specific_to_batch(d){
    console.log("bad batch run, dude");
    console.log("response.status:" + d.status);
}

Facciamolo. L'output della console è:

console:

esecuzione batch errata, amico utility.js (linea 109) Response.Status: 404

Ora, cambiamo un po 'le cose e usiamo un gestore di errori generici riutilizzabile, ma anche uno che è curry in fase di esecuzione con sia il contesto di chiamata al momento del codice noto sia il tempo di esecuzione informazioni disponibili dall'evento.

    ... rest is as before...
    var target = $(e.target).text();
    var context = {"user_msg": "bad batch run, dude.  you were calling :" + target};
    var contexted_fail_notification = curry(generic_fail, context); 

    promise_details.then(notify_batch_success, contexted_fail_notification);
}

function generic_fail(context, d){
    console.log(context);
    console.log("response.status:" + d.status);
}

function curry(fn) {
     var slice = Array.prototype.slice,
        stored_args = slice.call(arguments, 1);
     return function () {
        var new_args = slice.call(arguments),
              args = stored_args.concat(new_args);
        return fn.apply(null, args);
     };
}

console:

Oggetto {user_msg = " esecuzione batch errata, amico. stavi chiamando: esegui ACL ora "} utility.js (linea 117) Response.Status: 404 utility.js (linea 118)

Più in generale, dato quanto sia diffuso l'utilizzo del callback in JS, il curry sembra uno strumento abbastanza utile da avere.

https://javascriptweblog.wordpress.com / 2010/04/05 / curry-cucina-up-saporiti-funzioni / http: //www.drdobbs com / open-source / currying-e-parziali funzioni-in-javasc / 231001821? pgno = 2

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top