Let's start Scheme

(はじめよう Scheme 1)

プログラムの構成


ここでは簡単な例とともにSchemeのプログラムがどのように構成されているかを示し、いかにしてユーザー定義の手続きを作成かを示す。

Hello world


まずは伝統的なHello worldを表示してみよう。
(import (scheme base) (scheme write)) ;; (1)

(display "Hello world")               ;; (2)
(newline)                             ;; (3)
これをファイルに保存してスクリプトとして走らせると、以下のような出力が得られる。
% sash hello.scm
Hello world
では、プログラムの構成を順に見ていこう。
(import (scheme base) (scheme write)) ;; (1)
ライブラリ(scheme base)(scheme write)を使うという宣言。importについてはライブラリの説明の際に詳しく述べるので、ここではおまじないとして覚えておけばよい。
(display "Hello world")               ;; (2)
手続きdisplayを文字列"Hello world"引数に呼び出す。Schemeでは*1手続きの呼び出しは必ず以下の規則に従っている。
(procedure arg1 arg2)
 ^^^^^^^^^ ^^^^^^^^^
呼び出し手続きprocedureが必ず括弧の先頭に来て、それに続いて引数arg1arg2が与えられる。Schemeでは内側の括弧から順に評価されることと手続きも値として使うことができるので、以下のように書くこともできる。
((if #t proc1 proc2) arg1 arg2)
構文ifについては後述するが、この例では手続きproc1の呼び出しは(if #f proc1 proc2)の評価が完了した後に行われる。

内側の括弧が必ず先に評価されるというのはいくらか便利な点もある。四則演算をしてみよう。小学校のときに乗算、除算は先に計算して加算、減算はあとから計算するというのルールを習ったのを覚えているだろうか?こんな式があったとする:1 + 2 * 3 + 4。これはSchemeでは以下のように掛ける。
(+ 1 (* 2 3) 4)
前から順番に読んでいき、括弧がでたらその計算を先に行う。普通に数式を書いて乗算、除算を探すよりは多少読みやすいのではないだろうか?*2

設問1.1
Hello world以外の文字列を表示するプログラムを作成せよ。

設問1.2
四則演算を行う手続き+-*/を組み合わせて1 + 2 + 3 * 4 / 5 - 6を正しく計算するプログラムを作成せよ。

コラム:手続き?関数?
C言語など他の言語では関数と呼ばれるものがSchemeでは慣例的に手続きと呼ばれている。これは関数は数学的に同一の入力に対して同一の値を返すものであるが、displayのような副作用が発生する手続きではそれが期待できないためである。ここでは慣例に従って手続きという言葉を用いる。

手続きを作る

displayは標準ライブラリで既定されている手続きである。しかし、必要となる全ての手続きを標準ライブラリで網羅するのは不可能である。そこでユーザ定義の手続きが必要になる。

ユーザ定義の手続きを作るにはlambdaを使う。例えば与えられた引数に1を足す手続きを考えてみる。プログラムの構成上、呼び出す手続きは括弧の最初にくる必要があるので、以下のように書くことができる。(おまじないは省略しているが、おまじないも必要である。)
((lambda (x) (+ x 1)) 10)
;; -> 11
毎回このように書いても特に問題はないが、可読性が著しく低い。そこで手続きを定義することにする。定義を書くにはdefineを使う*3
(define add1 (lambda (x) (+ x 1)))

(add1 10)
;; -> 11
ユーザが定義した手続きadd1は他の手続きと同様に使うことができる。lambdaは括弧の二つめの要素に引数のリストを取る。2引数以上を受け取る手続きを書く場合は以下のように引数リストの要素を増やしていけばよい。
;; 2引数
(lambda (x y) (+ x y 1))

;; 3引数
(lambda (x y z) (+ x y z 1))
また、引数名はここでは一文字にしているが一文字である必要はない。適宜意味のある名前をつける方が好ましい。

設問1.3
入力された値を二乗する手続きsquareを作成せよ。

*1: Schemeに限らずLisp族全般に言える(除 MLisp)
*2: 筆者はこの演算子の優先順位を覚えるのがとても苦手である。特にビット演算子。
*3: 正確にはこの表現は正しくない。詳細は次回。

No comments:

Post a Comment