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

(wat-aro)

無職から有職者にランクアップしました

プログラミングGauche 19.7 簡易な例外機構のまとめ

scheme gauche

マクロとcall/ccを使ったcatchthrowの実装の解説を備忘録として残します.
 

;; 簡易例外機構
(define *signals* '())

(define-syntax catch
  (syntax-rules (finally)
    [(_ (sig body ...) (finally follow ...))
     (let* ((signals-backup *signals*)
            (val (call/cc (lambda (k)
                            (set! *signals* (cons (cons 'sig k) *signals*))
                            body ...))))
       (set! *signals* signals-backup)
       follow ...
       val)]
    [(_ (sig body ...))
     (catch (sig body ...) (finally))]))

(define-syntax throw
  (syntax-rules ()
    [(_ sig val) ((cdr (assq 'sig *signals*)) val)]))
;; サンプルコード
(define (div n d)
  (if (= d 0)
      (throw DivedeZeroError
             (print #`"ERROR: Divide Zero Error Occured...\n divide ,n by ZERO!\n--------------------"))
      (/ n d)))

(define (percentage a b)
  (catch (DivedeZeroError
          (print (* (div a b) 100.0) "%"))
         (finally
          (print "follow ..."))))

 
 
処理の流れを説明します.
*signal*の初期値をsignals-backupに束縛します.
その後*signal*には(set! *signal* ...)の行でcarにシグナル,cdrに継続を渡すk,というペアのリストが束縛されます.
((sig . k))という形になります.
サンプルコードでは((DivisedZeroError . k))が入ります.
その後bodyが実行されます.
サンプルコードではpercentageの最初のprint行.
ここでdivが呼ばれます.
(= d 0)のが真の時にthrowが呼ばれます.
(cdr (assq 'sig *signals*))が評価され継続を表すkが返され,(k val)throwprint 行を実行し,戻り値がcatchのvalに束縛されます.
(※ twitterでkeenさん(@blackenedgold)さんから,継続を渡すkではなく,継続を表すkと教えていただいたので訂正しました.)
ここではまってたのですが,(= d 0)なのにシグナルが違っていた場合は(cdr #f)となりエラーが返ります.
シグナルを間違えるなって話ですね.
(= d 0)#fの時は(/ n d)が実行され値が返ります.
 
catchに返ってくると*signals*にシグナルの初期値を戻します.
そしてfinally ...を実行します.
最後にvalに束縛した値を返して終了です. ここではpercentageprintしてるので#<undef>が返ります.
 
こうして一つ一つ追えばそんなに難しくないですね.
でも理解するのに時間かかりました...
最後に実行例紹介して終わります.
  
 

gosh> (percentage 1 40)
2.5%
follow ...
#<undef>
gosh> (percentage 10 0)
ERROR: Divide Zero Error Occured...
 divide 10 by ZERO!
--------------------
follow ...
#<undef>

 
マクロもcall/ccも難しい