星田さんの記事に対するコメント。
という訳で・・とりあえずレコード形式を使って成績の集計を行うってのは・・うッ(;´Д`)・・動作するのは出来ました。
うん。よく出来てる。
そして「なんかなぁ・・・」って思うのはいい事です。常にリファクタリング出来ないか?って考えるのも良い練習。
こんな風に書きたいんじゃないかな。
(define (shukei lst)
(let loop ((lst lst) (kekka kekka))
(if (null? lst)
(hyouzi_kekka)
(loop (cdr lst)
(begin (case (test-seiseki (car lst))
(("S") (set! (kekka0-s kekka) (+ (kekka0-s kekka) 1)))
(("A") (set! (kekka0-a kekka) (+ (kekka0-a kekka) 1)))
(("B") (set! (kekka0-b kekka) (+ (kekka0-b kekka) 1)))
(else (set! (kekka0-c kekka) (+ (kekka0-c kekka) 1))))
kekka)))))
caseは第一引数で、結果、「与えられたデータのどの部分で判定を下したいのか」指定できる。いまは(car lst)と言う構造体のseisekiスロットがどうなのか調べたいだけ、なんでそう指定する。これでこの部分を何度も書く必要はなくなる。
あとは第二引数以降は「指定したデータが何だったのか」場合分けするだけでいい。今は文字列となってるデータが"S"、"A"、"B"、かそれ以外、しかないんで、それを指定する。あとはそれに従った「操作」を記述していけばいいだけ。
LispのcaseはC言語のswitch文なんかより強力なんで重宝する。Lispのcaseに慣れるとC言語のswitch文はあまりに原始的でお粗末だ。
そしてここまで来ると、「構造体を破壊的変更する」んじゃなくって「構造体を新しく生成しても良い」と言う事に気づくんじゃないか。
(define (shukei lst)
(let loop ((lst lst) (kekka kekka))
(if (null? lst)
(hyouzi_kekka)
(loop (cdr lst)
(let ((s (kekka0-s kekka)) (a (kekka0-a kekka)) (b (kekka0-b kekka)) (c (kekka0-c kekka)))
(case (test-seiseki (car lst))
(("S") (make-kekka0 (+ s 1) a b c))
(("A") (make-kekka0 s (+ a 1) b c))
(("B") (make-kekka0 s a (+ b 1) c))
(else (make-kekka0 s a b (+ c 1)))))))))
Racketのbig-bangで行ってたのはこの形式だ。
ところでLispのcaseとmatchは構文的に非常に似通ってるのが分かるだろう。
実はこの2つは殆ど同じなんだけど、違いは次の点にある。
- caseは第一引数に与えられたデータが「具体的に」何なのか、で処理を分ける。
- match は第一引数に与えられたデータが「どんなパターンになってるか」分解してそれによって処理を継続する。
言い換えると上の例のように具体的にデータが"S"だとか"A"だとか分かってる時はcaseの出番。一方、データが具体的にどう、って分からなくって良い場合がmatchの出番。
「具体」のcase、「抽象」のmatchと覚えておこう。
ちょうど良いのでMatchを使って教科書そのままで置き換えてみるか・・あれ?1は?
うん、パターン記述を忘れてる(笑)。
正解はこう。
2番目のパターンの記述をキチンとしておかないとならない。
そのままいつもの(if (null?を使った形にしたらちゃんと動くよなぁ?と言うか・・(・.・;) 結局いつものこの形で良くない?
うん(笑)。
っつーか翻って言うと、「プログラミングの基礎」で使われてるパターンマッチングの例も実はそこまで旨味はないのよね(笑)。オーバースペックと言おうか。
だからフツーのプログラミングでも、ifやcondやcaseの出番が殆どを占めて、パターンマッチングの出番なんて殆どないかもしんない。だからこそSchemeの仕様に含まれてないわけで。
ところがプログラミングやっていくと分かるんだけど、レアケースでもデータ構造の分解、そしてその内情調査が必要になる、ってのがしばしば出てくるんだよな。
この、「たまに出てくる割にはプログラミングが面倒」ってのはすんげぇ嫌なんだよ(笑)。
こういうケースではifを入れ子にしたりcondを入れ子にしたり・・・とかとかく面倒なのね。
でも、OCaml式のパターンマッチングならデータを分解させる、って簡単じゃない。だからいざとなるとき重宝する。条件文の入れ子をガンガン書かなくて済むわけ。
だから条件分岐系機能としてはパターンマッチングはすごく抽象度が高いの。「プログラミングの基礎」ではその「高抽象性」にまずは慣れましょう、それが「モダンなスタイル」です、ってメッセージで徹頭徹尾パターンマッチングを掛けてるんだと思う。