(はじめよう Scheme 2)

構文


プログラムの構成が分かったところで基本的な構文を示す。ライブラリとマクロ*1については後ほどやるので、それらは除外されている。また、Schemeでは変数を定義することを束縛*2、変数に値を入れることを代入という。単なる言葉尻を捕らえているように思うかもしれないが我慢してほしい。さらに、トップレベルという言葉も出てくるが、これは束縛を作る構文を外側に含まない式(内側にふ含んでもよい)と思ってもらえばよい。

コメント


プログラムを書く上でもっとも重要な構文の一つはコメントである。Schemeでは3種類のコメントが用意されている。
; 一行コメント。行末までをコメントにする。

#;("式コメント" "括弧に囲まれた式一つをコメントにする。"
   "式であれば、改行を含んでもよい")

#|
ブロックコメント。
開始マーク #| から終了マーク |# までをコメントにする。
上記のように入れ子になっていてもよい。
|#

束縛を作る構文:define、let、let*、letrec、letrec*


defineはトップレベルで出てくる場合は大域に束縛をつくり、let等の局所変数を束縛する構文内に出てくる場合は局所変数を束縛する。
defineは以下の3つのフォームのいずれかを取る。
  • (define <variable> <expression>)
  • (define (<variable>  <formals>) <body>)
  • (define (<variable> . <formals>) <body>)
例:
;; 値123をシンボルfooに束縛する。
;; (シンボルfooは変数fooと読み替えてよい)
(define foo 123)

;; 3引数を取る手続きbarを定義する。
(define (bar a b c) (+ a b c))

;; 上記は以下の糖衣構文でもある
;; lambdaについては手続きを作る構文参照
(define bar (lambda (a b c) (+ a b c)))

;; 可変長引数を受け取る手続きbuzを定義する。
(define (buz . rest) (apply + rest))

;; 上記は以下の糖衣構文でもある
(define bar (lambda rest (apply + rest)))

letlet*letrecletrec*は局所変数を束縛する構文である。いくつかのバリエーションがあるがそれぞれ意味が違うので注意されたい。用例ではもっともよく使われるletlet*を示し、letrecletrec*については都度示すこととする。
let系の構文は以下のフォームを取る。
  • (let <bindings> <body>)
  • (let* <bindings> <body>)
  • (letrec <bindings> <body>)
  • (letrec* <bindings> <body>)
<bindings>の定義は以下。
  • ((<variable> <expression>) ...)
...は直前の式をn回繰り返してもよいという意味である(nは0でもよい)。
例:
;; 値0をシンボルaに、値1をシンボルbに束縛する。
;; (局所変数a、bを定義する)
(let ((a 0)
      (b 1))
  (+ a b))

;; 以下はエラー
;; letは先に定義された束縛を参照できない。
(let ((a 0)
      (b (+ a 1)))
  (+ a b))

;; 値0をシンボルaに、値1をシンボルbに束縛する。
;; (局所変数a、bを定義する)
(let* ((a 0)
       (b 1))
  (+ a b))

;; let*は先に定義された束縛を参照可能。
(let ((a 0)
      (b (+ a 1)))
  (+ a b))

コラム:letlet*
letは局所変数を束縛する構文ではあるが、lambdaの糖衣構文でもある。処理系によってはletを以下のように扱うものもある。
(let ((a 0)
      (b 1))
  (+ a b))
;; -> ((lambda (a b) (+ a b)) 0 1)
これであれば、同じ<bindings>で定義された変数が参照不可能なのが理解できる。
またlet*は入れ子になったletをまとめたものである。
(let* ((a 0)
       (b 1))
  (+ a b))
#|
(let ((a 0))
  (let ((b 1))
    (+ a b)))
|#

手続きを作る構文:lambda


暗黙の内に手続きを作るにはdefineが使えるが、明示的に手続きを作るには前回やったようにlambdaを使う。 なぜlambdaというキーワードなのかというのは歴史的な理由があるのだが、それは割愛させてほしい*3lambda構文は以下のフォームを取る。
  • (lambda <formals> <body> ...)
<formals>は以下の通り;
  • (<variable1> ...)
  • <variable>
  • (<variable1> ... <variable n> . <variable n+1>)
最初のフォームは与えられた個数の引数を要求し、2番目のフォームは可変長引数の定義となる。最後のは両方を足したもので、n個の引数を要求しかつそれ以上の可変長引数を受け付ける。可変長引数はリストとして手続きに渡される。リストとは括弧で囲まれた式のことを指す。
;; 3つの引数を受け取る
((lambda (a b c) (+ a b c)) 1 2 3)
;; -> 6

;; 可変長引数を受け取る
((lambda args args) 1 2 3)
;; -> (1 2 3)

;; 2つの引数を要求し、残りはリストにする
((lambda (a b . c) c) 1 2 3 4 5)
;; -> (3 4 5)
可変長引数がリストで渡されて何がうれしいのかということはもう少し後でやることにするので、しばらくは引数のリストは明示的に与えることとする*4

*1: 今はSchemeにはそういうものがあるという認識でよい。
*2: 実際には多少違うが、これくらいの認識でよいと思われる。
*3: John McCarthy Lisp辺りでググれば理由は分かる。
*4: リストを操作する手続きがなければ役に立たないのである。

No comments:

Post a Comment