練習:do

Scheme 入門 7.繰り返しから、練習問題5。

リストの要素の順番を反転させる関数

(define my-reverse-do
  (lambda (ls)
    (do ((l1 ls (cdr l1)) (l2 '() (cons (car l1) l2)))
        ((null? l1) l2))))

(print (my-reverse-do '(1 2 3 4 5)))
takatoh@nightschool $ gosh my-reverse-do.scm
(5 4 3 2 1)

数のリストの要素の合計を求める関数

(define sum-do
  (lambda (ls)
    (do ((l ls (cdr l)) (s 0 (+ s (car l))))
        ((null? l) s))))

(print (sum-do '(1 2 3 4 5)))
takatoh@nightschool $ gosh sum-do.scm
15

正の整数を表す文字列を整数に変関する関数

(define string->integer-do
  (lambda (str)
    (do ((l (string->list str) (cdr l))
        (i 0 (+ (- (char->integer (car l)) 48) (* i 10))))
        ((null? l) i))))

(print (string->integer-do "12345"))
takatoh@nightschool $ gosh string-to-integer-do.scm
12345

[追記]

do 構文について補足。このあいだは do 構文の体裁を↓こう書いたけど

(do ((変数 初期値 ステップ))
    (終了条件)
    本体)

より正しくはこう。

(do ((変数 初期値 ステップ) ...)
    (終了条件 式)
    本体)

まず、変数は複数定義できる。それから、終了条件の後に式を書くと、式を評価した値が do 構文の値になる。でもって、本体は必要がなければ省略ができる。
上の3つの例ではどれもそうなっている。
Gauche のユーザリファレンス
には次のような例が出ている。

(do ((i 0 (+ i 1))
     (j 0 (+ i j)))
    ((= i 10) j)
    (print j))

この例では、i と j の2つの変数を使って、最終的には j を返している。そして毎回のループごとに本体 (print j) を実行している。

gosh> (do ((i 0 (+ i 1))
           (j 0 (+ i j)))
          ((= i 10) j)
          (print j))
0
0
1
3
6
10
15
21
28
36
45

これを見ると、値を得るには終了条件の後の式を使って、本体は副作用に使うのがいいのかも。