LoginSignup
7
6

More than 5 years have passed since last update.

Common Lispでiteratorもどき

Last updated at Posted at 2015-02-01

概要

Common Lispでiteratorもどきをつくってみました。ちなみに私はiteratorとは何かをあまりわかってないですが、それにも関わらず書いてます。分かったらそのうち書き直すと思います。
追記 (2015/02/01): LISP界隈(?)ではgeneratorと言うらしいです。

ユースケースは、mapcarにすると重く、さらに途中で中断するかもしれないからリスト全体をmapcarするのは無駄という割とニッチな場面なんだと思います。

コード

;; iteratorはhas-nextとgetを持った構造体
(defstruct iter has-next get)

(defmacro each-iter ((var iter) &body body)
  `(loop while (funcall (iter-has-next ,iter)) do
     (let ((,var (funcall (iter-get ,iter)))) ,@body)))


;; 例
(defun primep (num)
  (loop for i from 2 to (sqrt num)
    when (= (rem num i) 0) do (return-from primep nil))
  t)

(defun get-next-prime (start)
  (loop for i from start
    when (primep i) do (return i)))

(defun make-prime-iter (start end)
  (let ((next-prime (1- start)))
    (make-iter
     :has-next
      (lambda ()
        (setq next-prime (get-next-prime (1+ next-prime)))
        (<= next-prime end))
     :get
      (lambda ()
        next-prime))))

(let ((count 0)
      (iter (make-prime-iter 1000 2000)))
  (each-iter (prime iter)
    (print prime)
    (incf count)
    (when (<= 10 count)
      (return))))

1000から2000までの素数を一度に列挙してリスト処理するのはさすがに重いので逐次処理になってます。最初の10個でいいや、みたいな時でもreturnできます。

見ての通りクロージャの威力が発揮されてます。

最後に

マクロで包まれているとはいえ、明示的にfuncallするのはなんとかしたいですね。defstructを工夫すれば大丈夫だと思います。

これってある種の遅延評価なんでしょうかね?

7
6
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6