真偽値列

HSPにはカーソルなどの入力を受け付けるための命令があって、HSPを使い始めたら、まずこれに親しむことだろう*1

; loopとかは割愛
stick a
if a&1 :gosub *left_routine
if a&2 :gosub *up_routine
if a&4 :gosub *right_routine
if a&8 :gosub *down_routine

stick命令を呼ぶと、変数にキーの入力状況がビットフラグとして代入される。HSPでは0が偽として扱われるため、この方法で簡潔にキーと動作の関係を記述できる。


これに慣れているため、lispで多数のフラグを渡すときに、HSPと同じように書いてしまいたくなった。

(let ((a (stick)))
  (if (plusp (logand a 1)) (left-routine))
  (if (plusp (logand a 2)) (up-routine))
  (if (plusp (logand a 4)) (right-routine))
  (if (plusp (logand a 8)) (down-routine)))

しかし、その値を与えるstick関数で、わざわざ真偽値を加算するのは明らかに無駄である。

;疑似コード
(defun stick ()
  ((中略)
  (+ (if left 1 0) (if up 2 0) (if right 4 0) (if down 8 0) ...)))

結局、データを与える関数を先に考えると、真偽値のlistで与えればすんなりいくことがわかる。

;疑似コード
(defun stick2 ()
  ((中略)
   (list left up right down ...)))

結果を受け取った先で使うときも、こちらの方が簡潔に書ける。
>|lisp|
(let ((a (stick2)))
  (if (car a) (left-routine))
  (if (cadr a) (up-routine))
  (if (caddr a) (right-routine))
  (if (cadddr a) (down-routine)))

あるいは、単純に多値を用いればいいことがわかる。

;疑似コード
(defun stick3 ()
  (values (leftキー取得) (upキー取得) (rightキー取得) (downキー取得) ...))

(multiple-value-bind (left up right down)
	(stick3)
  (if left (left-routine))
  (if up (up-routine))
  (if right (right-routine))
  (if down (down-routine)))

はじめからこのように書いていれば、変な方法を考えることもなかったわけで、普通はこのようにつまることはない。lispでは、論理計算は、数値として扱うより、論理をそのまま使った方が簡潔であると気がついた次第である。

*1:最近はonkeyなどウィンドウが受け取る入力を使う方法を覚える方が先かも知れない。