名前付きlet

9章5節。名前つきlet。


今回はRPGを想定して、以下のようなプレイヤーの状態をを表す連想リストを規定してみる。

((hp . 320)      ; 体力
 (mp . 66)       ; 魔力
 (position . #f) ; 現在位置
 (inventory potion potion dagger cookie dagger)  ; 持ち物
)


ポーション飲んだらポーションが減った」だけじゃ面白くないので、今後の展望としては、状態を変化させてみよう、という流れになるのだろう。
まずはその足がかりとして、今回はプレイヤーを作成するための手続きmake-playerを考える。

(make-player 'hp 320
             'mp 66
             'position #f
             'inventory '(potion potion dagger cookie dagger))

のように評価すると、上で述べた状態の連想リストを生成するとする。
この式を見て思ったのが、キーワード引数の形に似てるなぁー、ってこと。
でも、キーワード引数にしてしまうと、例えばプレイヤー状態に「レベル」が追加された時に、make-playerを書き換えないといけなくなるから不便だな。うん。
ってことで、make-playerを定義してみる。

(define make-player
  (lambda args
    (define loop
      (lambda (lis)
	(match lis
	  (() '())
	  ((attr value . rest) (cons (cons attr value) (loop rest))))))
    (loop args)))

んじゃ、使ってみる。

gosh> (make-player 'hp 320 'mp 66 'position #f)
((hp . 320) (mp . 66) (position . #f))
gosh> (make-player 'hp 320 'mp 66 'position)
*** ERROR: : no matching clause for (position)
Stack Trace:
_______________________________________
  0  loop

  1  loop

うむ、引き数は必ず偶数にする必要があるのか。


いままで何回もローカル手続きloopを使ってきた。
この方法は、手続きの実行に先立って手続きの定義があるので、定義が長くなる場合に見通しが悪くなる。...らしい。
ということで、シンタックスシュガーが用意されているらしい。
それが以下の名前付きlet。

(use util.match)

(define make-player
  (lambda args
    (let loop ((lis args))
      (match lis
	(() '())
	((attr value . rest) (cons (cons attr value) (loop rest)))))))

要は手続きの実行の同時に名前を束縛するわけですな。