2017-09-06 6 views
2

Ich glaube, dass das Verständnis dieser Feinheit mir helfen kann zu verstehen, wie Scope in Scheme funktioniert.Scheme/Guile: Variable Selbstreflexion innerhalb einer Funktion

Warum tut Scheme Fehler, wenn Sie versuchen, wie etwas zu tun:

(define (func n) 
    (define n (+ 1 n)) 
    n) 

Es nur Fehler zur Laufzeit, wenn die Funktion aufgerufen wird.

Der Grund, warum ich es seltsam finde, ist, weil Scheme Re-Definitionen erlaubt, sogar innerhalb von Funktionen. Zum Beispiel ergibt dies keinen Fehler und den Wert 5 wie erwartet immer zurück:

(define (func n) 
    (define n 5) 
    n) 

Zusätzlich Schema scheint auch selbst Neudefinition in globalem Raum zu unterstützen. Zum Beispiel:

(define a 5) 
(define a (+ 1 a)) 

gibt keinen Fehler und Ergebnisse in "a" Anzeige "6" wie erwartet.

Warum gibt das gleiche einen Laufzeitfehler, wenn er innerhalb einer Funktion auftritt (was die Neudefinition unterstützt)? Mit anderen Worten: Warum funktioniert die Selbstreferenzierung nur innerhalb einer Funktion?

Antwort

2

global define

First off, Top-Level-Programme als durch einen anderen Teil der Umsetzung behandelt werden in einer Funktion ist und ein bereits definierte Variable ist nicht zulässig.

(define a 10) 
(define a 20) ; ERROR: duplicate definition for identifier 

Es kann vorkommen, dass es in einem REPL funktioniert, da es üblich ist, Sachen neu zu definieren, aber wenn Programme laufen das ist absolut verboten. In R5RS und vor dem, was passiert ist, ist es unterspezifiziert und war nicht wichtig, da die Spezifikation durch Verletzung es ist nicht mehr ein Scheme-Programm und Implementierer sind frei zu tun, was sie wollen. Das Ergebnis ist natürlich, dass viele unterspezifizierte Sachen ein implementierungsspezifisches Verhalten bekommen, das nicht tragbar oder stabil ist.

Lösung:

set! mutiert Bindungen:

(define a 10) 
(set! a 20) 

define in einer Lambda (Funktion, lassen, ...)

A define in einem Lambda ist etwas ganz anderes, von völlig verschiedenen Teilen der Implementierung behandelt. Es wird von der Makro/speziellen Form behandelt lambda so dass es zu einem letrec* neu geschrieben wird

A letrec* oder letrec wird zur Herstellung von Funktionen verwendet rekursiv so die Namen zu der Zeit verfügbar sein müssen, die Ausdrücke ausgewertet werden. Aus diesem Grund, wenn Sie n hat es bereits die n beschattet Sie als Argument übergeben. Zusätzlich zu R6RS müssen Implementierer einen Fehler signalisieren, wenn eine Bindung ausgewertet wird, die noch nicht initialisiert wurde und das ist wahrscheinlich was passiert.Vor R6RS Implementierer waren frei zu tun, was sie wollten:

(define (func n) 
    (define n (+ n 1)) ; illegal since n hasn't got a value yet! 
    n) 

Dies wird tatsächlich:

(define func 
    (lambda (n) 
    (let ((n 'undefined-blow-up-if-evaluated)) 
     (let ((tmpn (+ n 1))) 
     (set! n tmpn)) 
     n))) 

Jetzt ein Compiler könnte sehen, dass es die Spezifikation zum Zeitpunkt der Kompilierung verletzt, aber viele Implementierungen weiß nicht, bevor es läuft.

(func 5) ; ==> 42 

Perfekt fein Ergebnis in R5RS, wenn die Implementierer guten Geschmack in Bücher haben. Der Unterschied in der Version die nachweislich funktioniert, ist, dass dies nicht die Regel verstößt n vor dem Körper Auswertung:

(define (func n) 
    (define n 5) 
    n) 

wird:

(define func 
    (lambda (n) 
    (let ((n 'undefined-blow-up-if-evaluated)) 
     (let ((tmpn 5)) ; n is never evaluated here! 
     (set! n tmpn)) 
     n))) 

Lösungen

Verwenden eines nicht widersprüchlicher Name

(define (func n) 
    (define new-n (+ n 1)) 
    new-n) 

Verwenden Sie let. Es hat keine eigene Bindung, wenn der Ausdruck ausgewertet wird:

(define (func n) 
    (let ((n (+ n 1))) 
    n)) 
Verwandte Themen