「48時間でSchemeを書こう/Scheme関数の定義」の版間の差分

削除された内容 追加された内容
編集の要約なし
編集の要約なし
1 行
 今、私たちは変数を定義できるようになったので、関数をもっとよく拡張することができるでしょう。このセクションのあとに、私たちはSchemeの中でオリジナル関数を定義し、他の関数から使うことができるようになるでしょう。私たちの実施は、終わりに近くになります。
 
 新しいLispValコンストラクタを定義するところから始めましょう。
7 ⟶ 8行目:
 
 私たちはprimitivesにセパレータコンストラクタを追加した。というのも、私たちは +やeqv?等のように、扱いたいからです。変数は、関数に渡されます。PrimitiveFuncコンストラクタはThrowsError LispValに対して、引数のリストを取るような関数を扱います。それはprimitiveリストで扱われているのと同じタイプです。
 
 私たちは同様に、ユーザー定義型関数を扱うためのコンストラクタが欲しい。私たちは4つの情報を扱います。
 
15 ⟶ 17行目:
 
 これは[http://www.haskell.org/hawiki/UsingRecords record]型の例になります。RecordsがHaskellはちょっと不器用ではありますが、目的をデモンストレーションするために、彼らを使うのは適切です。しかし、彼等は大きなスケールのプログラミングには重要でもあります。
 
 次に、私たちは新しい型を含んだ<span class="inline_code">show</span>関数を編集しましょう。
showVal (PrimitiveFunc _) = "&lt;primitive&gt;"
25 ⟶ 28行目:
 
 全部の関数を出す代わりに、私たちはただprimitivesに対して"&lt;primitive&gt;"という単語を出し、そしてユーザー定義型関数のために、ヘッダーへと入れています。これは、Recordsのためのパターンマッチングの例の一つでもあります。そして一般的な代数タイプは、まさにパターンがコンストラクタの呼び出しのようになっています。フィールド名は、最初にやってきて、そして後には変数名が束縛されます。
 
 次に、<span class="inline_code">apply</span>を変化させる必要がある。関数の名前を渡す代わりに、実際の関数に置き換えられるLispValを渡すようにすると、コードがシンプルになります。私たちに必要なのは、ただ関数が吐き出す値を読むことであり、それを適用することなのです。
44 ⟶ 48行目:
 
 この関数が行う一番始めのことは、期待されている引数の数に対して、パラメーターの長さをチェックことです。もしマッチしていないのならば、エラーを吐きます。私たちは、より読みやすくするためのローカル関数である<span class="include_code">num</span>を定義し、このプログラムに短い断片で作ってみましょう。
 
 呼び出しがされた途端に出てくると、呼び出しの大半はモナディックパイプの中で、引数が新しい環境に束縛され、関数のボディの中にある式が評価されることになります。私たちが行うこの処理をパラメーターの名前や(既に評価された)引数の値をペアのリストを通ってzipするということになります。だから、私たちはこれを取りますし、関数のクロージャ(現在の環境では、まだ出来ません。これは私たちに辞書型スコープがないといけません)、そして関数の中で評価するための新しい環境を作るために、彼らが使われるでしょう。関数の全体としてはIOThrowsErrorである、IO型が結果となり、そして私たちは複合モナドの中にliftIOする必要があるわけです。
 今や、残された引数を、ローカル関数として使われているbindVarArgを使って、varArgs変数変数に束縛するときです。もし関数がvarArgsを取れないのなら(Nothing節のことですね)、私たちはただ存在している環境を返します。同様に、私たちは、変数の名前をkeyとして、残った引数を値として、単独のリストを作成し、bindVarsに渡します。既に変数へ束縛されている引数全てを無視するための、ビルドイン関数である、[http://www.haskell.org/onlinereport/standard-prelude.html#$vdrop drop] を使うことを、可読性のために、<span class="inline_code">remainingArgs</span>は、ローカル変数として定義しています。
 
 最後のステージは、この新しい環境の中で、関数のボディーを評価することです。そのために、ローカル変数である<span class="inline_code">evalBody</span>を使います。これは、関数のボディの中で、モナディック関数である<span class="inline_code">eval env</span>に対して、全ての節をマッピングし、最後の節の値を返します。
 今や変数の中で、primitivesを、普段の値として扱うことができるので、プログラムがスタートしたときに、これらを束縛する必要がでてきます。
70 ⟶ 76行目:
 
 ここで、makeNormalFuncとmakeVarArgsはただ、普通の関数と、変数の引数の対応に対して、最初の引数をふさわしくセットするという、makeFuncの特殊さを尊重するべきです。これは、シンプルなコードで最初のクラス関数を如何にして使うか、という良いサンプルです。
 
 今、私たちは更なるeval節を追加することで、これらを使えるようにしましょう。彼らは、変数を定義する節の後、そして関数が適応される前に追加されるべきです。
88 ⟶ 95行目:
 
 これらは、かたちを解きほぐすためのパターンマッチングを使って、適応する関数の手助けをします。この定義の場合ですと、私たちは同様に、ローカル環境の中で変数を束縛できるよう、アウトプットをdefineVarに流し込みます。私たちは同様に、liftHtrowsを外すために、関数を適応する節を編集する必要があります。というのは、applyは既にIOThrowsErrorかモナドの中で動いているからです。
 
 さて、コンパイルして、プログラムを走らせることが出きるようになりました。なので、これを使ってリアルなプログラムを書いてみましょう!!