コルーチンをCommon Lispで簡単に定義
また更新滞ってしまいました...。なかなか長続きしませんね。
久しぶりに日記のネタが浮かんだので、メモがてら書きます。
ゲームを作っているとコルーチンが欲しくなることが多々あります。例えば、敵の行動が「飛行状態から通常攻撃をして着地から必殺技を出す」という連携攻撃を考えると、普通に状態遷移でやると、
- 飛行状態
- 通常攻撃
- 着地
- 必殺技
こんな状態を考える必要があるのではないでしょうか?そしてStateパターンなどを使って実装している場合には、このすべての状態に対してクラス定義をしたり、関数定義をしたりする必要があります。
コルーチンを使うとこういう時に便利です。コルーチンは、「関数を途中まで実行して、次に同じ関数を呼んだ時には前回の続きから実行する」というものです。pythonのジェネレータとかで同様のことができますね。
で、common lispでそれを実現するのはcl-contでできるのですが、cl-contが表に出て欲しくないのと
手軽に定義したかったのでマクロを作りました。
こんなのです。
(defmacro def-coroutine (name args &body body) "generate croutine from body form" `(defun ,name ,args (cl-cont:with-call/cc (macrolet ((yield () (let ((cc (gensym))) `(cl-cont:let/cc ,cc ,cc)))) ,@body))))
使い方は簡単で
(def-coroutine test-func (input1 input2) (print input1) (yield) (print input2) (yield))
こんな風にコルーチンが定義できます。定義したコルーチンは
CL-USER> (setf hoge (test-func "1" "2")) "1" #<COMPILED-LEXICAL-CLOSURE (:INTERNAL TEST-FUNC) #xCAD1AFE> CL-USER> (funcall hoge) "2" #<Compiled-function VALUES #x4071136>
こんな感じに使えます。