スマホからは書きにくくて挫折しましたwww
コードを書くとかムリすぎるwww
帰宅して気力があったら書きます。たぶん。
こんなプログラムを見た。
(define (read-data) (let ((a (read-line)) (b (read-line)) (c (read-line))) `(,a ,b ,c)))
って自分が書いたダメプログラムですがw
何がいけないかを解説します。
(read-line) は呼び出した毎に標準入力からデータを拾うので毎回戻り値が変化します。副作用を伴う処理です。
そして、Scheme の言語仕様として、let の評価順は未規定です*1。
つまり、このプログラムにテキストファイルを読み込ませたとき、
a には 1行目のデータが入る場合も有り得ますし、
a には 2行目のデータが入る場合も有り得ますし、
a には 3行目のデータが入る場合も有り得るのです。
なんじゃそりゃ!と思うかも知れませんが、
未規定ということはつまりそういうことなのです。
常に一定になるとは限らないのです。
これではプログラムの意図と反しますよね。
つまりバグを引き起こす恐れがある書き方である*2、ということです。
では、どのように書くのが正解でしょうか。
答えは let* を使います。let* は記述の順番で評価されていきます。
普通の使い方としては、
(define (hoge) (let* ((a (+ 2 1)) (b (+ a 4)) (c (+ a b))) `(,a ,b ,c)))
このように、後の評価式で前の評価結果を使いたい場合に使います。
これは let を入れ子で書けば解決するのですが、ソースがとても汚くなるので
let* で同じ事が書けますよってことなのです。
つまり、元のプログラムでは
(define (read-data) (let* ((a (read-line)) (b (read-line)) (c (read-line))) `(,a ,b ,c)))
のように、 let* を使うことによって、
変数 a の束縛が最初に行われ、次に b、最後に c という順序で変数の束縛が行われます。
let* は let と違い、評価順序が明確に決まっているわけです。
これにより、入力データの 1 行目は必ず変数 a に束縛され、
2 行目は変数 b に、3 行目は変数 c に必ず束縛されることが保証される、というわけです。
本日のポイント:
副作用を伴う処理を使用するときは、評価順序に注意しましょう。