Funzione locali: variabile utilizzata in una sola funzione

2

In OOP, ambito statico indica che l'ambito è collegato alla classe e l'ambito dell'istanza è collegato a un'istanza specifica della classe.

Alcune lingue supportano locals statici , che consentono di mantenere un valore da una chiamata di una funzione a un'altra, comunque sempre in ambito statico.

Come puoi concludere da alcuni altri post di me qui, puoi dire che sono un grande fan di incapsulamento .

A volte uso una determinata istanza all'interno di una classe, solo all'interno di un metodo . Per esempio. una bandiera che indica se un certo metodo è già stato chiamato. La mia convenzione ( seguendo i buoni standard ) è mettere queste variabili subito prima della funzione, vicino al loro utilizzo. Idealmente, sarei in grado di posizionarli nell'ambito della funzione . Seguendo lo schema di denominazione dei locals statici, chiamiamo questo ambito ipotetico locals di funzioni .

Darien l'ha riformulato molto chiaramente in un commento:

Ah, so what you really want is something beyond even private protection, which is so closely protected that it is only visible to a single method within a single instance.

Ora so che potresti dire, seguendo il principio di responsabilità singola , questo metodo e le variabili associate dovrebbero molto probabilmente essere diviso in una classe separata. Per verificare questo - e ti sfido a fare lo stesso - ho esaminato alcuni file di codice casuali e ho trovato rapidamente i file sorgente (anche < 100 linee) dove poteva essere usata una "funzione locale".

In realtà, C # offre una soluzione a questo per uno scenario comune fuori dagli schemi. Proprietà implementate automaticamente nascondi il campo di supporto.

Questa idea potrebbe essere inverosimile, ma in qualche modo credo che avrebbe senso. Qualcuno sa di una lingua che supporta questo?

    
posta Steven Jeuris 07.06.2011 - 02:23
fonte

4 risposte

4

In Python è facile:

class TheClass(object):
    def __init__(self):
        def closure(times_called):
            def new_func():
                self.func = closure(times_called + 1)
                """ other stuff goes here """
                return times_called
            return new_func
        self.func = closure(1)

o = TheClass()

print o.func()
print o.func()
print o.func()

dà:

1
2
3

Normalmente, i metodi in Python sono funzioni che sono memorizzate (per riferimento) nella classe dict. Se una variabile "statica" fosse associata a tale funzione, tutte le istanze condivideranno la stessa variabile. La soluzione consiste nel dare a ogni istanza la propria versione del metodo sotto forma di un attributo di istanza, che viene memorizzato (per riferimento) nell'istanza dict, come qualsiasi altra variabile di istanza. La creazione di questo metodo ( new_func() ) è racchiusa in una chiusura che lega la variabile "function-static" ( times_called ). Ogni chiamata del metodo crea anche una nuova versione del metodo. È una cosa molto Lispy (Lispish? Lispsome?), Però. In Lisp, le transizioni di stato e di stato sono spesso avvolte in un modo simile.

Inoltre, il concetto di ambito statico non è inerente a OO. Ad esempio, in Python non esiste un ambito statico. Solo pacchetto, modulo, funzione e ambito di classe, e le classi sono anche oggetti. L'idea di un campo di applicazione statico, per me, è abbastanza non-OO. OO riguarda tutto lo stato e il polimorfismo. I messaggi polimorfici possono essere implementati senza classi, ma con Multi-Methods, Prototypes, Type Constructors a la Haskell, ... e le transizioni di stato e stato con le chiusure. Lo scoping lessicale è carino, comunque. Python non ce l'ha, ma invece le parole chiave global e nonlocal , che sono solo stampelle invece della cosa reale.

Ad ogni modo, non utilizzerei la soluzione sopra, ma inserisco lo stato in un attributo di istanza, in cui appartiene a Python. Non sono molto interessato al strong incapsulamento. Se lo fossi, non userei Python, dove non esiste alcun concetto di privacy. È una lingua olandese! Hanno spiagge per nudisti!

    
risposta data 07.06.2011 - 05:30
fonte
2

This idea might be far-fetched, but somehow I feel it would make sense. Does anybody know of any language which supports this?

Il link wiki che hai pubblicato menziona Perl e Ruby, ma aggiungerei PHP:

function test()
{
    static $a = 0;
    echo $a; // Prints a different number each time test() is called
    $a++;
}

Modifica: una possibile soluzione "falsa istanza-metodo-locale", sebbene rischi di utilizzo della memoria:

class Foo(){
    function test(){
        static $a = array();
        if(!array_key_exists($this,$a)){
            $a[$this] = 0;
        }
        echo $a[$this]; // Prints a different number each time test() is called per instance
        $a[$this] += 1;
    }
}

Modifica2: Ripercorrendo l'ultimo blocco ... Aggiunge più modi per fare un errore di battitura e introdurre bug che ti aiuta con l'incapsulamento. A meno che tu non abbia un buon zucchero sintattico, la capacità semplicemente non ne vale la pena.

    
risposta data 07.06.2011 - 02:33
fonte
2

Ho voluto la stessa cosa in passato. È più semplice in un linguaggio dinamico, dove è possibile associare i dati all'istanza in fase di runtime.

In C ++, che è tipizzato staticamente, il compilatore deve essere in grado di dedurre la dimensione di una classe data solo la sua dichiarazione; con le variabili locali dell'istanza dichiarate all'interno delle definizioni del metodo, il compilatore non può più farlo. C # potrebbe potenzialmente gestirlo (come potrebbe Java) perché la dichiarazione e la definizione non sono disaccoppiati come lo sono in C ++.

Un'ipotetica soluzione C ++ assocerebbe le variabili di istanza a metodi particolari:

class Example {
public:

    void method_the_first();
    void method_the_other();

    // Externally accessible as "this->method_the_first::spam".
    int method_the_first()::spam;

private:

    // Not externally accessible.
    int method_the_other()::eggs;

};

Si potrebbe anche assumere che tutti i dati statici del metodo siano privati:

class Example {
public:

    void { int spam; } method_the_first();
    void { int bar; } method_the_other();

};
    
risposta data 07.06.2011 - 03:36
fonte
1

Può essere fatto anche in C # e possibilmente in altre lingue che supportano lambda ! Sebbene quanto segue sia ancora piuttosto sperimentale, funziona.

Test instance1 = new Test();
Test instance2 = new Test();

instance1.SomeFunction();  // functionLocal = 1, staticLocal = 1
instance1.SomeFunction();  // functionLocal = 2, staticLocal = 2
instance2.SomeFunction();  // functionLocal = 1, staticLocal = 3
instance2.SomeFunction();  // functionLocal = 2, staticLocal = 4

class Test
{ 
    public void SomeFunction()
    {
        // 'function local' as discussed in the question
        Local<int> functionLocal = Local<int>.Instance( () => this );
        // static local, normally not possible in c#
        Local<int> staticLocal = Local<int>.Static( () => {} );

        functionLocal.Value += 1;
        staticLocal.Value += 1;
    }
}

Il lambda è creato solo una volta , consentendo di collegare il luogo in cui è stato creato al suo ambito. Le variabili sono memorizzate nella classe Local<T> come istanze Local<T> , in un contesto statico.

Le persone fisiche statiche non sono supportate in C #, ma collegando l'istanza al lambda passato, è possibile trovare l'ambito corretto.

I 'function locals' sono possibili con un po 'più di fatica usando un lambda che restituisce anche l'attuale scope' instance '.

È possibile trovare un'implementazione di Local<T> su questa domanda SE pertinente .

    
risposta data 07.06.2011 - 11:07
fonte

Leggi altre domande sui tag