2017-11-02 7 views
1

私はスキームや一般的なLispのに新たなんだ、と学習時に私が結合するローカル・プロシージャで使用される不可解な構文つまずいてきました:それはあることを把握するために私にしばらく時間がかかったローカルプロシージャバインディング

(define mock 
    (lambda (s) 
     ;; this is what I don't understand 
     (let splice ([l '()] [m (car s)] [r (cdr s)]) 
     (append 
     (map (lambda (x) (cons m x)) r) 
     (if (null? r) '() 
      (splice (cons m l) (car r) (cdr r))))))) 

splice 3つの要素を持つスコープ付きプロシージャです。

(define mock2 
    (lambda (s) 
     ;; define `splice` first 
     (define splice 
     (lambda (la lb lc) 
      (append 
      (map (lambda (x) (cons lb x)) lc) 
      (if (null? lc) '() 
       (splice (cons lb la) (car lc) (cdr lc)))))) 
     ;; bind `splice` and its arguments together and call it with them 
     (let ([sp splice] [l '()] [m (car s)] [r (cdr s)]) 
     (splice l m r)))) 

番目のバージョンは少し長く、やや不可欠に見えますが、並行して、それを結合する前に範囲内の通常の手順としてspliceを定義:ML風のスタイルでこれを書き換えるには、同様の出力を生成しているようです議論(または単にそのままの状態で)をして、それを賢明に見せてください。

質問この2つのバージョンは交換可能ですか?はいの場合、spliceバインディングフォーム内のローカル変数(lm、およびr)をバインドする最初のバージョンの構文を説明するのに役立ちますか?

+0

'(null?r)'に達するとLを返してはいけません。そうでなければ決して使用されないようです。 – coredump

+0

@coredumpはいそうです。 – PieOhPah

答えて

2

spliceを呼び出すのは、ループの再入力のようなものです。これはそのためのものです。テールコールはとにかく後藤です。それはいくつかの特別な名前を考えるのではなく、しばしばloopという名前です。

「見た目がいいです」は議論が可能で、実際にはSchemerでこれを失うことになります。という名前の非常に一般的なScheme構造であるためです。もしそれを書き直したいなら、それをよく理解するために、通常はletrec btwで書き直されます。内部defineも使用できますが、最初は(define (mock s) ...を使用してください。

ので、再書き込み、これに通常の方法では

(define mock        ; or: (define (mock s) ... 
    (lambda (s) 
     (let splice ([l '()] [m (car s)] [r (cdr s)]) 
     (append 
     (map (lambda (x) (cons m x)) r) 
     (if (null? r) '() 
      (splice (cons m l) (car r) (cdr r))))))) 

はこれです:

(define mock 
    (lambda (s) 
    (letrec ([splice (lambda (l m r)  ; or: (define (splice l m r) ... 
         (append 
         (map (lambda (x) (cons m x)) r) 
         (if (null? r) '() 
          (splice (cons m l) (car r) (cdr r)))))]) 
     (splice '() (car s) (cdr s))))) 

と方法は、それが一つの場所で定義されたから1を保存してみましょうという名前に書き込みます潜在的に遠く離れて別の場所で呼び出されました。コールは最初から本体に入り、名前はとよく反映されます。

これは非常に自明です。あるフォームから他のフォームへの変換は純粋に構文的であり、両方を同じ意味で使用することができます。

+0

あなたのポインタで私は最終的に文書http://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Iteration.htmlを見つけました。プロシージャに名前をバインドする特殊なフォームのようです。これは 'do 'で達成できるものなのでしょうか? – PieOhPah

+0

書き直しは 'do'とはるかに関わっています。それはおそらくそれ自体マクロであり、おそらくこれはマクロでしょう。 'letrec'はSchemeの最も基本的なビルディングブロックの1つと見なされます。 –