Va bene usare la digitazione dinamica per ridurre la quantità di variabili nell'ambito?

5

Spesso, quando sto inizializzando qualcosa devo usare una variabile temporanea, ad esempio:

file_str = "path/to/file"
file_file = open(file)

o

regexp_parts = ['foo', 'bar']
regexp       = new RegExp( regexp_parts.join('|') )

Tuttavia, mi piace ridurre l'ambito delle mie variabili al più piccolo ambito possibile in modo che ci siano meno punti in cui possono essere (male) utilizzati. Ad esempio, provo a usare for(var i ...) in C ++, quindi la variabile di loop è confinata al corpo del ciclo.

In questi casi di inizializzazione, se sto usando un linguaggio dinamico, sono spesso tentato di riutilizzare la stessa variabile per impedire che il valore iniziale (e ora inutile) venga utilizzato da ultimo in la funzione.

file = "path/to/file"
file = open(file)

regexp = ['...', '...']
regexp = new RegExp( regexp.join('|') )

L'idea è che riducendo il numero di variabili in ambito I riduca le possibilità di utilizzarle impropriamente . Tuttavia questo a volte rende i nomi delle variabili un po 'strani, come nel primo esempio, dove "file" si riferisce a un "nomefile".

Penso che forse sarebbe un problema se potessi usare gli ambiti non annidati

begin scope1
   filename = ...
begin scope2
   file = open(filename)
end scope1
   //use file here
   //can't use filename on accident
end scope2

ma non riesco a pensare a nessun linguaggio di programmazione che supporti questo.

Quali regole del pollice dovrei usare in questa situazione?

  • Quando è meglio riutilizzare la variabile?
  • Quando è meglio creare una variabile extra?
  • In quali altri modi risolviamo questo problema di ambito?
posta hugomg 11.11.2011 - 13:35
fonte

5 risposte

23

Risposta breve: No, non riutilizzare le variabili.

The idea is that by reducing the number of variables in scope I reduce the chances to misuse them.

Sembra più stai utilizzando le variabili in modo errato in modo da poterne ridurre il numero .

Le variabili sono a buon mercato - usa quante ne hai bisogno. Riusare le variabili per rappresentare cose diverse in tempi diversi sarà non a buon mercato nel lungo periodo; renderà il tuo codice più difficile da capire, e tu o qualcuno dopo di te avrai molte più probabilità di creare bug cercando di mantenere tale codice.

What other ways do we solve this scope problem?

Il modo migliore è scegliere nomi descrittivi per le variabili e non provare mai a riutilizzare una variabile per due o più concetti diversi. Il problema non è scope , il problema (per quanto posso dire) sta accidentalmente usando una variabile per la cosa sbagliata. I nomi descrittivi aiutano in questo senso.

È sempre possibile impostare una variabile che non è più necessaria per un valore innocuo o non valido. Questo è comune nelle lingue con gestione manuale delle risorse, in cui è possibile liberare / eliminare / rilasciare i puntatori una volta terminato. Dopo averlo fatto, è spesso una buona idea impostarli su zero per impedire l'uso futuro di quel puntatore. Puoi fare cose simili con altri tipi, come impostare un contatore di loop su -1, ma in pratica non è solitamente necessario.

Inoltre, non scrivere funzioni / metodi così grandi da avere difficoltà a tenere traccia di tutte le variabili che stai utilizzando. Se hai decine di variabili in giro, c'è una buona possibilità che il codice sia troppo complicato; scomposizione in compiti indipendenti più piccoli.

    
risposta data 11.11.2011 - 13:49
fonte
9

Credo che avere una variabile con significati multipli sia molto più confusa di avere molte variabili nell'ambito.

In ogni caso, se ti senti sopraffatto dal numero di variabili in ambito, è probabile che il tuo metodo / funzione stia facendo troppo e dovrebbe essere diviso.

Quindi la risposta è: No, non riutilizzare le variabili, in particolare non in questo modo.

    
risposta data 11.11.2011 - 15:04
fonte
4

reducing the number of variables in scope I reduce the chances to misuse them

La mia esperienza dice principalmente l'altro modo.

  • L'uso multiplo (che alla fine diventa abuso) costringe le persone a generare più significati e incostanza della variabile. Questo è generalmente cattivo. Uno degli esempi classici (i tuoi esempi sono ancora piuttosto una partenza) è questo:
int err 

err = some_function1()  
//... some other code
if(new_condition)
   err = some_fuction2(); 

return err;

Comprendi che quando l'err restituisce ha potenzialmente il valore di ritorno err a seconda del contesto di new_condition , questo potrebbe funzionare. Tuttavia, quando qualcuno modifica il codice, attorno a una delle due funzioni, si verifica un disastro.

  • l'altra incoerenza che vedo nella tua idea è che mentre il valore di una variabile non viene modificato o utilizzato per qualche tempo, deve essere mantenuto fino alla fine dell'ambito. Quindi nel tuo caso precedente -

file = "path/to/file"
file = open(file)
// 
if(file != NULL) 

L'ultima riga in if ha un significato ambiguo. Quindi ora non so a cosa ti riferisci.

idealmente mi piacerebbe trattarli file.path e file.handle che aiuta a rimuovere l'ingombro di alto livello pur mantenendo la sanità mentale del lavoro.

  • Ultimo ma più importante è che la maggior parte delle variabili vengono abusate solo se non hanno un nome appropriato e se hanno un nome strong quasi non si sbagliano. Questo non sembra ovvio per molti - ma quando vedi il codice evolversi per alcuni anni molti sarebbero d'accordo.
risposta data 11.11.2011 - 14:13
fonte
1

Sei del tutto corretto per limitare l'ambito delle variabili se puoi. È preferibile utilizzare semplicemente le funzioni brevi, ma se si dispone di più variabili utilizzate solo per poche righe, può essere opportuno creare un ambito attorno ad esse. Tuttavia, è un po 'brutto nella maggior parte delle lingue che conosco, e i benefici sono relativamente piccoli se segui i consigli di seguito, quindi la maggior parte delle persone non si preoccupa il più delle volte.

Tuttavia, dovresti assolutamente:

  1. Se hai una variabile una tantum, dai un nome più lungo che difficilmente useresti di nuovo accidentalmente, ad esempio:

    path_to_config_file = "blah/blah/blah";
    cfg_fileh = open path_to_config_file;
    
  2. Sempre, sempre, evitare di riutilizzare le variabili (a parte l'uso della stessa variabile in più iterazioni di un ciclo):

    // Do not do this:
    val = get_value_from_user();
    val = val*100; // Convery from cm to m
    val = normalise_val_for_calc(val);
    
    // Instead try this:
    len_cm = get_value_from_user();
    len_m = len_cm * 100;
    len_normalised = normalise_val_for_calc(len_m);
    

Ho avuto, molti, molti più errori nel fare il n. 1 e dimenticare quale valore individuale era quello che dal riutilizzo accidentale di una variabile più tardi in una funzione

Trovo che questo sia più importante del limite della portata, certamente non una buona soluzione!

Penso che la maggior parte delle lingue che usano scope (C e C ++, e penso C #, perl, python, ecc., anche se non lo so per certo) ti permettono di aprire un nuovo blocco. È particolarmente utile in C ++ in cui una variabile che esce dall'ambito cancella automaticamente qualsiasi memoria o filehandle che memorizza (RAII).

Per esempio, in C ++:

 int main() {
      int len_m;
      {
           // long calculation with many temporary variables
           // consider moving this to a separate function
           // but it's ok here under some circumstances
           len_m = blah
      }
      // temporary variables have gone away
      // do stuff with len_m
 }

(Non è perfetto perché devi dichiarare la variabile prima del blocco e inizializzarla durante, ma a volte è utile.Alcuni linguaggi di scripting ti permetteranno invece di usare una funzione ad-hoc, len_m = { /* calculation */ } ...)

    
risposta data 17.11.2011 - 18:03
fonte
0

Se stai utilizzando molte variabili in un ambito, è probabilmente un'indicazione che sta accadendo troppo in questo ambito.

Prendi questo:

regexp_parts = ['foo', 'bar']
regexp       = new RegExp( regexp_parts.join('|') )

Fai questo (assumendo JS qui):

function matchAnyOf() {
    var parts = [];
    for (var i = 0; i < arguments.length; i++)
        parts = parts.concat(arguments[i]);
    return new RegExp(parts.join('|'));
}
//And any of these self-explanatory oneliners will do what you want
regexp = matchAnyOf('foo', 'bar');
regexp = matchAnyOf(['foo', 'bar']);
regexp = matchAnyOf('foo|bar');//granted, this won't work if you choose to escape '|' in matchAnyOf (which probably makes sense)

Prima disponeva di una variabile helper, che ingombrava il tuo scope, ora hai una funzione isolata e riutilizzabile (nota come parts non richiede più un prefisso in contrasto con regexp_parts , perché il contesto è ora molto chiaro) .

Presumo che una cosa del genere sia possibile per l'esempio di file, anche se sarebbe necessario più contesto, perché, come direi, direi che una variabile il cui unico scopo è memorizzare un letterale che viene usato esattamente una volta è un po ' un eccessivo;)

    
risposta data 17.11.2011 - 20:54
fonte

Leggi altre domande sui tag