Domanda

In realtà sto leggendo il libro per divertimento, ma potrebbe essere considerato compiti a casa. In ogni caso, non mi sento a mio agio con le variabili di stato locali a tutti con questo linguaggio ... Prendiamo ad esempio questo codice:

(define flip
  (let ((count 0))
    (lambda ()
      (if (= 0 count)
          (begin (set! count 1) count)
          (begin (set! count 0) count)))))

Perché questo codice si alternano tra 1 e 0? conteggio viene dato il valore 0 ogni volta che questa funzione viene chiamata! Un pitone equivalente potrebbe essere:

class Flip:
    def __init__(self):
        pass
    def __call__(self):
        count = 0
        if count == 0:
            count = 1
            return count
        else:
            count = 0
            return count

Questa operazione riporta la stessa cosa ogni volta. Sono confuso ...

È stato utile?

Soluzione

Ho un po 'di esperienza con la scrittura dei compilatori per i linguaggi funzionali, quindi forse una breve descrizione di come tale funzione viene memorizzata / rappresentata in memoria è in ordine. Ogni funzione può approssimativamente essere pensato come una coppia (E, F), dove E è l'insieme di variabili libere, e F è il "codice" della funzione stessa. Quando si chiama la funzione, ci vogliono i valori in E e sostituisce quelli per le variabili in F, e poi esegue il codice utilizzando questi valori.

Quindi, dove il vostro esempio è interessato, è stato definito il "flip" variabile per essere la funzione restituita da tua espressione lasciare. Tale funzione è la roba dentro il lambda. Perché "conteggio" è definito all'esterno della lambda, è una variabile libera, quindi è memorizzato nell'ambiente della funzione. Poi, ogni volta che si chiama (Flip), l'interprete va al codice nella lambda, vede che ha bisogno di guardare in alto il valore di "conteggio" nell'ambiente, lo fa, lo cambia, e ritorna. Ecco perché ogni volta che si voglia chiamare, il valore memorizzato in "contare" persiste.

Se si vuole contare fino a zero ogni volta che si chiama a fogli mobili, mettere l'espressione let all'interno del lambda, quindi è una variabile legata al posto di una variabile libera.

Altri suggerimenti

La lambda è una chiusura. E 'una funzione che fa riferimento a una variabile libera (conteggio), che, non essendo definito localmente o uno dei parametri, è associato all'ambiente racchiude vicina lessicale.

La funzione chiamata è il lambda, non "flip". Flip è solo un nome che avete dato al lambda che viene restituito dal (let ...) espressione.

Per quanto riguarda il Python, non so la lingua, ma sembra che conta dovrebbe essere un membro dell'oggetto a fogli mobili, non è una variabile locale a Chiama .

Perché la vostra funzione di vibrazione in realtà restituisce una funzione (che è definito all'interno lambda)

Ogni volta che si chiama la funzione restituita esso modifica il suo ambiente.

Se ci pensate il let crea l'ambiente (e inizializza contare fino a 0) solo una volta -. Quando la funzione lambda è restituito a voi

In un certo senso lambda crea un oggetto funzione per voi che utilizza l'ambiente, il cui telaio ultima è stata inizializzata in lasciare che con un unico conteggio variabile. Ogni volta che si chiama la funzione che modifica il suo ambiente. Se si chiama capovolgere una seconda volta restituisce un altro oggetto funzione con ambiente diverso. (Contate inizializzato a 0) È quindi possibile alternare i due funtori indipendente.

Se si vuole capirne appieno come funziona si dovrebbe leggere su modello environmantal .

è più come

class Flip:
    def __init__(self):
        self.count = 0
    def __call__(self):
        if self.count == 0:
            self.count = 1
            return self.count
        else:
            self.count = 0
            return self.count

Aggiornamento con più spiegazioni: La funzione nello Schema è una chiusura che "chiude" attorno al count variabile libera, che è definita nella portata all'esterno. Il modo in cui count è definito in un let con appena la funzione come il corpo, significa che la funzione è l'unica cosa che può accedervi -. Fare count effettivamente una sorta di stato mutabile privata che è collegato alla funzione

Questo è il modo "oggetti" sono tradizionalmente creati nello Schema in SICP - avere un let definiscono un gruppo di variabili (variabili istanza, inizializzate ai valori iniziali), e nel corpo definisce una serie di funzioni che sono "metodi" che hanno accesso alle variabili di istanza condivise. Questo è il motivo per cui è naturale qui per utilizzare una classe Python per rappresentare ciò che sta accadendo, con count essendo una variabile di istanza.

Una traduzione più letterale in Python 3.x sarebbe qualcosa di simile (notare che è solo approssimativa come Python non ha un let (-portata limitata locali dichiarazione di variabile) della sintassi, e lambdas di Python non può essere utilizzato perché non prendono le dichiarazioni):

count = 0

def flip():
    nonlocal count
    if count == 0:
        count = 1
        return count
    else:
        count = 0
        return count

# pretend count isn't in scope after this

Il problema con il codice originale è che ha una forte influenza dello stile imperativo. Una soluzione più idiomatica sarà:

(define (flip)
  (let ((flag #t))
    (lambda ()
      (set! flag (not flag))
      (if flag 1 0))))

Per rispondere alla domanda in che commentate ooboo, si desidera una funzione che restituisce una funzione

(define make-flipper
  (lambda ()
    (let ((count 0))
      (lambda ()
    (let ((v count))
      (set! count (- 1 count))
      v)))))

;; test it
(let ((flip-1 (make-flipper)))
  (format #t "~s~%" (flip-1))  
  (format #t "~s~%" (flip-1))
  (format #t "~s~%" (flip-1))

  (let ((flip-2 (make-flipper)))
    (format #t "~s~%" (flip-2))
    (format #t "~s~%" (flip-2))
    (format #t "~s~%" (flip-2))))

È possibile modificare banalmente il set! linea in modo che diventi un contatore, non un flipper (più utile).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top