1

その理由は次のとおりです。

  1. 関数定義は、その後に定義された定義を使用できます
  2. 変数定義はできませんが。

例えば、

a) 次のコード スニペットは間違っています。

; Must define function `f` before variable `a`.
#lang racket
(define a (f)) 
(define (f) 10)

b) 次のスニペットは正しいですが:

; Function `g` could be defined after function `f`.
#lang racket
(define (f) (g)) ; `g` is not defined yet
(define (g) 10)

c)そうです:

; Variable `a` could be defined after function `f`
#lang racket
(define (f) a) ; `a` is not defined yet
(define a 10)
4

2 に答える 2

5

Racket についていくつかのことを知っておく必要があります。

  1. Racket では、モジュールを持たない多くの (従来の r5rs) スキームとは異なり、各ファイル (で始まる#lang) はモジュールです。

  2. モジュールのスコープ規則は関数の規則に似ているため、ある意味では、これらの定義は関数内の定義に似ています。

  3. Racket は定義を左から右に評価します。スキーム用語では、Racket の定義にはletrec*セマンティクスがあると言います。letrecこれは、相互に再帰的な定義が機能しないセマンティクスを使用する一部のスキームとは異なります。

つまり、定義はすべてモジュールの環境で作成され (関数ローカル定義の場合も同様に関数で)、左から右に初期化されます。したがって、後方参照は常に機能するため、いつでも次のようなことができます

(define a 1)
(define b (add1 a))

それらは単一のスコープで作成されるため、理論的には前方定義はスコープ内にあるという意味で有効です。#<undefined>ただし、実際の値が評価されるまで特別な値を取得するため、実際に前方参照の値を使用しても機能しません。これを確認するには、次のコードを実行してみてください。

#lang racket
(define (foo)
  (define a a)
  a)
(foo)

モジュールのトップレベルはさらに制限されているため、そのような参照は実際にはエラーになります。これは次のように表示できます。

#lang racket
(define a a)

これらすべてを念頭に置いて、関数内の参照についてはもう少し寛大です。問題は、関数が呼び出されるまで関数の本体が実行されないことです-したがって、関数内で前方参照が発生した場合、それは有効です(=エラーを取得しない#<undefined>か、関数が最後に呼び出された場合)バインディングが初期化されました。これは単純な関数定義に適用されます

(define foo (lambda () a))

通常のシンタックス シュガーを使用する定義

(define (foo) a)

最終的には機能に拡張される他の形式でさえも

(define foo (delay a))

これらすべてを使用すると、定義が初期化された後に関数本体のすべての使用が発生する場合、同じ規則によってエラーが発生することはありません。

ただし、この種の初期化を代入と混同しないでください。これは、次のようなことを意味します

(define x (+ x 1))

主流言語と同等ではありません。x = x+1それらはvar x = x+1、「初期化されていない変数への参照」エラーで失敗する言語のいくつかに似ています。これは、現在のスコープで新しいdefineバインディングを作成するためであり、既存のものを「変更」しないためです。

于 2013-10-29T10:47:45.973 に答える