Arantium Maestum

プログラミング、囲碁、読書の話題

lazy-seqでrangeを書いてみる

range関数はPythonでもおなじみで、引数の扱いもほぼ同じ。ただし、引数なしで0から始まって無限に続く整数のシーケンスになるというのはPythonではitertools.countの挙動でrangeではできない。

Clojurelazy-seqで書くにあたって、実は「遅延評価で無限に続く」という部分は非常に簡単。むしろ面倒くさいのは1個以上引数があった場合の挙動の違いである。つまりmulti-arity対応。

引数が一つだけの場合と複数の場合とで、第一引数の意味が変わるのが一番面倒くさい。repeatなどと同じようにまずは引数無しのものを書いてからdroptakeを使って他のarityに対応しようかと思ったのだが、引数一つの場合意味合いがstartではなくendになるのでそれもおかしくなる。

ということで、引数三つの場合をベースに、他のarityはデフォルト値を入れることで対応する。

(defn my-range
  ([] (my-range 0 Float/POSITIVE_INFINITY 1))
  ([end] (my-range 0 end 1))
  ([start end] (my-range start end 1))
  ([start end step]
   (if (or (and (pos? step) (>= start end))
             (and (neg? step) (>= end start)) 
     ()
     (lazy-seq (cons start 
                     (my-range (+ start step) end step))))))

引数無しの場合、endFloat/POSITIVE_INFINITYにすることで無限に続いても大きさ比較が成り立つようにする。

あと少しややこしいのはstepが負の値をとる場合の対応で、これを忘れると(range 10 0 -1)などに空のリストを返してしまう。

全体を見渡すと、やはりコアのロジックが(lazy-seq (cons start (my-range (+ start step) end step))))))で記述できてしまうのがClojureらしくていいのではないだろうか。