整数のリストを作る

たまには Scheme を。

Scheme で整数のリストを作るには iota 手続きが使える。

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

これを自前で実装してみよう。
最初に作ったのがこれ。可変長引数なので、引数の数に応じて対応する補助手続きを呼び出している。

(define my-iota
  (lambda (n . args)
    (cond ((null? args) (my-iota-simple n))
          ((= (length args) 1) (my-iota-start n (car args)))
          (else (my-iota-step n (car args) (cadr args))))))

(define my-iota-simple
  (lambda (n)
    (let loop ((start 0) (i (- n 1)) (lis '()))
      (if (< i start)
          lis
          (loop start (- i 1) (cons i lis))))))

(define my-iota-start
  (lambda (n start)
    (let loop ((i (- (+ start n) 1)) (lis '()))
      (if (< i start)
          lis
          (loop (- i 1) (cons i lis))))))

(define my-iota-step
  (lambda (n start step)
    (let loop ((i (+ start (* (- n 1) step))) (lis '()))
      (if (< i start)
          lis
          (loop (- i step) (cons i lis)))))) (print (my-iota 10))

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

実行例:

^o^ > gosh my-iota.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)

上の実装では3つの補助手続きを定義しているけど、内容は同じようなものなので1つにまとめることにした。つまり、引数を3つとる補助手続き my-iota-general を定義しておいて、本体手続き my-iota のほうで場合分けをして呼び出す。

(define my-iota
  (lambda (n . args)
    (cond ((null? args) (my-iota-general n 0 1))
          ((= (length args) 1) (my-iota-general n (car args) 1))
          (else (my-iota-general n (car args) (cadr args))))))

(define my-iota-general
  (lambda (n start step)
    (let loop ((i (+ start (* (- n 1) step))) (lis '()))
      (if (< i start)
          lis
          (loop (- i step) (cons i lis))))))

(print (my-iota 10))
(print (my-iota 10 2))
(print (my-iota 10 2 2)) 
^o^ > gosh my-iota2.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)

だいぶシンプルになった。もう一歩進めてみよう。上の my-iota 手続きの中では場合分けをして my-iota-general の引数 start、step を決めているので、let で局所変数にしてしまえば my-iota-general の本体部分を取り込むことができる。

(define my-iota
  (lambda (n . args)
    (let ((start (if (>= (length args) 1) (car args) 0))
          (step (if (>= (length args) 2) (cadr args) 1)))
         (let loop ((i (+ start (* (- n 1) step))) (lis '()))
           (if (< i start)
               lis
               (loop (- i step) (cons i lis)))))))

(print (my-iota 10))
(print (my-iota 10 2))
(print (my-iota 10 2 2)) 
^o^ > gosh my-iota3.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)

めでたく1つの手続きにできた。