読者です 読者をやめる 読者になる 読者になる

一般化された set!

例えば C のこんなコードを考えてみよう。

int main(void) {
  int a, b;
  a=1;
  b=a;
  return 0;
}

最初の代入では変数 a は a という場所を指している。 次の代入では a は a に入っている値のことだ。
普通は変数が「場所」である場合と「値」である場合を意識して区別したりはしない。 それはこの区別が感覚的に自然だからだと考えられる。
Scheme でも同様の概念を導入しようという試みがある。 srfi-17 の「一般化された set!」がそうだ。

(set-car! a 1)

などとするかわりに

(set! (car a) 1)

と書ける。
だが、 petite chez scheme でこの機能を使おうとしたところ、用意されていないようだったので自分で書いてみた。 おおよそ仕様を満しているつもり。 petite chez scheme 以外でも R6RS 準拠の処理系なら使えるはず。

(library (srfi-17)
  (export set! getter-with-setter setter)
  (import (prefix (only (rnrs) set!) rnrs:)
          (except (rnrs) set!)
          (rnrs exceptions (6))
          (rnrs conditions (6))
          (rnrs mutable-pairs (6))
          (rnrs mutable-strings (6)))

  (define ht (make-eq-hashtable))

  (define (default-setter . _)
    (raise (make-serious-condition)))

  (define (setter proc)
    (hashtable-ref ht proc default-setter))
  
  (define-syntax set!
    (syntax-rules ()
      ((_ (proc args ...) val)
       ((setter proc) args ... val))
      ((_ var val)
       (rnrs:set! var val))))

  (define (getter-with-setter getter setter)
    (define (new-proc . args) (apply getter args))
    (hashtable-set! ht new-proc setter)
    new-proc)

  (hashtable-set! ht setter
                  (lambda(proc setter)(hashtable-set! ht proc setter)))

  ;; Standard setters
  (hashtable-set! ht car set-car!)
  (hashtable-set! ht cdr set-cdr!)
  (hashtable-set! ht string-ref string-set!)
  (hashtable-set! ht vector-ref vector-set!)
  (hashtable-set! ht caar (lambda(x v)(set-car! (car x) v)))
  (hashtable-set! ht cadr (lambda(x v)(set-car! (cdr x) v)))
  (hashtable-set! ht cdar (lambda(x v)(set-cdr! (car x) v)))
  (hashtable-set! ht cddr (lambda(x v)(set-cdr! (cdr x) v)))
  ;; 3階層以上は省略 要るなら定義してネ
)

実行時に毎回ハッシュテーブルから探しているのがダサいとは思う。 しかし、 setter は getter の名前ではなくオブジェクトに結びつけられていることになっているというのもあって他に方法が思い付かなかった。
もっと効率的な方法があるだろうか?
Document ID: c0fc717cc86d8f9c15e684dbfb97c484