SRFI-1のiotaっぽいものをCommon Lispで書き直してみた(その3)

id:eel3:20090307:1236401934 のCommon Lisp版の自作iotaにはSchemeっぽさが残っているというか、Common Lispっぽさが足りていないと思う。

Schemeは末尾再帰の最適化が仕様に含まれているけどCommon Lispでは含まれていない、というのは有名な話で、だから末尾再帰を最適化してループに展開して欲しい場合は処理系依存の機能に頼ることになる。

しかしそんな軟弱な態度じゃマズイだろう、ということでCommon Lispではループ用のマクロが多用される*1

ということで、ループマクロの勉強を兼ねて書き直してみた。

まずはdotimes。iotaの第1引数はcountという名前だから、やはり繰り返す回数が大切なんだろうと考えて、dotimes。

; dotimes 版その01
; let が余分な感じに見える
(defun iota (count &optional (start 0) (step 1))
  "iota is a function ported from Scheme SRFI-1."
  (let (nums)
    (dotimes (i count) (push (+ start (* step i)) nums))
    (nreverse nums)))

; dotimes 版その02
; 戻り値を指定できることに気づいたので変更
(defun iota (count &optional (start 0) (step 1))
  "iota is a function ported from Scheme SRFI-1."
  (let (nums)
    (dotimes (i count (nreverse nums))
      (push (+ start (* step i)) nums))))

名前の通り、単純に指定回数だけループする場合に向いている。ただ今回の場合、letでレキシカル変数を用意する必要があって、その分だけ何となく面倒に感じる。ループ構文側でレキシカル変数を用意したいのだけど、dotimesでは無理そうだ。

次はdo。dotimesより自由度が高くて、色々な書き方ができるみたい。

; do版その01
; 取り敢えずリファレンス片手に書いてみた
(defun iota (count &optional (start 0) (step 1))
  "iota is a function ported from Scheme SRFI-1."
  (do ((i 0 (1+ i)) nums)
      ((>= i count) (nreverse nums))
    (push (+ start (* step i)) nums)))

; do版その02
; ループ本体をなくしてみた
(defun iota (count &optional (start 0) (step 1))
  "iota is a function ported from Scheme SRFI-1."
  (do ((i 0 (1+ i))
      (nums nil (push (+ start (* step i)) nums)))
      ((>= i count) (nreverse nums))))

; do版その03
; その02がイマイチに感じたので、変数を増やしてみた
(defun iota (count &optional (start 0) (step 1))
  "iota is a function ported from Scheme SRFI-1."
  (do ((i 0 (1+ i))
      (n start (+ n step))
      (nums nil (push n nums)))
      ((>= i count) (nreverse nums))))

ループ本体をなくした書き方が可能だけど、その分書き方に気をつけないと読みにくくなりそうだ。

最後はloop。ループキーワードに手を出してみた。

; loop版
; 何というかもうLispじゃない感じ
(defun iota (count &optional (start 0) (step 1))
  "iota is a function ported from Scheme SRFI-1."
  (loop for i from 0 below count collect (+ start (* step i))))

ループキーワードを使ったloopは、何というかスゴイと思う。その代わりLispっぽさが消えるけど。これは賛否両論あるのも納得できる。

*1:「そんな軟弱な態度じゃマズイだろう」という理由はウソだけど、「ループ用のマクロが多用される」という認識自体は正しいと思う。