星田さんがちと悩んでるようなんでヒントを。
まぁ、前もって言っておきますが、プログラムって実際問題「動けばいい」んですよね。ユーザーは「どんな風に実装されてるのか」なんかは気にしない。
よって「REPLで書く」ってのはコッチ側で勝手に持ってる「矜持」と言って良いかもしんない。
と言う前提の下で。
再帰を使ってるだけで関数型言語感が無い気がするw
うん、関数型プログラミングになってません。
どこが問題なのか、と言うとset!を使っちゃった辺り。
set!は変数を「破壊的変更」しちまうんで、これが出た時点で関数型プログラミングにならなくなります。
問題は、これを使わずにどうやってプログラムすれば良いのか、ですね。
というかこれ、そもそもREPLになってるのか不安になってきた
うん、まぁ、なってませんね(笑)。
何故かと言うとREPLのひな型の「中身」で計算しようとしてるから、です。
要するに「ゲーム処理」の部分を独立させてevalとしてデザインしないとならないわけです。
まぁ、evalと言う名前は使えない(Racketそのものにevalが既にあるから壊せない)ので別の名前にしなきゃならないんですが、いずれにせよ、evalはREPLの外側で、独立してデザインしなけりゃなんない。
evalの一般的なデザインのコツは次の三点です。
- 引数は最低でも2つ取る。1つは入力情報(read)を受け取る引数(ここではxとしてみる)で、もう1つ(別に1つじゃなくてもいいが)ゲームに関わる環境情報(ここではenvとしてみる)である。
- 環境情報envは単一の引数にしてもいいし、複数の引数にしても良い。これは、データ形式は普通の単一データ(数値とか文字列とか)にして、バラバラにしてもいいし、構造体、リスト、あるいはハッシュテーブルで纏めて1つとしても良い事を示してる。この辺は好みで。
- 返り値で何を返すか考えるが、基本は環境情報envそのものを返す。
だからevalはたとえば
(define (engine x env) ...
みたいにして書き始めます。
んで、星田さんの方針から言うと、envは「石の数」と「どっちのプレイヤーか」の2つの情報が必要です。
それで、envをdisplayに手渡す前に一旦letで束縛して、
(let ((env (engine (read) env))) ...
env情報を利用してformatしてからdisplayに手渡した方がいいかな?