整数のリストを作る(2)-あるいは省略可能な引数の扱いについて

先週のエントリを見て省略可能な引数を扱う方法を書いてくれた人がいる。

 cf. 省略可能引数 – 主題のない日記

そこでは、R6RS/R7RS で定められている case-lambda と Gauche の拡張である let-optionals* が紹介されている。どちらも省略可能な引数を簡単に扱うためのマクロのようだ。

my-iotacase-lambda を使って書くとこうなる。

(define my-iota
  (case-lambda
    ((count) (my-iota count 0 1))
    ((count start) (my-iota count start 1))
    ((count start step)
      (let loop ((c count)
        (i (+ start (* (- count 1) step)))
        (lis '()))
        (if (= c 0)
            lis
            (loop (- c 1) (- i step) (cons i lis)))))))

(print (my-iota 10))
(print (my-iota 10 2))
(print (my-iota 10 2 2))
(print (my-iota 10 2 -2))

ついでに step に負数を指定すると期待通りに動作しない不具合も直しておいた。
実行例:

^o^ > gosh my-iota4a.scm
(0 1 2 3 4 5 6 7 8 9)
(2 3 4 5 6 7 8 9 10 11)
(2 4 6 8 10 12 14 16 18 20)
(2 0 -2 -4 -6 -8 -10 -12 -14 -16)

一方、let-optionals* を使って書くとこんな感じ:

(define my-iota
  (lambda (count . restargs)
    (let-optionals* restargs
      ((start 0)
       (step 1))
      (let loop ((c count)
        (i (+ start (* (- count 1) step)))
        (lis '()))
        (if (= c 0)
            lis
            (loop (- c 1) (- i step) (cons i lis)))))))

(print (my-iota 10))
(print (my-iota 10 2))
(print (my-iota 10 2 2))
(print (my-iota 10 2 -2))
^o^ > gosh my-iota4b.scm
(0 1 2 3 4 5 6 7 8 9)
(2 3 4 5 6 7 8 9 10 11)
(2 4 6 8 10 12 14 16 18 20)
(2 0 -2 -4 -6 -8 -10 -12 -14 -16)

当然ながら実行結果はどちらも同じだけど、どちらかというと case-lambda のほうが好みだな。引数の数でパターンマッチしてる感じがいい。let-optionals* だと省略可能引数の部分だけ別に書くことになるのでコードの見通しが悪いような気がする。

Gauche のマニュアルだと、case-lambdahttp://practical-scheme.net/gauche/man/gauche-refj_24.html の一番下、let-optionals*http://practical-scheme.net/gauche/man/gauche-refj_58.html6.18.4 省略可能引数のパージング にある。