Scheme

出典: フリ??科書『ウィキブックス(Wikibooks)』

?象?者 [ 編集 ]

このペ?ジではプログラミングのまったくの初心者、もしくは他のプログラミング言語は知っているがSchemeについて知識がないかたを主な?象者として、Schemeプログラミングを素早く習得できるように解?していきます。

Schemeの紹介 [ 編集 ]

Scheme (スキ?ム)とは w:プログラミング言語 のひとつです。 Schemeの最大の魅力はそのシンプルさにあります。おそらく( Brainf*** などのジョ?ク言語を除けば)現存するあらゆる言語のなかでも最も言語仕?が小さい言語で、つまりはもっとも習得のしやすい言語であるといえます。また、?用にもじゅうぶん耐えうるだけの機能を持ち、プログラミングの?しさを??するにはまさにうってつけです。すでに C言語 Perl などの他の言語を習得している方にとっても、プログラミングの理解を深める上で?えておいて損はない言語です。

この項ではSchemeの知識がゼロの?態から、最低限必要なことだけを最短で理解できるように解?していきます。この項では基本的だがしかし本格的なプログラミングの?念を?び終えるのに、初?者の方でも半日とかからない分量にしています。 さあ、Schemeでプログラミングをあっという間に身に着けてしまいましょう!

Schemeは2015年現在に至るまで何度か改良が加えられ、Revised7 Report on the Algorithmic Language Scheme(R7RS)という仕?書がもっとも新しいものです。ここではR7RSに準じて?明していきます。仕?の詳細については 外部リンク の項を?照してください。

?理系に?れてみる [ 編集 ]

プログラムを?行するには、その言語に??した何らかの?理系(プログラムを?理する ソフトウェア 。ここではプログラムを?行するもののこと)が必要です。プログラムの構文解?に入る前に、自分でプログラムを入力して確かめることができるように インタプリタ の使い方を?えましょう。 フリ?ウェア として公開されている?理系も多いので探してみましょう。?理系を探すには 外部リンク の項も?照してください。

もし?理系のインスト?ル作業が億劫であれば、Webブラウザ上でSchemeプログラムの?行を試せるサイトがあります。次の「 codepad 」というサイトでは、(1)ペ?ジ左のオプションボタンから、「Scheme」を選?する (2)テキストボックスにプログラムを入力する、もしくはコピ?アンドペ?ストする (3)「Submit」ボタンを押す という手順を踏むだけで?理系のインスト?ル作業なしにSchemeの?行を試すことができます。この項目程度の?容であれば「Codepad」でもじゅうぶん事足ります。

ためしに「codepad」を使ってみましょう。「Scheme」を選んでからテキストボックスに「(display "Hello, World!")」と入力し、「Submit」ボタンを押すと次のような結果が表示されます。

このコ?ドを?行したところ、「Hello, World!」という文字列が出力された、ということです。他のコ?ドを試してみる場合、「(display "Hello, World!")」の「"Hello, World!"」の部分を表示したい値に置き換えてください。たとえば、「(display (+ 5 9))」を入力すると「14」が出力されます。

他のインタプリタの大まかな使い方を?明します(以下は「Codepad」の使いかたではありません)。Schemeの?理系が入手できたら、そのヘルプにしたがって起動してみましょう。たいていのインタプリタでは、起動すると「>」記?が表示されてユ?ザからの入力待ちになります。

>

では、インタプリタに「(+ 1 2)」と入力してみましょう。たいていの?話式の?理系では改行すると?行します。

>(+ 1 2)
3
>

「3」が表示され、再びユ?ザからの入力待ちになりましたね。Schemeプログラム「(+ 1 2)」の?行結果が「3」だったということです。プログラムを入力して改行すると、入力されたプログラムが?行され、次の行に先ほどのプログラムの?行結果が表示されます。これを繰り返してSchemeプログラミングを進めていきます。

Schemeプログラムの構造、意味と評?の過程 [ 編集 ]

プログラムとはおおまかに言えば命令と式の羅列です。?値計算の時にはまさに?式を書いていくのですが、プログラミング言語には、文字列から一部分を切り取ったり、リストからデ?タを取り出したりと、?値計算ではない式があります。下?を見ればわかるように、??で使われる?式の表現と??するものが多くあります。?式に??するものをSchemeで書けば、それがまさに?値計算をするプログラムになります。

??とSchemeの??する主な表現
意味 ??の表記 ??するSchemeの表記
??の適用 ( f x y )
??の値の更新 なし ( set! x y )
等値性の?査 ( = x y )
??の定義 ( define ( f x y ) ( + x y ))
?算 ( * a b )

Schemeでは一貫して括弧の入れ子構造になっているのがおわかりになると思います。これがまさにSchemeの言語仕?がシンプルであるという所以です。Schemeではプログラムの?行を進めていくことを「評?」と呼びますが、これも簡?に言えば?式を?形して簡?にしていくことに似ています。たとえば、??では次のように式を?形し、値を求めていきます。

これ以上簡?にしようがない「4」が出てきた時点で?形は終了です。これで答えが求まりました。このような?形をSchemeで表せば次のようになります。

(+ 1 (+ (- 4 2) 1))
       ↓
(+ 1 (+ 2       1))
       ↓
(+ 1 3            )
       ↓
4

??の表記とは?字や記?の順番が異なりますが、何となく似た雰??はつかめると思います。入れ子になった括弧は、??と同?に?側から評?してきます。プログラムといっても、??の式を?特の表記で書き換えたようなものなのです。ただ、?際は式?形の途中?過は見せず、?座に「その式の値」つまり答えのみが出力されます。以下は?際の入力と出力の一例です。

>(+ 1 (+ (- 4 2) 1))    ←入力(プログラム、式)
4                       ←出力(答え、式の値)

この式を「codepad」で試すときは「(display (+ 1 (+ (- 4 2) 1)))」と入力してください(「display」はこの式の評?結果を表示させるという意味です。詳しくは後ほど解?します)。

このように評?して値を得ることを「値が返る(値を返す、値を?す)」、返ってきた値を「返り値(?り値)」などともいいます。ソ?スコ?ド上でいえば、「値が返る」とは「ある式を評?し、その式の部分をその値で置き換える」ことだと考えるとわかりやすいかもしれません。

式の要素 [ 編集 ]

プログラムとは式そのものですから、 w:ソ?スコ?ド では?字や文字列といったデ?タを記述していく必要があります。プログラミング言語にはそれぞれこのようなデ?タを書くための構文があり、このコ?ド中に直接書かれた値を「リテラル」(literal)と呼びます。

ここでは最低限必要と思われるものだけ解?しますが、以下で紹介される以外にもいろいろな表現があります。詳しくは仕?を?照してください。また、以下の中にはまだ使い方が?明されていないものがありますが、それは後述します。

?値リテラル [ 編集 ]

Schemeでソ?スコ?ド中に?値を記述するには、そのまま半角?字で表記し、これを「?値リテラル」といいます。小?は小?点をピリオドで入力し、負の?を示すマイナス記?(-)も使えます。「2004」「3.14159265358979」「-273.15」などです。ただし、入力した?値が必ずしも入力したとおりの精度で扱われるとは限りません。頭に「#e」をつけ完全な精度で扱うことを指定することもできます。

?値型は、整?型integer、有理?型ratioral、??型real、複素?型complexの構造を持っており、右のものは、すべての左のものを含みます。

文字列リテラル [ 編集 ]

文字列の値を記述する場合は、その文字列をダブルクォ?テ?ション " で?んで記述します。これを「文字列リテラル」といいます。これはソ?スコ?ド上で?値や??(後述)と?別するためであり、?際の評?にこのダブルクォ?テ?ションが影響することはありません。たとえば、 "古今" "東西" というふたつの文字列をつなげると、 "古今""東西" ではなく "古今東西" となります。また、 123 は?値ですが "123" は文字列です。さらに、 "Hello." "こんにちは。" なども文字列です。

特別な文字を表す表現もあります。たとえば改行は \n 、タブ文字は \v 、逆スラッシュは \\ と入力します。また、これらの特別な文字は文字列リテラルの中では直接入力できません。つまり、 "(改行)" と書くと構文エラ?になります(エラ?にならない?理系もあります)。

リテラルを評?すると、そのリテラルそのものが示す値を返します。 1 を評?すると 1 "こんにちは" を評?すると "こんにちは" がそのまま返ります。

文字リテラル [ 編集 ]

?一の文字を表現するには文字リテラルを使います。これは #\ に任意の一文字を?けて表記します。たとえば #\a a を表します。また、スペ?スを #\space 、タブ文字を #\tab で表します。 これは文字列リテラルとは扱いが異なりますので注意してください。たとえば "a" #\a はどちらも?面に表示させると a ですが、それぞれ「 一文字の 文字列」と「文字」で異なるので注意してください。

??値リテラル [ 編集 ]

?件が?か?かを表すには、??値型の値を使います。 #t は?、 #f は?を表し、?件によって?理をわけるときなどに使います。ただし、 #f 以外のすべての値は?として扱われます。これを利用すると、失敗したり無?だったときは?を返し、それ以外は何か別の型の値を返す、といった??を??できます。

式が??値型か調べるには、??boolean?を利用します。

コメント [ 編集 ]

ソ?スコ?ド中には コメント と呼ばれる注?を書くことができます。コメント部分はプログラムの評?に一切??しません。Schemeでは ; からその行末までがコメントです。

>(define hoge 10) ;ここがコメント
hoge

Wikipedia:サンドボックス#ここから下に書き?んでください。 注?を書く用途のほか、プログラムの一部分を一時的に評?しないようにするためにも使われます。もし評?して欲しくない部分を?純に削除してしまうと、あとで?そうと思ったときに書き直さなければならず手間がかかるからです。コメントにしておけば先頭のセミコロンを削除するだけで元に?せます。このように評?して欲しくない部分をコメントにすることをコメントアウトと呼びます。

手?き [ 編集 ]

Schemeには「手?き」(procedure)という?念があります。これは幾つかの?理を行いその結果を返すまとまりで、 w:?? における w:?? と非常に良く似ています。たとえば、??では「f(x,y) = x + y のとき、 f(1,2) = 3である」などといいますよね。Schemeでは f(x,y) = x + y の部分を「手?きの定義」、 f を「手?き」、手?きの評?に必要な値を受け取る x y を「?引?」と呼びます。 f(1,2) 」の部分を「手?きの呼び出し」、 1 2 のように手?きの評?に使われる値を「引?」、?行した結果である 3 を「返り値」といいます。 では、手?きの呼び出しを表現してみましょう(?は下記の手?きの解?には幾つか方便が含まれています。ですが、ここで詳細を解?すると難しくなりすぎるので、詳しくは後述します。手?きの定義は少し難しいので後回しにします)。

手?きはただ命令や?式の列をまとめる役目だけではありません。Schemeの豊富な機能は手?きを介して提供されているのです。また、あなたの書いたプログラムの機能を他のプログラムに公開するとき、それは手?きの定義によって行われます。Schemeの機能を呼び出すとは、用意された手?きを呼び出す構文を書くことと同義です。

手?きの文法 [ 編集 ]

Schemeの手?きの呼び出しは (手?き名 引?1 引?2 ……) という文法です。手?き名は??です。手?きがどんな引?を取るのかは手?きによって異なります。手?き名やそれぞれの引?の間はひとつ以上の 空白文字 で?切らなければなりません。手?き名や引?の間に空白文字がないと、?切りが分からなくなってしまうからです。空白文字とは 改行 タブ文字 半角スペ?ス の3つのいずれかです。この構文はどんな手?きでも同じです。Schemeのほとんどの手?きは、その引?が評?されてから手?きに渡されます。

手?き呼び出しの丸括弧は??の優先順位を示す括弧とは異なり、省略 できません 。このため、??のような?算が加算に優先する、といった優先順位はSchemeには 存在しません 。この仕?は記?の優先順位を?える必要がない反面、?式を煩?にしがちで、Schemeらしい点でもあります。手?きの呼び出しが何重にもなると括弧の?を間違いやすいです。括弧は ( ) がきちんと?になっていなければなりませんから?をつけましょう。SchemeのようなLisp系の言語は括弧だらけになるから苦手、という人も少なくないです。

?際に試してみる [ 編集 ]

Schemeには加算をする手?き「+」が予め定義されています。さっきの構文にのっとると、??での はSchemeでは (+ 1 2) と表記されます。?理系で?行して試してみましょう。できたら 以外にも試してみましょう。引?の和が返ってくるはずです。

>(+ 1 2)
3
>(+ 10 50)
60
>(+ 2004 2004)
4008
>(+ -25 10)
-15
>(+ 3.141592 -273.15)
-270.008408

なんだか?な構文だと思われるかもしれませんが、これらの構文は S式 と呼ばれ、Schemeの構文のシンプルな言語仕?を支えています。「S式」は前置記法と呼ばれるもののひとつで、手?き名にあたるものが先頭に?ます。

「前置記法」の他に「中置記法」や「後置記法」もあります。C言語などは、??のような 1 + 2 という感じの中置記法が中心ですが、3種類全ての記法が入り混じっています。これに?してSchemeでは、前置記法のみしか使わないシンプルな言語仕?になっています。

注意 [ 編集 ]

幾つか手?き呼び出しに?して?をつけておくことがあります。手?きには「手?き名」「引?の?」「引?の型」「返り値の型」などの要素を持っています。たとえば、幾つ引?をとるかは手?きごとに決められており、多すぎたり少なかったりすると?行したときに w:エラ? になります。ただ、たまたま「+」は引?が幾つあってもよい手?きです。 また、手?きは引?の型が決まっています。たとえば、 w:加算 をする手?き + は文字列を引?に呼び出すとエラ?です。 たまたま「+」は引?の順番を?えても同じ値が返ってきますが、ほとんどの手?きは引?はその順番に意味があります。たとえば、 - は1つ目の引?の値から2つめ以降の引?の値を減算する手?きなので、 (- 10 5) (- 5 10) の値は違います。 Schemeには予め幾つかの手?きが定義されており、ユ?ザは新たに手?きを定義することもできます。そのScheme?理系にどの手?きが用意されているか確かめるには、その?理系の w:ヘルプ と言語仕?を確認する必要があります。

束縛 [ 編集 ]

束縛とは??に値を?連付けることです。例を見てみましょう。手?きdefineは第一引?の??に第2引?の値を束縛します。値が束縛された??を評?すると、その??に束縛された値が返ります。つまり、束縛された値を取得するには、?にその??を書きます。

>(define year 2004)
year
>(+ year 1)
2005

??yearには? 2004 が束縛されましたので、??yearが評?されるとyearに束縛された値 2004 が返ります。手?き+は 2004 1 の和 2005 を返します。今まで?明に使ってきた手?き+も、じつはインタプリタ起動と同時に+に加算をする手?きが束縛されていたから使えるのです。手?きも一種の値として扱えるため、defineで束縛することができるのです。また、何も束縛されていない??を評?しようとするとエラ?です。

構文 [ 編集 ]

上で束縛を?明しました。が、ここで疑問に思って欲しいことがあります。次の例を見てみましょう。

>(define year 2004)
year
>(define year 1000)
year

下のdefineでは、yearはすでに2004が束縛されています。手?きは引?を評?してから渡すのですから、yearは2004を返すはずです。?って、 (define year 1000) (define 2004 1000) になるはずではありませんか。2004は??ではないので、束縛はできないはずです。いや、そもそも最初のdefineではyearには何も束縛されていませんでしたから、なにも束縛されていない??yearが評?されてエラ?になるはずです。これはどうしたことでしょうか。

?はSchemeには手?き呼び出しと同じような構文でありながら、引?を評?せずに受け取る手?き呼び出しとはまったく別の式も存在します。defineは引?(のように見える部分)は評?しないのです。defineは手?きではなく、定義を行う「構文」に分類されます。

リスト [ 編集 ]

Schemeなど w:Lisp 系の言語が何故これほどまでにシンプルな構文にこだわったかには?があります。Schemeプログラムは「リスト」と呼ばれるツリ??の構造をとるようになっているのです。Lispが w:人工知能 ?究の分野に使われてきたのは、プログラム上でプログラムを組み立てるのが非常に容易だからです。

たとえば、 (+ 1 (- 2 3)) という式は、?は次のようなリストです。

グレ?の矩形は w:carとcdr の二つの??を持つ「ペア」と呼ばれるものです。car部はそのペアが持っている値、cdr部はその次の要素を格納していると捉えることができます。car部には別のペアを格納することができます。cdr部は次の要素を示しますが、リストの終端を示すには空リストを使います。?中では () で表されているのが空リストです。Lispプログラムはこの構造の繰り返しであり、このツリ?構造を作り上げることでLispプログラムを作ることができるのです。 car cdr set-car! set-cdr! list quote などの手?きを使えば、Schemeプログラムをそのままリストとして扱えます。?はペアを直接作成する?用の構文もあるのですが、知らなくても今のところは構わないので、ここでは割愛します。

Schemeの重要な手?きや構文 [ 編集 ]

?は、以上で大まかな文法は?明し終わりました。Schemeの文法はほとんど w:S式 なのです。まだまだ?明していない?念はいくらでもあるのですが、それらの機能はすべて手?きや構文で提供されるのです。たとえば、?値の大小を比較するのも手?きですし、文字列を表示するのもまた手?きです。 w:GUI w:ライブラリ が提供されれば、ウィンドウを開いたりファイルを操作したり?像を表示したりするのもまた、手?きを介して行われます。

とはいえ、?際にあなたが望む機能を?現するには、どの手?きをどのように使えばいいのかわからないことでしょう。あとあなたに必要なのは、どんな手?きや構文が用意されているのか知り、具?的なプログラムを?んだり書いたりしてプログラミングの??を積むことです。そのためには言語仕?を?めるようになる必要があります。ですが、以下でかんたんに主な手?きや構文を紹介しておこうかと思います。このほかにも有用なものがたくさんありますので、すべての機能を知るには言語仕?を?照しましょう。

cons, car, cdr [ 編集 ]

Schemeの最も重要な手?きはペアをつくり、ペアから要素を 取り出す次の3つです。

  • cons ペア(dotted pair)をつくる
  • car pairの最初の要素を取り出す
  • cdr pairの次の要素を取り出す

consはペアを作る手?きです。 (cons 1 2) => 1 . 2

このペアから無限に?くリストが作れます。リストの終わりを () という空リストで示すこととすれば、1 2 3と?くリストは、 (cons 1 (cons 2 (cons 3 '()))) と定義され、 (1 2 3) と表現されます。 特に、Schemeでは、リスト構造が根本であり、それは、リストは再?的(recursive)に定義される構造だからです。

 (
cons
 1
 2
)
 =>
 (
1
 .
 2
)

 (
car
 (
cons
 1
 2
))
 =>
 1

 (
cdr
 (
cons
 1
 2
))
 =>
 2

 (
cons
 1
 (
cons
 2
 '
()))
 =>
 (
1
 2
)

 (
car
 (
cons
 1
 (
cons
 2
 '
())))
 =>
 1

 (
cdr
 (
cons
 1
 (
cons
 2
 '
())))
 =>
 (
2
)

この例だけ見ると、リストの重要性は判らないかも知れません。

四則演算 [ 編集 ]

  • + 加算
  • - 減算
  • * ?算(「×」ではなく「*」を使います)
  • / 除算(「÷」ではなく「/」を使います)

これらの w:四則演算 の手?きは、整?や小?など?値型の値に適用できます。これだけでもSchemeを簡易電卓として使えそうですね。

display [ 編集 ]

displayは引?に?えられた値を出力します。プログラム?行中の途中?過を表示したりする場合にも便利です。下の例では結果が1010となっていますが、displayが表示した10とインタプリタがプログラムから得た値10が、改行していないのでつながって見えるだけです。文字列や??も引?として使えます。

>(display 10)
1010

表示できるのは、Schemeの?理系が受け付けるすべての S式です。?値、文字、文字列、クオ?トされたリストなど、何でも表示できます。ただし、??は?部表現が?理系?に異なることに注意して下さい。 (display 10) の評?値は、未定義です。他の??に束縛する意味はありません。複?の値を表示するとき、Scheme では以下のように書くことができます。

>(define x "Xvalue")
>(for-each display `(1 2 3 x ,x "\n" "newline"))
1 2 3 x Xvalue
newline

準引用により , が前に付くと、値が展開されます。それから

>(display 10 output-port)

と書けば、出力先を?更できます。output-port は、?? open-output-file の返り値を指定します。

lambda [ 編集 ]

lambdaは手?きを新たに定義し、その手?きを表す値を返す構文です。Schemeは手?きであっても、?値や文字列と同じように値として?理することができます。lambda構文の第一引?には手?きの引?名となる??のリストを渡します。第2引?以降は手?き本?となる式を渡します。ここで作成された手?きは一種の値なので、defineで束縛することができます。サンプルプログラムを示します。

> (define average (lambda (x y) (/ (+ x y) 2)))
average
> (average 10 20)
15

新たに定義した手?きは二つの引?をとり、その平均を返します。その手?きは??averageに束縛しています。インタプリタは (average 10 20) を手?き呼び出しだと判?し呼び出しました。前述では手?き名には??を記述すると解?しましたが、?際には手?きの値をとる式を書きます。averageには手?きが束縛されているため、リストの先頭に書くことができます。?って次のようなこともできます。

> ((lambda (x y) (/ (+ x y) 2)) 10 20)
15

平均を計算する無名の手?きを定義し、返り値をリストの先頭に使いその手?きを呼び出しています。

手?きは引?や返り値にも使えるため、手?きを返す手?きも作成できます。下の例では、最初に平均を計算する手?きを返す手?きを作成し、それを??create-average-functionに束縛しています。そして、2つめ、3つめで??create-average-functionに束縛されていた平均を計算する手?きをつくる手?きに、まず加算と?算の定義を?え、平均を計算する手?きを得ます。そして、その手?きに引?10と20を?えて、具?的な平均値を計算しています。

>
 (
define
 create-average-function
 
     (
lambda
 (
add
 multiply
)
 
       (
lambda
 (
x
 y
)
 (
multiply
 (
add
 x
 y
)
 (
/
 1
 2
)))))

create-average-function

>
 ((
create-average-function
 +
 *
)
 10
 20
)
 ; 相加平均

15

>
 ((
create-average-function
 *
 expt
)
 10
 20
)
 ; 相?平均、exptは累?を計算する組み?み??。

14.1421356...

quote [ 編集 ]

quoteは引?に?えられたものを評?せずにそのまま返す構文です。

>(quote (+ 1 2))
(+ 1 2)		;(+ 1 2)は評?しないと(+ 1 2)というリスト
>(define hoge 10)
hoge
>hoge
10		;quoteを使わなければ、??は束縛されている値を返す
>(quote hoge) 
hoge		;quoteを使うと、??は??そのものを返す

特別な構文(Schemeの機能の「構文」ではなく、一般的な意味の構文)として、 '(+ 1 2) と書くことができます。 (quote (+ 1 2)) まったく同じ意味ですが、字?が少なく見やすくなります。このように、書きやすさ、見やすさのために導入された構文を w:糖衣構文 (シンタックスシュガ?)といいます。

?連項目 [ 編集 ]

外部リンク [ 編集 ]

Wikipedia
Wikipedia
ウィキペディア Scheme の記事があります。
このペ?ジ「 Scheme 」は、 まだ書きかけ です。加筆?訂正など、協力いただける皆?の 編集 を心からお待ちしております。また、ご意見などがありましたら、お??に ト?クペ?ジ へどうぞ。