Quando sarebbe utile lo scope dinamico?

8

Con lo scope dinamico, un callee può accedere alle variabili del suo chiamante. Codice Pseudo C:

void foo()
{
    print(x);
}

void bar()
{
    int x = 42;
    foo();
}

Dal momento che non ho mai programmato in una lingua che supporta lo scoping dinamico, mi chiedo quali sarebbero alcuni casi di utilizzo del mondo reale per l'ambito dinamico.

    
posta fredoverflow 23.08.2011 - 20:17
fonte

6 risposte

15

Un'applicazione molto utile dell'ambito dinamico è per che passa i parametri contestuali senza dover aggiungere esplicitamente nuovi parametri a ogni funzione in uno stack di chiamate

Ad esempio, Clojure supporta lo scoping dinamico tramite binding , che può essere utilizzato per riassegnare temporaneamente il valore di *out* per la stampa. Se ricolleghi *out* , tutte le chiamate da stampare nell'ambito dinamico del collegamento verranno stampate sul nuovo flusso di output. Molto utile se, ad esempio, si desidera reindirizzare tutto l'output stampato su qualche tipo di registro di debug.

Esempio: nel codice qui sotto, la funzione do-stuff stamperà sull'output di debug piuttosto che sullo standard, ma nota che non ho bisogno di aggiungere un parametro di output a cose da fare per abilitare questo ....

(defn do-stuff [] 
  (do-other-stuff)
  (print "stuff done!"))

(binding [*out* my-debug-output-writer]
  (do-stuff))

Nota che i collegamenti di Clojure sono anche thread-local, quindi non hai un problema con l'uso simultaneo di questa funzionalità. Ciò rende i binding notevolmente più sicuri di (ab) usando le variabili globali per lo stesso scopo.

    
risposta data 23.08.2011 - 21:20
fonte
2

(Disclaimer: non ho mai programmato in un linguaggio di scoping dinamico)

L'ambito è molto più facile da implementare e potenzialmente più veloce. Con lo scope dinamico, è necessaria solo la tabella dei simboli (le variabili attualmente disponibili). Si legge solo da questa tabella dei simboli per tutto.

Immagina in Python la stessa funzione.

def bar():
    x = 42;
    foo(42)

def foo(x):
    print x

Quando chiamo bar, inserisco x nella tabella dei simboli. Quando chiamo foo, prendo la tabella dei simboli attualmente usata per la barra e la spingo in pila. Quindi chiamo foo, che ha passato x (probabilmente è stato inserito nella nuova tabella dei simboli per chiamare la funzione). Dopo aver chiuso la funzione, devo distruggere il nuovo scope e ripristinare quello vecchio.

Con lo scope dinamico, questo non è necessario. Ho solo bisogno di conoscere le istruzioni di cui ho bisogno per tornare a quando la funzione termina poiché non è necessario eseguire nulla sulla tabella dei simboli.

    
risposta data 23.08.2011 - 20:32
fonte
2

La gestione delle eccezioni nella maggior parte delle lingue utilizza l'ambito dinamico; quando si verifica un'eccezione, il controllo verrà trasferito al gestore più vicino sullo stack di attivazione (dinamico).

    
risposta data 12.02.2015 - 13:26
fonte
1

Non sicuro al 100% se si tratta di una corrispondenza esatta, ma penso che almeno si avvicini abbastanza in senso generale per mostrare dove può essere utile per infrangere o cambiare le regole di scoping.

Il linguaggio Ruby viene fornito con la classe di template ERB, che per esempio in Rails viene utilizzato per generare file html. Se lo usi, assomiglia a questo:

require 'erb'

x = 42
template = ERB.new <<-EOF
  The value of x is: <%= x %>
EOF
puts template.result(binding)

Il binding consente l'accesso alle variabili locali alla chiamata al metodo ERB, in modo che possa accedervi e utilizzarli per riempire il modello. (Il codice tra gli EOF è una stringa, la parte tra <% =% > valutata come codice Ruby da ERB e dichiara il proprio ambito come una funzione)

Un esempio di Rails lo dimostra ancora meglio. In un controller articolo, potresti trovare qualcosa di simile a questo:

def index
  @articles = Article.all

  respond_to do |format|
    format.html
    format.xml  { render :xml => @posts }
  end
end

Il file index.html.erb potrebbe quindi utilizzare la variabile locale @articles come questa (in questo caso la creazione di un oggetto ERB e il binding sono gestiti dal framework Rails, quindi non lo vedi qui) :

<ul>
<% @articles.each do |article| %>
  <li><%= article.name</li>
<% end %>
</ul>

Quindi usando una variabile di binding, Ruby permette di eseguire uno e lo stesso codice template in diversi contesti.

La classe ERB è solo un esempio di utilizzo. Ruby consente in generale di ottenere lo stato effettivo di esecuzione con binding di variabili e metodi mediante l'associazione Kernel #, che è molto utile in qualsiasi contesto in cui si desidera valutare un metodo in un contesto diverso o si desidera mantenere un contesto per un uso successivo.

    
risposta data 23.08.2011 - 20:41
fonte
1

I casi d'uso per lo scope dinamico sono IHMO come per le variabili globali. Lo scope dinamico evita alcuni dei problemi con le variabili globali consentendo un aggiornamento più controllato della variabile.

Alcuni casi d'uso a cui posso pensare:

  • Registrazione : ha più senso raggruppare i registri per contesto runtime che da un contesto lessicale. È possibile associare dinamicamente il logger a un gestore richieste in modo che tutte le funzioni richiamate da lì condividano lo stesso "requestID" nelle voci del registro.
  • Reindirizzamento dell'output
  • Allocatori di risorse : per tenere traccia delle risorse allocate per una particolare azione / richiesta.
  • Gestione delle eccezioni .
  • Comportamento contestuale in generale, ad esempio Emacs che modifica la mappatura delle chiavi in determinati contesti .

Ovviamente lo scope dinamico non è "assolutamente necessario", ma allevia l'onere di dover passare i dati di trasporto lungo la catena di chiamate o di dover implementare proxy globali intelligenti e gestori di contesto.

Ma ancora una volta, l'ambito dinamico è utile quando si tratta di tipi di elementi che sono trattati molte volte come globali (registrazione, output, allocazione / gestione delle risorse, ecc.).

    
risposta data 15.12.2016 - 10:18
fonte
-1

Non ce ne sono praticamente. Spero che tu, ad esempio, non usi mai la stessa variabile due volte.

void foo() {
    print_int(x);
}
void bar() {
    print_string(x);
}

Ora come chiami sia foo che bar dalla stessa funzione?

Questo è in effetti simile al semplice utilizzo di una variabile globale, ed è negativo per tutti gli stessi motivi.

    
risposta data 23.08.2011 - 20:37
fonte

Leggi altre domande sui tag