※追記あり
Scheme では繰り返しを再帰で実現する。じゃ、n 回の繰り返しはこう書けばいいだろうか。
(define hello
(lambda (n)
(let loop ((m n))
(if (= m 0)
#t
(begin
(print "Hello!")
(loop (- m 1)))))))
(define main
(lambda (args)
(hello (string->number (cadr args)))))
このスクリプトは、引数の回数だけ “Hello!” を繰り返し出力する。
^o^ > gosh hello-loop.scm 3 Hello! Hello! Hello!
確かに n 回の繰り返しを行っていて、目的は達成している。けど、気になるのは hello 関数の返り値だ。ここでは便宜的に(というか苦し紛れに)#t を返しているけど、なんだか気持ちが悪い気がする。Scheme 的にはどうなんだろうか。
[追記]
Kei さんからコメントをもらった。返り値が要らないなら when か unless を使えばいいとのこと(未定義値が返ってくる)。
gosh> (define (hello n)
(let loop ((i 0))
(unless (= i n)
(print "Hello!")
(loop (+ i 1)))))
hello
gosh> (hello 3)
Hello!
Hello!
Hello!
#<undef>
確かに #<undef> が返ってきている。この方がしっくりくるな。when を使うとこうかな。
gosh> (define (hello n)
(let loop ((i 0))
(when (< i n)
(print "Hello!")
(loop (+ i 1)))))
hello
gosh> (hello 3)
Hello!
Hello!
Hello!
#<undef>
なるほど。うん、unless と when を覚えた。Kei さんありがとうございます。
と、ここまで書いといて何だけど、そもそも print を繰り返すこと自体が Scheme 的じゃない気がしてきた。なのでこう書き直してみた。
(define hello
(lambda (n)
(let loop ((m n) (ls '()))
(if (= m 0)
ls
(loop (- m 1) (cons "Hello!" ls))))))
(define main
(lambda (args)
(for-each print (hello (string->number (cadr args))))))
hello 関数が返すのは “Hello!” のリストで、それを main 関数のほうで出力している。
^o^ > gosh hello-loop2.scm 3 Hello! Hello! Hello!
良くなった。
戻り値が要らないのであれば、when/unlessを使って以下のように書くことも可能です。
(define (hello n)
(let loop ((i 0))
(unless (= i n)
(print “hellp”)
(loop (+ i 1)))))
これなら見た目上は値を返さない手続きになります。(実際には未定義値が返ります。)
仕様の話をすると、 Scheme で「戻り値が未規定」となっているものは何が返ってくるか決まっておらず値の個数も決まっていません。 unless が #t や #f や、あるいはでたらめな数値を返したとしても仕様には違反しないのです。 もしかすると 2 個以上の値 (多値) が返ってくることも有り得ます。
ただ、 REPL で評価して返された値が全く無関係なものだとわかり難いということもあって多くの処理系は便宜的に「未規定であることを表す値 (オブジェクト)」を用意しています。 Gauche でもまたそのようなアプローチをとっているということです。
戻り値に意味がないことを表したいときによく用いられるイディオムとして、 (if #f #t) を使うというのがあります。 これの戻り値は未規定なので、戻り値が未規定の場合を各処理系がどう扱っているにしてもそれに準じた動作になるからです。