Question

Je lis le livre en fait pour le plaisir, mais il peut être considéré comme devoirs. En tout état de cause, je ne me sens pas à l'aise avec les variables d'état locales du tout avec cette langue ... Prenons par exemple le code suivant:

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

pourquoi est-ce autre code entre 1 et 0? compte est donné la valeur de 0 à chaque fois que cette fonction est appelée! Un équivalent python serait:

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

renvoie la même chose à chaque fois. Je suis confus ...

Était-ce utile?

La solution

J'ai un peu d'expérience avec les compilateurs d'écriture pour les langages fonctionnels, donc peut-être une brève description de la façon dont cette fonction est stockée / représentée dans la mémoire est en ordre. Chaque fonction peut à peu près être considéré comme une paire (E, F) où E est l'ensemble des variables libres et F est le « code » de la fonction elle-même. Lorsque vous appelez la fonction, il prend les valeurs dans E et remplace celles des variables F, et exécute ensuite le code en utilisant ces valeurs.

Alors, où votre exemple est concerné, vous avez défini la variable « flip » pour être la fonction renvoyée par votre expression let. Cette fonction est la substance dans votre lambda. Parce que est défini « compte » en dehors du lambda, il est une variable libre, donc il est stocké dans l'environnement de la fonction. Ensuite, chaque fois que vous appelez (FLIP), l'interprète va au code dans le lambda, voit qu'il doit rechercher la valeur de « compte » dans l'environnement, le fait que, le change, et retourne. Voilà pourquoi chaque fois que vous l'appelez, la valeur stockée dans « count » persiste.

Si vous voulez compter à zéro chaque fois que vous appelez chiquenaude, mettez l'expression let dans le lambda, il est donc une variable liée au lieu d'une variable libre.

Autres conseils

Le lambda est une fermeture. Il est une fonction qui fait référence à une variable libre (nombre), qui, sans être définie localement ou l'un des paramètres, est lié à la plus proche enfermant environnement lexical.

La fonction appelée est le lambda, non « flip ». Flip est juste un nom que vous avez donné au lambda qui est revenu de l'expression (... laisser).

En ce qui concerne le Python, je ne sais pas la langue, mais il semble que le nombre devrait être un membre de l'objet flip, pas une variable locale à Appel .

Parce que votre fonction flip en fait retourne une fonction (qui est définie à l'intérieur lambda)

Chaque fois que vous appelez la fonction renvoyée, il modifie son environnement.

Si vous y pensez let crée l'environnement (et initialise à 0 compte) qu'une seule fois -. Lorsque la fonction lambda est retourné

Dans un sens lambda crée un objet de fonction pour vous qui utilise l'environnement, dont le dernier cadre a été initialisé dans let avec un nombre variable unique. Chaque fois que vous appelez votre fonction modifie son environnement. Si vous appelez retourner une deuxième fois, il retourne un autre objet de fonction avec environnement différent. (Nombre initialisé à 0) Vous pouvez ensuite basculer les deux foncteurs indépendamment.

Si vous voulez undestand pleinement comment cela fonctionne, vous devriez lire sur modèle environmantal .

il est plus comme

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

Mise à jour avec plus d'explications: La fonction dans le schéma est une fermeture qui « se ferme » autour de la count libre variable, qui est définie dans le cadre extérieur. La façon dont count est définie dans un let avec juste la fonction que le corps, signifie que la fonction est la seule chose qui peut y accéder -. count faire efficacement une sorte d'état mutable privé qui est attaché à la fonction

Ceci est la façon dont « les objets » sont traditionnellement créés dans le schéma en SICP - d'avoir un let définir un groupe de variables (les variables d'instance, initialisées à leurs valeurs initiales), et dans le corps définissent un tas de fonctions sont des « méthodes » qui ont un accès partagé aux variables d'instance. Voilà pourquoi il est naturel ici d'utiliser une classe Python pour représenter ce qui se passe, avec count étant une variable d'instance.

Une traduction plus littérale en Python 3.x serait quelque chose comme ceci (notez qu'il est approximative que Python n'a pas de let (de portée limitée déclaration variable locale) la syntaxe et les lambdas de Python ne peut pas être utilisé parce qu'ils ne prennent pas des déclarations):

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

Le problème avec le code d'origine est qu'il a une forte influence du style impératif. Une solution plus idiomatiques sera:

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

Pour répondre à la question dans le commentaire de ooboo, vous voulez une fonction qui renvoie une fonction

(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))))

Vous pouvez trivialement changer le jeu! ligne afin qu'il devienne un compteur, pas un Flipper (plus utile).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top