BETA

Scheme: スニペットに局所変数の値を与える

投稿日:2020-09-19
最終更新:2020-09-21

お題

スニペット snippeteval すると実行できるオブジェクトになる。だけどスニペット中の自由変数 lang にどうやって局所変数の値を与えるか。

(define snippet  
 '(case lang  
    ((en) "Hello.")  
    ((de) "Guten Tag.")  
    (else #f)))  

なおかつ、このスニペットでサポートされていない言語が lang に指定された場合についての処理をこのスニペットの外側で追加したい。つまり lang に与える値はスニペットにも、その外のコードにも与える。

(define (get-greetings lang)  
  スニペット由来の処理  
  スニペットでサポートされない言語について追加的な処理。)  

正しくない答え

snippet を単純に eval するだけではだめだ。

(define (get-greetings lang)  
  (or  
    (eval snippet (interaction-environment))  
    (case lang  
      ((fr) "Bonjour.")  
      ((ja) "こんにちは。")  
      (else #f))))  

これだとスペット由来の lang は自由変数のままなので、get-greetings を実行すると未定義の変数があるというエラーになる。

正しい答え

スニペット snippet のコード(S式)を (lambda (lang) …) の中にいれて、これを eval して無名関数を作る。これでスニペット中の自由変数 langlambda の引数として束縛変数になる。こうして得られた無名関数を applylang に値を与えればよい。

(define (get-greetings lang)  
  (or  
    (apply  
      (eval  
       `(lambda (lang) ,snippet)  
        (interaction-environment))  
      (list lang))  
    (case lang  
      ((fr) "Bonjour.")  
      ((ja) "こんにちは。")  
      (else #f))))  

これで、スニペットでサポートされている言語にも、追加的なコードでサポートされる言語にも対応できる関数 get-greetings ができた。

(display (get-greetings 'de)) (newline)  
⇒ Guten Tag.  

(display (get-greetings 'fr)) (newline)  
⇒ Bonjour.  

限界

これにも限界があって、スニペット中の set! は外側のコードには影響を与えない。なぜならそれは lambda の引数の変数でしかないから。もしスニペット中で lang の値を set! で変更したいなら、get-greeting のないのコード全体を eval の対象にするしかないだろう。

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう