Différence entre définir, laisser et définir!

Ok, c'est une question assez basique: je suis les vidéos SICP, et je suis un peu confus au sujet des différences entre define, let et set!.

1) selon Sussman dans la vidéo, define est autorisé à attacher une valeur à avariable qu'une seule fois (sauf lorsque dans le REPL), en particulier deux définit en ligne ne sont pas autorisés. Pourtant, Guile exécute heureusement ce code

(define a 1)
(define a 2)
(write a)

Et Produits 2, comme prévu. Les choses sont un peu plus compliquées parce que si j'essaie de le faire (EDIT: après les définitions ci-dessus)

(define a (1+ a))

Je reçois une erreur, alors que

(set! a (1+ a))

Est autorisé. Pourtant, je ne pense pas que ce soit la seule différence entre set! et define: qu'est-ce qui me manque?

2) La différence entre define et let me énigmes encore plus. Je sais en théorie que let est utilisé pour lier des variables dans la portée locale. Pourtant, il me semble que cela fonctionne de la même manière avec define, par exemple je peux remplacer

(define (f x)
    (let ((a 1))
        (+ a x)))

Avec

(define (g x)
    (define a 1)
    (+ a x))

Et f et g fonctionnent de la même: en particulier, la variable a est indépendant à l'extérieur de g ainsi.

La seule façon de voir cela utile est que let peut avoir une portée plus courte que toute la définition de la fonction. Pourtant, il me semble que l'on peut toujours ajouter une fonction anonyme pour créer la portée nécessaire, et l'invoquer tout de suite, un peu comme on le fait en javascript. Alors, quel est le véritable avantage de let?

35
demandé sur Andrea 2011-03-23 16:37:45

4 réponses

Voulez-vous dire (+ 1 a) au lieu de (1+ a) ? Ce dernier n'est pas syntaxiquement valide.

La portée des variables définies par let sont liées à cette dernière, donc

(define (f x)
  (let ((a 1))
    (+ a x)))

Est syntaxiquement possible, tandis que

(define (f x)
  (let ((a 1)))
  (+ a x))

Ne l'est pas.

Toutes les variables doivent être define d au début de la fonction, ainsi le code suivant est possible:

(define (g x)
  (define a 1)
  (+ a x))

Alors que ce code va générer une erreur:

(define (g x)
  (define a 1)
  (display (+ a x))
  (define b 2)
  (+ a x))

Parce que la première expression après la définition implique qu'il n'y a pas d'autres définitions.

set! ne définit pas la variable, elle est plutôt utilisée pour attribuer une nouvelle valeur à la variable. Par conséquent, ces définitions sont dénuées de sens:

(define (f x)
  (set! ((a 1))
    (+ a x)))

(define (g x)
  (set! a 1)
  (+ a x))

L'utilisation valide pour set! est la suivante:

(define x 12)
> (set! x (add1 x))
> x
13

Bien que ce soit découragé, car Scheme est un langage fonctionnel.

14
répondu Yasir Arsanukaev 2011-03-23 14:09:40

Votre confusion est raisonnable: 'let' et 'define' créent tous deux de nouvelles liaisons. Un avantage à "laisser" est que sa signification est extraordinairement bien définie; il n'y a absolument aucun désaccord entre les différents systèmes de schéma (incl. Raquette) à propos de ce que signifie "let".

La forme "définir" est une bouilloire de poisson différente. Contrairement à 'let', il n'entoure pas le corps (région où la liaison est valide) avec des parenthèses. En outre, cela peut signifier différentes choses au niveau supérieur et interne. Différents systèmes de schéma ont des significations radicalement différentes pour "définir". En fait, Racket a récemment changé le sens de "définir" en ajoutant de nouveaux contextes dans lesquels il peut se produire.

D'un autre côté, les gens aiment "define"; il a moins d'indentation, et il a généralement un niveau de portée "do-what-I-mean" permettant des définitions naturelles de procédures récursives et mutuellement récursives. En fait, j'ai été mordu par cela l'autre jour :).

Enfin, 'set!'; comme "let", 'set!"est assez simple: il mute une liaison existante.

FWIW, une façon de comprendre ces étendues dans DrRacket (si vous l'utilisez) est d'utiliser le bouton "Vérifier la syntaxe", puis de survoler différents identifiants pour voir où ils sont liés.

29
répondu John Clements 2011-03-24 16:39:18

La réponse de John Clements est bonne. Dans certains cas, vous pouvez voir ce que les definedeviennent dans chaque version de Scheme, ce qui pourrait vous aider à comprendre ce qui se passe.

Par exemple, dans Chez Schéma 8.0 (qui a son propre define bizarreries, esp. wrt R6RS!):

> (expand '(define (g x)
             (define a 1)
             (+ a x)))
(begin
  (set! g (lambda (x) (letrec* ([a 1]) (#2%+ a x))))
  (#2%void))

Vous voyez que la définition de "niveau supérieur" devient un set! (bien que l'expansion de define dans certains cas changera les choses!), mais l'intérieur définir (c'est - define à l'intérieur d'un autre bloc) devient un letrec*. Différent Les schémas élargiront cette expression en différentes choses.

MzScheme v4.2.4:

> (expand '(define (g x)
             (define a 1)
             (+ a x)))
(define-values
 (g)
 (lambda (x)
   (letrec-values (((a) '1)) (#%app + a x))))
5
répondu erjiang 2011-03-25 03:39:00

Vous pouvez utiliser define plus d'une fois mais ce n'est pas le cas idiomatic: define implique que vous ajoutez une définition à la environnement et set! implique que vous mutez une variable.

Je ne suis pas sûr de la ruse et pourquoi cela permettrait (set! a (+1 a)) mais si a n'est pas encore défini, cela ne devrait pas fonctionner. Habituellement on utiliserait {[1] } pour introduire une nouvelle variable et seulement la muter avec set! tard.

Vous pouvez utiliser une application de fonction anonyme au lieu de let, dans fait que l' habituellement, exactement ce que let se développe, c'est presque toujours une macro. Ceux-ci sont équivalents:

(let ((a 1) (b 2))
  (+ a b))

((lambda (a b)
   (+ a b))
 1 2)

La raison pour laquelle vous utiliseriez let est que c'est plus clair: les noms de variables sont juste à côté des valeurs.

Dans le cas des définitions internes, Je ne suis pas sûr que Yasir soit correct. Au moins sur ma machine, en cours D'exécution raquette en mode R5RS et en le mode normal permet aux définitions internes d'apparaître au milieu du définition de la fonction, mais je ne suis pas sûr de ce que dit la norme. En tout cas, beaucoup plus tard dans SICP, la ruse qui définit la pose interne est discuté en profondeur. Au chapitre 4, Comment mettre en œuvre mutuellement récursif interne définit est explorée et ce que cela signifie pour la mise en œuvre de l'interpréteur métacirculaire.

Alors restez avec elle! SICP est un livre brillant et les conférences vidéo sont merveilleux.

2
répondu michiakig 2011-03-23 14:48:00