Schemeでn回の繰り返し(の返り値)

※追記あり

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-&gt;number (cadr args))))))

hello 関数が返すのは “Hello!” のリストで、それを main 関数のほうで出力している。

^o^ > gosh hello-loop2.scm 3
Hello!
Hello!
Hello!

良くなった。