2011-03-23 15 views
34

Ok, esta es una pregunta bastante básica: estoy siguiendo los videos del SICP, y estoy un poco confundido acerca de las diferencias entre define, let y set!.¡Diferencia entre define, let y set!

1) De acuerdo con Sussman en el video, define puede adjuntar un valor a avariable una sola vez (excepto cuando está en REPL), en particular, dos definiciones en línea no están permitidas. Sin embargo, Guile felizmente ejecuta este código

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

y salidas 2, como se esperaba. Las cosas son un poco más complicado porque si trato de hacer esto (EDIT: después de las definiciones anteriores)

(define a (1+ a)) 

consigo un error, mientras que

(set! a (1+ a)) 

está permitido. Aún así, no creo que esta sea la única diferencia entre set! y define: ¿qué es lo que me falta?

2) La diferencia entre define y let me desconcierta aún más. Sé que en teoría let se utiliza para vincular variables en el ámbito local. Aún así, me parece que esto funciona de la misma con define, por ejemplo, puedo reemplazar

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

con

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

y f y g funcionan de la misma: en particular, la variable a no está consolidado fuera g también.

La única forma en que puedo ver esto útil es que let puede tener un alcance más corto que la definición de toda la función. Aún así, me parece que siempre se puede agregar una función anónima para crear el alcance necesario e invocarlo de inmediato, al igual que hace uno en JavaScript. Entonces, ¿cuál es la verdadera ventaja de let?

+0

Usted debe adecuadamente guión '(+ a x)' 'en la definición de F', ya que es en el ámbito de' let'. –

+0

Tiene usted razón, gracias – Andrea

+0

Depende del sistema de esquema que utilice. – knivil

Respuesta

14

¿Quieres decir (+ 1 a) en lugar de (1+ a)? El último no es sintácticamente válido.

ámbito de las variables definidas por let están unidos a este último, por lo tanto

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

es sintácticamente posible, mientras

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

no lo es.

Todas las variables tienen que ser define d en el principio de la función, por lo que el siguiente código es posible:

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

mientras que este código generará un error:

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

porque la primera expresión después de la definición implica que no hay otras definiciones.

set! no define la variable, sino que se utiliza para asignar a la variable un nuevo valor. Por lo tanto, estas definiciones no tienen sentido:

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

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

uso Válido para set! es el siguiente:

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

pesar de que está desaconsejado, ya que el esquema es un lenguaje funcional.

+8

'1 +' es una función en algunas versiones de Scheme, por lo que esa llamada es válida. –

+0

Ok, el hecho de que 'define' solo se permita al comienzo de una función es interesante. Esto descarta la posibilidad de utilizar funciones anónimas para crear el alcance de la función, al menos en ciertas situaciones. – Andrea

+0

@Jeremiah: ¿Es idéntico a 'add1' en Racket, la función que incrementa su argumento? –

2

Usted puede ser capaz de utilizar define más de una vez pero no es idiomática: define implica que va a añadir una definición para el medio ambiente y set! implica que está mutando alguna variable.

No estoy seguro acerca de Guile y por qué permitiría (set! a (+1 a)) pero si a aún no está definido, eso no debería funcionar. Por lo general, uno usaría define para introducir una nueva variable y solo mutarla con set! después.

Usted puede utilizar una aplicación función anónima en lugar de let, en hecho de que por lo general es exactamente lo que se expande en let, es casi siempre una macro. Estos son equivalentes:

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

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

La razón por la que tendría que utilizar let es que es más claro: los nombres de las variables son justo al lado de los valores.

En el caso de las definiciones internas, no estoy seguro de que Yasir sea correcto. Al menos en mi máquina, ejecutar Racket en modo R5RS y en modo normal permitía que las definiciones internas aparecieran en el medio de la definición de función , pero no estoy seguro de lo que dice la norma. En cualquier caso , mucho más tarde en SICP, el truco que define interna pose es discutido en profundidad. En el Capítulo 4, se explora cómo implementar las definiciones internas mutuamente recursivas y lo que significa para la implementación del intérprete metacircular.

¡Quédate con eso! SICP es un libro brillante y las conferencias en video son maravillosas.

28

Su confusión es razonable: 'let' y 'define' crean nuevos enlaces. Una ventaja de 'dejar' es que su significado está extraordinariamente bien definido; no hay absolutamente ningún desacuerdo entre varios sistemas Scheme (incluido Racket) acerca de lo que significa "dejar pasar".

La forma 'definir' es una olla de pescado diferente. A diferencia de 'let', no rodea el cuerpo (región donde el enlace es válido) con paréntesis. Además, puede significar cosas diferentes en el nivel superior e internamente. Los diferentes sistemas de esquemas tienen significados dramáticamente diferentes para 'definir'. De hecho, Racket ha cambiado recientemente el significado de 'definir' al agregar nuevos contextos en los que puede ocurrir.

Por otro lado, a las personas les gusta 'definir'; tiene menos indentación, y generalmente tiene un nivel de alcance "do-what-I-mean" que permite definiciones naturales de procedimientos recursivos y mutuamente recursivos. De hecho, me picó esto el otro día :).

Finalmente, '¡listo!'; como 'let', 'set!' es bastante sencillo: muta una unión existente.

FWIW, una forma de entender estos ámbitos en DrRacket (si lo está usando) es utilizar el botón "Comprobar sintaxis", y luego pase el puntero sobre varios identificadores para ver dónde están enlazados.

+0

Gracias; He editado la pregunta para dejar en claro que estaba usando 'set!' ** after ** the defitions. El punto de ese párrafo es que puedo 'establecer!' Una variable en función de su valor anterior, pero no puedo 'definir' una variable en función de su valor anterior. – Andrea

+0

Derecha; esto se debe a que el alcance de un 'definir' incluye el lado derecho del enlace mismo. Eso es importante, porque permite 'definir' para crear procedimientos recursivos, algo que no es posible con 'let' (a menos que use un truco similar a Y-combinator). –

+0

En Chicken Scheme, ¡listo! se puede utilizar para introducir nuevas vinculaciones también. Esto me hizo tropezar durante la programación exploratoria una vez. Por ejemplo, (define (foo a) (set! Ba)) funcionará felizmente en una nueva ejecución del entorno: csi -e '(define (foo a) (set! B (* a 2))) (foo 42) (pantalla b) '. ¡Parece que está listo! recorrerá entornos desde la mayoría de locales a globales hasta que encuentre la variable nombrada. Estoy usando 4.9.0.1. –

5

La respuesta de John Clements es buena. En algunos casos, puede ver en qué se convierte el define s en cada versión de Scheme, lo que puede ayudarlo a comprender lo que está sucediendo.

Por ejemplo, en Chez Esquema 8.0 (que tiene sus propias define peculiaridades, especialmente WRT R6RS.!):

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

Usted ve que el "alto nivel" definir convierte en un set! (aunque ¡simplemente expandir define en algunos casos cambiará las cosas!), pero la definición interna (es decir, un define dentro de otro bloque) se convierte en letrec*. Los diferentes Esquemas expandirán esa expresión en diferentes cosas.

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)))) 
Cuestiones relacionadas