Assegnazione di valori di default alle variabili e trattamento dell'accesso a una variabile non definita come errore

3

Avendo giocato con diversi linguaggi di scripting ed essendo un po 'un linguista, sembra esserci un modo per dividere le lingue digitate dinamicamente in due gruppi: le lingue che danno alle variabili un valore predefinito e le lingue che trattano l'accesso a un up-to -now variabile inutilizzata un errore.

Ad esempio, considera questo codice:

print(hi)
  • In Lua, stampa nil
  • In PHP (con punto e virgola e $ ), non stampa nulla
  • In Perl (con punto e virgola e $ ), non stampa nulla (ma utilizzando say genera un errore)

Tuttavia,

  • In Python getta un NameError
  • In Ruby, lancia un NameError (sembra che Ruby abbia cancellato il nome di Python per esso)
  • In Javascript, genera un ReferenceError
  • In Lisp, si verifica un errore che mi porta a un debugger (sto solo imparando Lisp ora, quindi non so come chiamarlo)

Inoltre, queste lingue si comportano diversamente quando si indicizza una mappa hash, un'operazione simile, e rientrano di nuovo in due campi (e quando dico "niente" intendo la rappresentazione di niente di quella lingua):

  • Lua non restituisce nulla
  • PHP non restituisce nulla
  • Ruby non restituisce nulla
  • Perl non restituisce nulla
  • Lisp non restituisce nulla
  • Javascript non restituisce nulla

Ma

  • Python lancia un KeyError

Quindi mi sto chiedendo quali sono i vantaggi e gli svantaggi comparativi di un linguaggio per rendere l'accesso a una variabile indefinita un errore o semplicemente restituire un valore predefinito, e anche quali decisioni porterebbero un disegnatore linguistico a scegliere un percorso o l'altro.

    
posta Seth Carnegie 13.03.2012 - 21:10
fonte

2 risposte

3

Bene, una cosa da tenere a mente è che è più difficile eseguire il debug di una variabile errata o di un errore simile se non si ha un errore. Soprattutto se devi dichiarare variabili in qualche modo (come con define o var ), è probabile che l'uso di una variabile non dichiarata sia un errore, quindi segnalarlo come tale è probabilmente la cosa giusta da fare.

Tuttavia, ci sono anche momenti in cui avere un valore di undefined ha anche senso. Ad esempio, in JavaScript l'accesso a un argomento non passato è non un errore:

(function (foo) {
  alert(foo)
})()// alerts 'undefined'

Anche questo ha senso - mentre una variabile globale è qualcosa che il programmatore deve definire, un argomento può essere qualsiasi cosa passi qualcun altro nella funzione. Ora, questi casi non sono al 100% analoghi, ma è qualcosa da tenere a mente.

Per quanto riguarda gli hash è una storia diversa. A seconda della lingua, ci saranno molte volte in cui hai a che fare con un hash che non è il tuo. Penso che sia del tutto ragionevole aspettarsi che un hash contenga solo alcune delle chiavi che devono essere passate come argomento per una fucilazione, per esempio, quindi avere un accesso alle chiavi non nell'hash ha senso. Di nuovo prendendo in prestito da JavaScript, questo ci permetterebbe di scrivere codice come:

function (options) {
  var color = options.color || "white",
      font  = options.font  || "palatino"
  ...
}

Dato che ci sono posti in cui è assolutamente ragionevole accedere a una chiave che non è stata impostata, penso che farla restituire un valore è meglio che segnalare un errore. Dal momento che il programmatore non ha dovuto definire l'hash stesso - a differenza delle variabili locali - questo è più simile agli argomenti di una funzione rispetto alle variabili locali.

Si noti inoltre che alcune di quelle lingue hanno dei meccanismi per gestire le chiavi mancanti in un hash in un modo speciale. Si consiglia inoltre di fornire un meccanismo per consentire al programmatore di specificare come vengono gestiti, in modo che se non gli piace il comportamento predefinito, può cambiarlo.

Quindi, in sintesi: penso che l'uso di una variabile non dichiarata dovrebbe essere un errore durante l'accesso a un campo non dichiarato in un hash non dovrebbe .

    
risposta data 13.03.2012 - 21:42
fonte
1

Se stai cercando di decidere cosa dovresti fare in una lingua che stai progettando, ti suggerirei di fornire moduli sintattici che i programmatori possono usare per indicare varie aspettative. Come esempio ipotetico (prendendo in prestito concetti da alcune lingue):

  • lascia x = someValue;
    Crea un nuovo identificatore di sola lettura x e si aspetti che qualsiasi codice che possa mai vedere il nuovo identificatore non sarà mai in grado di vedere alcun identificatore che esisteva in precedenza. Dovrebbe essere accettabile che x esista già come nome, anche nell'attuale ambito, a condizione che tutto ciò che può vedere la nuova definizione lo faccia sempre.

  • dichiara x: someType;
    Crea una nuova variabile chiamata x, che deve essere scritta prima che possa essere utilizzata. L'identificatore non deve essere dichiarato altrove nello stesso ambito.

  • def x: someType;
    Crea una nuova variabile chiamata x e assegna ad essa il valore predefinito associato a someType. L'identificatore non deve essere dichiarato altrove nello stesso ambito.

  • def x: someType = initialValue;
    Crea una nuova variabile chiamata x e assegna ad essa il valore iniziale specificato. L'identificatore non deve essere dichiarato altrove nello stesso ambito.

  • x = someValue;
    Cambia il valore associato a una variabile creata usando declare o def.

Per le lingue che consentono al codice di creare e manipolare variabili in contesti di esecuzione esterni (gli esempi sottostanti presuppongono che tali variabili siano tipicamente imprecise):

  • crea x = someValue;
    Richiede che nessuna variabile x esista nello scope esterno e ne crei una nuova. Dovrebbe trap se la variabile esiste già.

  • import x (default = someValue)
    Indica che nell'ambito dello scope attuale, x dovrebbe fare riferimento a un identificatore di ambito esterno; se nessuno esiste, creane uno con il valore predefinito indicato.

  • importa x Indica che nell'ambito dello scope attuale, x dovrebbe fare riferimento a un identificatore di ambito esterno, che deve già esistere (trap se non lo fa).

  • variableName! Rende il valore di una variabile in un ambito esterno con il nome indicato, senza dover rendere questo nome "direttamente" accessibile in tutto lo scope attuale; trap se non esiste un tale nome di ambito esterno.

  • nomeVariabile? | valore predefinito Rende il valore di una variabile di ambito esterno se esiste, o il valore predefinito indicato se non lo è. Né crea né influenza in alcun modo la variabile in ogni caso.

Sebbene si tratti di un numero piuttosto elevato di moduli sintattici, l'uso di tali forme può aiutare a chiarire le intenzioni di un programmatore. Se un numero di punti all'interno di un metodo utilizzano ciascuno let q=something; , ma gli usi di q sono tutti nettamente distinti, che può essere più facile da leggere rispetto al fatto che il codice utilizza un nome diverso per ogni q o una singola dichiarazione per q in alto (ma non sapendo se è possibile che l'esecuzione salvi il codice che assegna il valore di q e poi legge il valore precedente q mantenuto. Avere sintassi separata per let , declare e def dovrebbero rendere più chiare le intenzioni del programma.

    
risposta data 01.08.2015 - 22:17
fonte

Leggi altre domande sui tag