Efficienza delle funzioni senza nome

0

La tendenza attuale sembra essere quella di usare la funzione di tipo lambda in lingue che possono assumere quella sintassi. È solo una cosa alla moda o è effettivamente più efficiente? ad esempio in javascript, puoi ottenere cose come

var x = ((aa,bb) => {
    ....
    aa((xyz) => {...}
    }).then((aa, bb) => {
    ...
    }).doit

In realtà è più efficiente per l'interprete javascript eseguire codice come questo o è più efficiente per il programmatore definire funzioni separate e chiamarle. Questo sta accadendo anche molto in C #.

    
posta cup 10.11.2016 - 06:58
fonte

3 risposte

6

Le funzioni anonime (senza nome) sono efficienti come le funzioni con nome. Tuttavia, le funzioni annidate hanno un leggero overhead se si riferiscono a variabili locali di una funzione di inclusione (tali funzioni annidate sono chiamate "chiusure"). Il modo più efficiente per implementare questo implica un puntatore indiretto aggiuntivo per ciascun accesso variabile non locale. Questo è equivalente al costo di accesso a un membro di istanza / campo di un oggetto. Pertanto, non è mai più efficiente sostituire una funzione anonima con un oggetto che fornisce la stessa funzionalità attraverso un metodo.

L'unico svantaggio notevole delle funzioni anonime è la debugabilità: una traccia dello stack non può riferirsi al nome della funzione anonima poiché non ne ha uno. Se la debugabilità è più importante della stretta correlazione del codice correlato, l'estrazione della funzione anonima può essere ragionevole.

Inoltre, le funzioni annidate non possono essere testate direttamente, indipendentemente dallo stato denominato / senza nome o di chiusura della funzione nidificata. Se la funzione nidificata ha un significato individuale (e non è solo parte del flusso di controllo ordinario - non testerebbe il corpo di un ciclo singolo individualmente), allora l'estrazione della funzione nidificata per renderla testabile può essere ragionevole.

Ma questi punti devono essere valutati caso per caso. Poiché le funzioni nidificate sono parte integrante di JavaScript e C #, non è ragionevole o addirittura impossibile farne a meno.

    
risposta data 10.11.2016 - 08:20
fonte
5

Questa non è una risposta diretta sull'efficacia delle funzioni anonime, ma più sullo stile di codifica, dal momento che ho l'impressione che questo sia ciò a cui realmente interessa l'OP. (Specialmente pre-montaggio, la domanda sembrava essere più "Penso che questo stile sia illeggibile, quindi la gente deve farlo per efficienza, ho ragione?")

C'è un equilibrio da trovare in tutto il codice tra verbosità e concisione. Sono contrari, ma nessuno dei due è buono; entrambi hanno degli svantaggi.

Il codice troppo prolisso è difficile da leggere perché la lettura del codice richiede tempo, tempo in cui si dimentica ciò che si legge in precedenza. Inoltre, il codice verboso tende a contenere informazioni aggiuntive (come il nome di una funzione non anonima) che compete con lo spazio nella memoria a breve termine con altre informazioni. Infine, può introdurre non-località, ad es. se introduci un'altra funzione che ora devi cercare per capire il codice.

Il codice troppo conciso è difficile da leggere perché devi tenere a mente molti dettagli senza avere un moniker a cui fare riferimento, e perché un sacco di codice conciso si accumula, rendendo più facile perdere il tuo posto nel codice durante la lettura. E richiede che tu capisca tutti i dettagli del codice, mentre una funzione estratta ben denominata può essere spesso considerata come una scatola nera.

Da qualche parte nel mezzo è il punto di massima leggibilità.

Ecco un esempio che riguarda solo i calcoli matematici. Diciamo che ho una funzione che calcola qualcosa di semplice, come la conversione da Celsius a Fahrenheit:

function celsiusToFahrenheit(celsius) {
  return celsius * (9.0/5.0) + 32;
}

Chiaro e semplice. C'è un fattore di scala e un offset. Alcuni puristi potrebbero obiettare che queste dovrebbero essere chiamate costanti, ma non vedo il punto; la funzione è un one-liner e lo scopo di questi numeri è ovvio e non cambierà.

Tuttavia, potrei scrivere in modo più dettagliato:

function celsiusToFahrenheit(celsius) {
  let scaledCelsius = celsius * (9.0/5.0);
  return scaledCelsius + 32;
}

Qui ho preso un risultato intermedio del calcolo e gli ho assegnato un nome. Mi sono spostato di più verso la verbosità. Ha migliorato la leggibilità? Ora hai più codice da leggere e ho dovuto inventare un nome aggiuntivo che devi ricordare mentre leggi il codice. Direi che la leggibilità è peggiorata.

Ecco un esempio diverso. Questa formula calcola un'approssimazione del percorso del segnale radio da un'antenna del trasmettitore a un punto nello spazio, sintonizzabile con un gruppo di coefficienti specificati dall'utente. Questa è una semplificazione della formula reale.

function pathloss(heightAntenna, heightDevice, distance, frequency, coefficients) {
  // formula uses log scale
  frequency = log10(frequency);
  heightAntenna = log10(heightAntenna);
  distance = log10(distance * 0.001);
  return
    (coefficients.A0 + coefficients.A1 * frequency - coefficients.A2 * heightAntenna) -
    ((coefficients.a0 * frequency - coefficients.a1) * heightDevice -
    coefficients.a2 * frequency + coefficients.a3) +
    coefficients.C0 * pow(frequency - coefficients.C1, 2) - coefficients.C2 +
    (coefficients.B0 - coefficients.B1 * frequency) * distance;
}

Penso che sarai d'accordo sul fatto che questo è completamente illeggibile. Qui, dividerlo in parti aiuta davvero.

function pathloss(heightAntenna, heightDevice, distance, frequency, coefficients) {
  // formula uses log scale
  frequency = log10(frequency);
  heightAntenna = log10(heightAntenna);
  distance = log10(distance * 0.001);
  // names are somewhat weird, that's the way they're written in the
  // reference book this is based on
  let a = (coefficients.a0 * frequency - coefficients.a1) * heightDevice -
    coefficients.a2 * frequency + coefficients.a3;
  let A = (coefficients.A0 + coefficients.A1 * frequency -
           coefficients.A2 * heightAntenna) - a;
  let C = coefficients.C0 * pow(frequency - coefficients.C1, 2) - coefficients.C2;
  let B = (coefficients.B0 - coefficients.B1 * frequency) * distance;
  return A + C + B;
}

Questo è ancora matematicamente impegnativo, ma almeno è suddiviso in blocchi, rendendo molto più facile tenere traccia di quali parti sono state aggiunte alle quali, qualcosa che ha richiesto un attento tracciamento delle parentesi nella versione originale.

Le funzioni anonime sono soggette allo stesso compromesso. Usarli migliora la località. L'uso eccessivo di questi elementi (in particolare l'annidamento) rende il flusso del programma difficile da tracciare. Sta a te trovare un equilibrio.

    
risposta data 10.11.2016 - 11:18
fonte
3

No, in genere è meno efficiente.

Quando si utilizza una funzione inline, si finisce per creare un oggetto funzione. La costruzione di quell'oggetto richiederà in genere alcune risorse. In effetti, a volte viene applicata un'ottimizzazione a javascript per spostare le funzioni inline quando possiamo dimostrare che sarebbe equivalente.

Il vantaggio consiste nell'evitare di scrivere sempre le funzioni una tantum, confrontiamo:

function foo() {
   let males = _.filter(people, (person) => person.male);
}

function is_male(person) {
     return person.male;
}

function foo() {
     let males = _.filter(people, is_male);
}

La sintassi della funzione inline è utile, perché mi consente di filtrare facilmente su qualsiasi criterio che desidero. Altrimenti, dovrei scrivere una funzione separata ogni volta che ho usato il filtro.

Naturalmente, alcune persone probabilmente lo prendono troppo lontano. A volte chiamare una funzione con nome è più chiaro, quindi scrivere una funzione inline, specialmente se la logica si complica. Ma correttamente usato, può aiutare la leggibilità (almeno, una volta che vi siete abituati)

    
risposta data 10.11.2016 - 08:16
fonte

Leggi altre domande sui tag