Syntax highlighter

2011-11-11

Version 0.2.2リリース

1日寝かせたら不具合を発見したのでそれを直してのリリース。

今回のリリースではコンパイラがさらに定数畳み込みを行うようになりました。また、定数畳み込みを補助する構文としてdefine-constantが正式にサポートされました。
またビルドの際に必要なライブラリがプラットフォームにインストールされていなかった場合、自動的にダウンロードするように改善されました。 ただし、MSVC環境以外ではテストがあまり行われていません。

修正された不具合
  • get-bytevector-n!でcountパラメータがバイトベクターのサイズだった再に&assertionが投げられる不具合が修正されました。
  • Windows版のmake-transcoderがEOLスタイルをlfとして作成する不具合が修正されました。
  • ライブラリのrenameインポートの際に、renameされたものだけをインポートする不具合が修正されました。
  • let*-valuesの右辺値に左辺値が含まれている際にコンパイラがエラーを報告する不具合が修正されました。
  • delete-fileの挙動がR6RSのものと異なっていた不具合が修正されました。
  • ライブラリ内インライン化の際にコンパイラが誤ってインライン可能フラグをつけていた不具合が修正されました。
  •  カスタムポートでclose引数を指定しても呼ばれていなかった不具合が修正されました。
変更された機能
  • パターンマッチライブラリ(match)がAndrew Wright氏のものからAlex Shinn氏のものに変更されました。
  • current-directoryがパラメータ化されました。(current-directory "somewhere")で(set-current-directory "somewhere")と同様の働きをします。これによって(srfi :39)にあるparameterizeとの親和性があがりました。
  • ソケットライブラリ(sagittarius socket)からエクスポートされていた定数が、define-constantで定義されたものと同様の振る舞いをするように変更されました。
新たに追加された機能
  • Zlib圧縮ライブラリ(rfc zlib)が新たに追加されました。
  • プログラム引数処理ライブラリ(srfi :37)が新たにサポートされました。
  • ハッシュテーブルユーティリティ(util hashtables)が新たに追加されました。


個人的に割りといい感じになってきた感がある。正規表現でクラスのサポートをするために文字セットを組み込みにしないと。

2011-11-10

定数畳み込み

コンパイラの最適化をもう一歩進めてみた。
実は以前から構文としてはdefine-constantをサポートしてはいたんだけど、何もしない単なるサポートにしかなっていなかった。
っで、せっかくライブラリのインライン化とかやったので定数も畳み込んでしまおうと思い立ってやってみた。

基本方針は以下の通り。
  • 定数のみ。
  • exportで定義されていない。
  • コンパイラの最適化で定数になるものは含める。例えばこんなの
    (define a (car '(a b c))) ;; コンパイラは'aをaに定義する。
  • define-constantで定義されたものはライブラリ外でも参照する
とりあえず、なんとなく動くようになったっぽい。以下のコードでは定数が畳み込まれていることが確認できた。
(define-constant a (car '(a b c))) ;; carはコンパイル時に計算される(可能なら)

(define (test)
  (print a))
(disasm test)
;; size: 5
;; 0: CONST_PUSH a <-- シンボルaがそのまま渡されている
;; 2: GREF_TAIL_CALL(1) #<identifier print#user(0x5fd1e0)> ;; print
;; 4: RET

(library (inner)
    (export const-value)
    ;; define-constant は(sagittarius)ライブラリにて提供
    (import (sagittarius))
  (define-constant const-value 10)
)

(library (test)
    (export test2)
    (import (rnrs)
     (inner))

  (define a (+ 1 2 3)) ;; a はexportされていない

  (define (test2)
    (display a)
    (display const-value)
    (newline))
)

(import (test))
(disasm test2) 
;; size: 13
;; 0: FRAME 4
;; 2: CONSTI_PUSH(6) <-- 計算された後の定数になっている
;; 3: GREF_CALL(1) #<identifier display#|(test)|(0x73d018)> ;; display
;; 5: FRAME 4
;; 7: CONSTI_PUSH(10) <-- GREF const-valueではない
;; 8: GREF_CALL(1) #<identifier display#|(test)|(0x73efa8)> ;; display
;; 10: GREF_TAIL_CALL(0) #<identifier newline#|(test)|(0x73ef30)> ;; newline
;; 12: RET
ま、まずまずでしょう。Gambitのベンチマークにはなんら影響が出ないところが多少以上に悲しいが。(あれらはR6RSのライブラリを使ってないから、最適化がかからん)
もう少し寝かせてから0.2.2をリリースしよう。

2011-11-09

割と大きめな岐路

このサイズの岐路は3度目なのだが、2度あることはじゃないけど、3度目がきたわけだ。
(まだ、不確かな要素が多いので、歯に衣を着せた物言いだったり)

正直迷ってはいたりするのだが、さてどうしたものか。別に今の位置にそう固執しているわけではないのだが、割と気に入っていたりはするので、迷うわけだ。
う~ん、どうしたものだろう。

2011-11-02

ドキュメント

オープンオフィス(以下OOo) をやめて、RacketのScribbleに乗り換え中。
乗換えと言っても、Racketに付属しているものをそのまま使うのではなく、ドキュメント生成スクリプトも同時に書いている。なので、コンセプトだけを使うといった感じ。
Chibi Schemeに触発された感はあるが。

その際にあんまりうまいこと解決方法が見つからなかったのが以下のセクションを拾って目次に変換する処理。
用件としては、以下のようになっているリストをulとliで作成されたsxmlに変換すること。
((section
  (@ (tag "tag1") (number "1"))
  "Section 1")
 (subsection 
  (@ (tag "tag1.1") (number "1.1"))
  "Subsection 1.1")
 (subsection 
  (@ (tag "tag1.2") (number "1.2"))
  "Subsection 1.2")
 (section
  (@ (tag "tag2") (number "2"))
  "Section 2")
 (subsection 
  (@ (tag "tag2.1") (number "2.1"))
  "Subsection 2.1")
 (subsubsection 
  (@ (tag "tag2.1.1") (number "2.1.1"))
  "Subsection 2.1.1")
 (sub*section 
  (@ (tag "tag2.1.1.1") (number "2.1.1.1"))
  "Subsection 2.1.1.1")
 (section
  (@ (tag "tag3") (number "3"))
  "Section 3"))
期待される出力
(div (@ (id "G137") (class "table-of-contents"))
     (ul (@ (class "section"))
  (li (@ (class "section"))
      (a (@ (href "#tag1"))
  (span (@ (class "section-number")) "1")
  "Section 1")
      (ul (@ (class "sub-section"))
   (li (@ (class "sub-section"))
       (a (@ (href "#tag1.1"))
   (span (@ (class "section-number")) "1.1")
   "Section 1.1"))
   (li (@ (class "sub-section"))
       (a (@ (href "#tag1.2"))
   (span (@ (class "section-number")) "1.2")
   "Section 1.2"))))
  (li (@ (class "section"))
      (a (@ (href "#tag2"))
  (span (@ (class "section-number")) "2")
  "Section 2")
      (ul (@ (class "sub-section"))
   (li (@ (class "sub-section"))
       (a (@ (href "#tag2.1"))
   (span (@ (class "section-number")) "2.1")
   "Section 2.1")
       (ul (@ (class "sub-sub-section"))
    (li (@ (class "sub-sub-section"))
        (a (@ (href "#tag2.1.1"))
    (span (@ (class "section-number"))
          "2.1.1")
    "Section 2.1.1")
        (ul (@ (class "sub-sub-sub-section"))
     (li (@ (class "sub-sub-sub-section"))
         (a (@ (href "#tag2.1.1.1"))
     (span (@ (class "section-number"))
           "2.1.1.1")
     "Section 2.1.1.1"))))))))
  (li (@ (class "section"))
      (a (@ (href "#tag3"))
  (span (@ (class "section-number")) "3")
  "Section 3"))))
単にulとliで構成されたリストにちょっとしたhtmlのメタ情報が入ったものといった感じ。これが結構てこずった。
最終的にはこんな風になったけど、もうちょっといい感じにならないだろうか?
;; sxml toolsをふんだんに使ってます。
(define *section-classes*
  '((section       . "section")
    (subsection    . "sub-section")
    (subsubsection . "sub-sub-section")
    (sub*section   . "sub-sub-sub-section")))

(define (content-list-handler element)
  (define (process contents)
    (define (li-gen content class)
      (let* ((attrs (sxml:attr-list-node content))
      (tag   (cond ((assq 'tag attrs) => cadr)))
      (section (cond ((assq 'number attrs) => cadr)
       (else
        (assertion-violation 'li-gen
        "section is not defined" content)))))
 `((li (@ (class ,class))
       (a (@ (href ,(format "#~a" tag)))
   (span (@ (class "section-number")) ,section)
   ,@(map phase2/dispath (sxml:content content)))))))
    (define (ul-gen class)
      `(ul (@ (class ,class))))
    (define (rec contents generator top)
      (let loop ((contents contents)
   (r top))
 (if (null? contents)
     r
     (let ((content (car contents)))
       (let-values (((class depth) (generator content)))
  (let loop ((i 0)
      (r r))
    (if (= i depth)
        (cond ((and (zero? i) ;; top
      (eq? (car r) 'ul))
        (append! r (li-gen content class)))
       (else
        (let ((tail (car (list-tail r (- (length r) 1)))))
          (cond ((eq? (car tail) 'ul)
          (append! tail (li-gen content class))
          r)
         (else
          (let ((ul (ul-gen class)))
     (append! ul (li-gen content class))
     (if (eq? (car tail) 'li)
         (append! tail (list ul))
         (append! r (list ul)))
     r))))))
        (loop (+ i 1)
       (let((rr (car (list-tail r (- (length r) 1)))))
         (if (eq? (car rr) 'li)
      rr
      (car (list-tail rr (- (length rr) 1))))))))
  (loop (cdr contents) r))))))

    (define (section-generator element)
      (define len (length *section-classes*))
      (define sections (map car *section-classes*))
      (cond ((assq (car element) *section-classes*)
      => (lambda (slot)
    (let ((name (car slot))
   (class (cdr slot)))
      (values class (- len (length (memq name sections)))))))
     (else
      (assertion-violation 'section-generator
      "unknown tag" element))))
    ;; assume first one is section
    (rec contents section-generator (ul-gen "section")))

  (let* ((contents (*table-of-contents*))
  (attr (sxml:attr-list-node element))
  (id   (cond ((and attr (assq 'id attr)) => cadr)
       (else (symbol->string (gensym))))))
    `(div (@ (id ,id)
      (class "table-of-contents"))
   ,(process contents))
    )
  )
リストの作成を破壊的に行っているので、なんとなく反則技を使っている気分。いい点は、*section-classes*に追加のサブセクションを足せば簡単にネストできることか。4つ以上サブセクションが要るドキュメントなんて読みたくないが・・・

2011-10-28

とある面接での質問

つい最近、面接を受けたのだがその際に質問された項目の一つ。 「リストとマップとセットの違いについて説明してください」 この質問を聞いた際に、頭の中で、 「listとmapとset!(これ重要)について説明してください」 と変換したため、何を言っているんだろう?と本気で悩んでしまった。 本来の意味では「コレクションとしての」が接頭辞として付くと分かりやすいのかな? ただ、質問の意図は正直今でも分からず、違いを理解せずに使ってるやつなんているの?と思ってしまう。まぁ、自分の理解が正しいか不安にはなるので、ここで晒してみよう。晒すのが答えなのか、恥なのかは分からないけど。 リスト:要は可変配列、要素は重複を許す。 マップ:キーと値をの対を要素として持つ集合。キーの重複は許されない場合が多い。(許す実装をどっかで見た、気のせいかも) セット:集合。要素の重複を許さない。 順序が保障されるかどうかは実装によるのでどうでもいい。ArrayListなら入れた順番だし、LinkedListなら要素の値で順序が保障される(本当?あんまり使わないから分からん)といった感じ。 これが答えられない、もしくは大きく理解がずれてるとしたらそれはそれで問題な気がする。

2011-10-25

R7RSのドラフト4

45時間前にScheme Working Groupに載ってたので読んでみた。なんだかタイムリーに読んだ気分に。
まぁ大きな点はドラフト3から変わらないだろうと踏んで、モジュールだけを読んでみた。ここがmoduleからdefine-libraryに変わるので。
ぶっちゃけ名前が変わっただけだったので特に特筆すべきところはないかなぁ(まだ、しっかり読んでないけど)
モジュールシステムの項で気になる点:
A library definition takes the following form:
    (define-library <library name>
        <library declaration> ... )
<library name> is a list whose members are identifiers or unsigned exact integers that is used to identify the library uniquely when importing from other programs or libraries. Libraries whose first identifier is scheme are reserved for use by this report and future versions of this report. Libraries whose first identifier is srfi are reserved for libraries implementing Scheme Requests for Implementation.
A <=<library declaration> may be any of:
  • (export <export spec> ... )
  • (import <import set> ... )
  • (begin <command or definition> ... )
  • (include <filename1> <ilename2> ... )
  • (include-ci <filename1> <filename2> ... )
  • (cond-expand <cond-expand clause> ... )
これってこんな風に書けるってこと?
(define-library (test)
  (begin (define a 'a))
  (import (scheme base))
  (export a)
)
リファレンス実装(だと思われる)chibi-schemeで試してみたが、どう動かすのかまったく分からん。 まぁ、この程度の違いならマクロで吸収できるなぁ・・・(いろいろ面倒な部分もあるので)R7RS対応にするかは分からないけど。

O(n^2)正規表現(from Island Life)

Gaucheの河合史郎さんのBlogのエントリーO(n^2)の正規表現をみて、そういえばSagittariusでは正規表現のベンチマークとったことないなぁと思いやってみた。 そのままでは当然動かないので、コンバート。
こんな感じ。
(import (sagittarius regex)
 (srfi :1)
 (srfi :13)
 (rnrs))

(define format.6f
  (lambda (x)
    (let* ((str (number->string (/ (round (* x 1000000.0)) 1000000.0)))
           (pad (- 8 (string-length str))))
      (if (<= pad 0)
          str
          (string-append str (make-string pad #\0))))))

(define-syntax time
  (syntax-rules ()
    ((_ expr)
     (let-values (((real-start user-start sys-start) (time-usage)))
       (let ((result (apply (lambda () expr) '())))
         (let-values (((real-end user-end sys-end) (time-usage)))
           (let ((real (format.6f (- real-end real-start)))
                 (user (format.6f (- user-end user-start)))
                 (sys  (format.6f (- sys-end sys-start))))
             (format #t "~%;;  ~a real    ~a user    ~a sys~%" real user sys)
        (flush-output-port (current-output-port))))
         result)))))

(define (time-this runs thunk)
  (time
   (do ((i 0 (+ i 1)) (r (thunk) (thunk)))
       ((= i runs)))))
       
(define rx1 (regex "[-_.0-9A-Za-z]+@[-_0-9A-Za-z]+[-_.0-9A-Za-z]+"))

(define (regex-test rx text runs)
  (display (string-length text))
  (time-this runs (lambda () (regex-replace-all rx text " "))))

(define (repeat s c) (string-concatenate (make-list c s)))

(print rx1)
(regex-test rx1 (repeat "abcdefgh" 10) 20)
(regex-test rx1 (repeat "abcdefgh" 50) 20)
(regex-test rx1 (repeat "abcdefgh" 100) 20)
(regex-test rx1 (repeat "abcdefgh" 500) 20)
(regex-test rx1 (repeat "abcdefgh" 1000) 20)
結果は散々であった。以下結果。(Core2Duo 3GHz)
#
80
;;  0.005000 real    0.000000 user    0.000000 sys
400
;;  0.100000 real    0.110000 user    0.000000 sys
800
;;  0.400000 real    0.375000 user    0.000000 sys
4000
;;  9.965000 real    9.937000 user    0.000000 sys
8000
;;  39.94300 real    39.70300 user    0.000000 sys
いやぁ、速くはないだろうなぁとは思っていたが、ここまで遅いとは。
Sagittariusの正規表現はJavaのコードを流用(もちろんCに書き直したが)しているので、マッチしなかったら一文字進めて再試行するようになっているので、入力文字列がマッチしないと結構不利にはなる。特にregex-replace-allみたいなのは痛い。
今回のケースだと、クラスのマッチはあんまり遅くないんだよなぁ、組み込みのcharsetを入れても改善の余地は少なさそう。さて、どうしようかね。

2011-10-24

Sagittarius 0.2.1リリース

0.2.0の時はブログに書き忘れたが、今回は書いておこう。
短めのリリース周期で行きたいと思い(どうせ長続きしないが)、早めに出してみた。
バグフィックスリリースなので、目新しい機能は特になし。

修正されたバグ
  • SRFI-42を記述するsyntax-rulesがうまく動かないのが修正されました
  • ライブラリのrenameインポートが動作していないのが修正されました
  • if文内でeqv?などインラインアセンブラに展開され、かつその中でlet式を使用している場合にコンパイラが不正なコードを出力する問題が修正されました
  • (apply = '(1 1 1))が#fを返す不具合が修正されました
  • load関数が#!r6rsや#!compatibleなどのフラグを上書きする不具合が修正されました
新たに追加、更新されたライブラリ
  • (util port)、(util file)のユーティリティライブラリが追加されました
  • (text sxml serializer)が追加されました。これでSXMLからXMLへの変換が可能です
  • (text sxml sxpath)が更新されました。名前空間に対応しているはずです
  • 移植性のためSRFI-38が追加されました。(import (srfi :38))でread/ss及びwrite/ssが使用可能です
  • (text sxml ssax)及び(text parse)のドキュメントが追加されました
 コンパイル時の注意:
テストケースが大幅に追加されたことに加え、sxpathのテストケースが膨大であるために、make testを行うとメモリが足りずにプログラムが終了する可能性があります。キャッシュ化されたものは問題なく動くので、上記の問題が発生した場合は再度make testコマンドを実行してみてください。

さて、誰がこれを見てダウンロードしてみようなんて思うのだろうか・・・割とまじめに書いてみたが・・・

2011-10-17

自分の価値について

いろいろあって最近自分の価値というものについて考えることが多くなった。
そもそも何を持って価値とするかというのは難しいしところだが、今回は自分の市場価値というところに焦点を絞ってみる。(というか、そこについて考えているだけ)
また、細かいことだけど、ここでは他人からの評価+αを価値と呼ぶことにする。

事の発端はいたって簡単で、最近結構頻繁にオファーのメールやら電話やらをもらうことがある。その際に端的に言えば今の職場より割りといい額を提示されているのでそこそこ評価されているのかなぁと感じるわけだ。
でも自分では自分は人と同じもしくはそれ以下くらいの価値かなぁといつも思っていて、「そんなにもらう仕事俺にできるんかい?」と割と躊躇することが多い。つまるところ他人の評価≠自己評価になっていると言うわけだ。
市場という目で見た場合、評価≒賃金ということができる。(まぁ、コンピュータジョークでは無能な人ほど稼ぐなんてジョークがあるがそれは置いておこう)。とりあえず客観的に自分を見てみた場合、30歳(もうすぐ)男、開発者、経験7年(より少し少ないが)、英語話せる。こんなところだろうか?細かく見ればもう少しあると思うが。
何が難しいかといえば、表面から見えるものと中身が違うというのは多々あるので、上記のスペックだけでは何も決められないというところだ。例えば本人が「経験あります、できます」と言ったところでそれを証明するものがなければ単なるやる気ありくらいな評価だろう。特にプログラマという立場なので何をなしたかを証明しにくい。(納品物は基本非公開だし、数字に表しにくい)
履歴書上でこれの経験がある、あれの経験があると言っても、末端の部分を触っただけでも経験だし、コアな部分から使い切っても経験だ。
となれば、他人からの評価を得やすくするために自分を着飾る必要があるのだろう。例えば履歴書で嘘ではないが少し大げさに書くとか、実際には末端の部分をやっただけだがあたかもすべてを知っているように書くとか。正直、こういったことはあまり好きではなく、またそれをすると過大評価を買ってしまうのではないかと思ってしまう。(もちろん評価者がそれを見越した過小評価をすればいいのかもしれないが)

なんとなくまとまりのない文章になってしまったが、言いたいこととしては価値を見定めるのは難しいということだ。それが自分自身のものであればなおさら。
でも、転職ということを考えれば自分の市場価値を見定めれた方が有利な気がする。もしくは履歴書を着飾れたらとか。

2011-10-10

DBI/DBDを導入

CLOSを使わずレコードだけでやったのでDBDの実装が面倒ではあったが、使う分にはそんなに気にならないレベルになったと思う。
実際のコードはこんな感じで書ける。
(import (dbi))
;; とりあえず組み込みでODBCをサポート。
;; 他に標準になっているデータベースアクセス方式があったらサポートするかも。
(define conn (dbi-connect "dbi:odbc:server=XE"
			  :username "username"
			  :password "password"))
;; プレースホルダーに直接値を渡せる
(let ((query (dbi-prepare conn
			  "select * from a_table where id >= ?"
			  10000)))
  ;; (dbi-bind-parameter query 1 10000) ;; こう書いてもOK
  (dbi-execute! query) ;; 実行自体は何も返さない。
  (print (dbi-fetch-all! query))) ;; dbi-fetch!なら一行返す。なければ#f
(dbi-close conn)
直接ODBCのAPIを叩くよりははるかに楽。SQLのDATE型とかは(DBDの実装依存なので、組み込みのODBCではだけど)srfi-19の日付型に変換して、BLOBや長いテキストはポートで返す(現状の実装だと結局全部読み込むのであまり意味はないが、そうするべきという意味合いで)。
なんとなく、仕事で使うような機能はほぼそろいつつあるなぁ。

2011-10-08

ODBCを入れてみた

DBI/DBDの実装をしようと思い、組み込みで1つ入れるには何がいいかなぁと考えた結果ODBCにした。
理由は割りと単純で、
  • 「ある程度」実装依存の部分を回避できること。
  • PostgreSQL, MySQL, Oracleなど実装を限定してしまうとビルド時にそれらが入ってないと組み込めないこと。
  • 一応ISOの標準になっており、(既に)Microsoftの独自実装というわけでもないこと。
といった感じ。特に3つ目は割りと重要で、これのおかげ(か、どうかか知らんが)で Unix系プラットフォームでもODBCが使えるらしい。
とりあえず初期バージョンを作ってみて、CSVファイルを検索してみた。

こんな感じ
(import (odbc))
;; DSNはODBCの設定でつけた名前
(define dsn "TEST")
(define env (create-odbc-env))
;; 今回はCSVなのでユーザー名、パスワードは要らない。
;; Oracleなどに接続する際は必要になる
(define dbc (connect! env dsn "" ""))
;; もちろんプレースホルダーも使える
(define stmt (prepare dbc "select STREET from [ken_all_rome.csv] where CODE > ? and CODE < ?"))

(bind-parameter! stmt 1 23203)
(bind-parameter! stmt 2 23205)
(execute! stmt)
;; 警告レベルの例外を投げたりするので、with-exception-handlerで囲む必要がある。
(with-exception-handler
 (lambda (e)
   (report-error e))
 (lambda ()
   ;; fetch!は行が取得できたかどうかを返す while (fetch(stmt)) ... なイメージ
   (let loop ((next? (fetch! stmt)))
     (when next?
       ;; 現状では列名では取得できない。この辺は改善の余地あり。
       (let ((col1 (get-data stmt 1)))
	 (print col1)
	 (loop (fetch! stmt)))))))
;; 接続解除
(disconnect! dbc)
基本的にはDBDの低レベルAPIな位置づけなので、あまり高機能にする必要もないのだが、Prepared Statement(日本語訳しらない)に束縛された値のリセット機能はいれてもいいかもしれない。あと、DBD用に結果列名の取得とかも。
文字列周りをどうするか考える必要があるかもしれない。現状だとUTF-8しか使えないが、CSVとか使うならSHIFT-JISがどうしても必要になってくる。コネクション辺りにトランスコーダーを持たせるか。(コネクションごとでいいよなぁ?)

どうでもいい話ではあるのだが、ODBCのAPIは資料が少ない気がする。いや、MSDNとかあるんだけど、例えばOracleのVARCHAR2は定義されたSQL型に入ってなくてえらく困った。
あと、SQLBindColとかSQLBindParameterとかの説明が足らん気がする。
ODBC Programmer's Referenceここがすごく役に立った。特にChapter9のBinding Parameters。MSDNには明示的に書いてないこと(読み飛ばしたのかもしれん)が普通に書かれてるのと、サンプルが多くてお勧め。

2011-10-05

Cygwinのヒープ

今更ながらだが、Cygwinのヒープは拡張できることが分かった。
参照:Changing Cygwin's Maximum Memory

何故これが必要だったかというと、SagittariusのビルドをX60で行った際に頻繁にWin32 487エラーが発生していたため。
おそらく原因はここにあるものだろう。LogicoolのWebcamのドライバー入ってるし。
とりあえず、512MBまで増やしてビルド再開。動いている。

call/ccを何とかしたった

方言かこれ?まぁいいや。

call/ccのコピー回数を2回から1回に減らした。今のところばっちり動いているのでOKだろう。
ここまできたらベンチマーク。最初ctakとfibcを除くすべてが遅くなったのであせったが、フルビルドしたら戻った。
結果をmosh(0.2.6)と比較(Core2Duo 3GHz, Cygwin on Windows XP, memory 3GB)
まずはSagittarius:
;;  GABRIEL

;;  boyer   (x3)
;;  0.664000 real    0.610000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  browse  (x120)
;;  6.073000 real    6.031000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  cpstak  (x80)
;;  0.969000 real    0.969000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  ctak    (x25)
;;  2.972000 real    2.953000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  dderiv  (x160000)
;;  1.307000 real    1.297000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  deriv   (x320000)
;;  2.092000 real    2.078000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  destruc (x100)
;;  1.514000 real    1.500000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  diviter (x200000)
;;  1.698000 real    1.703000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  divrec  (x140000)
;;  1.307000 real    1.312000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  puzzle  (x12)
;;  0.619000 real    0.625000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  takl    (x35)
;;  0.704000 real    0.703000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  triangl (x1)
;;  0.677000 real    0.672000 user    0.000000 sys
;;  ----------------------------------------------------------------

;;  ARITHMETIC

;;  fft     (x200)
;;  0.419000 real    0.422000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  fib     (x1)
;;  1.542000 real    1.547000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  fibc    (x50)
;;  0.994000 real    0.984000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  fibfp   (x1)
;;  5.692000 real    5.610000 user    0.047000 sys
;;  ----------------------------------------------------------------
;;  mbrot   (x10)
;;  1.676000 real    1.656000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  nucleic (x1)
;;  1.211000 real    1.188000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  pnpoly  (x10000)
;;  0.639000 real    0.641000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  sum     (x1000)
;;  0.360000 real    0.359000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  sumfp   (x600)
;;  1.162000 real    1.157000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  tak     (x200)
;;  0.592000 real    0.593000 user    0.000000 sys
;;  ----------------------------------------------------------------

;;  MISCELLANEOUS

;;  conform (x4)
;;  0.842000 real    0.844000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  earley  (x20)
;;  0.366000 real    0.375000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  graphs  (x15)
;;  0.572000 real    0.562000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  mazefun (x100)
;;  0.566000 real    0.563000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  nqueens (x150)
;;  0.354000 real    0.344000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  paraffins (x100)
;;  0.616000 real    0.625000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  peval   (x20)
;;  0.949000 real    0.938000 user    0.000000 sys
;;  ----------------------------------------------------------------
;;  ray     (x1)
;;  1.656000 real    1.610000 user    0.046000 sys
;;  ----------------------------------------------------------------
;;  scheme  (x3000)
;;  0.852000 real    0.844000 user    0.000000 sys
;;  ----------------------------------------------------------------
browse及びfibfpが遅いが他はまずまず。ctakは改善前から4秒速くなってるので劇的に良くなったといえるだろう。

次にmosh:
;;  GABRIEL

;;  boyer   (x3)
;;4.444999933242798 real 4.406 user 0.0 sys
;;  ----------------------------------------------------------------
;;  browse  (x120)
;;5.337000131607056 real 5.327999999999999 user 0.0 sys
;;  ----------------------------------------------------------------
;;  cpstak  (x80)
;;1.5829999446868896 real 1.5630000000000006 user 0.0 sys
;;  ----------------------------------------------------------------
;;  ctak    (x25)
;;5.3460001945495605 real 5.311999999999999 user 0.0 sys
;;  ----------------------------------------------------------------
;;  dderiv  (x160000)
;;2.8910000324249268 real 2.875 user 0.0 sys
;;  ----------------------------------------------------------------
;;  deriv   (x320000)
;;2.3340001106262207 real 2.2970000000000006 user 0.0 sys
;;  ----------------------------------------------------------------
;;  destruc (x100)
;;1.9910001754760742 real 2.0 user 0.0 sys
;;  ----------------------------------------------------------------
;;  diviter (x200000)
;;2.055999994277954 real 2.0470000000000006 user 0.0 sys
;;  ----------------------------------------------------------------
;;  divrec  (x140000)
;;1.4830000400543213 real 1.4529999999999994 user 0.0 sys
;;  ----------------------------------------------------------------
;;  puzzle  (x12)
;;1.0709998607635498 real 1.0779999999999994 user 0.0 sys
;;  ----------------------------------------------------------------
;;  takl    (x35)
;;0.9110000133514404 real 0.907 user 0.0 sys
;;  ----------------------------------------------------------------
;;  triangl (x1)
;;0.9219999313354492 real 0.9060000000000024 user 0.0 sys
;;  ----------------------------------------------------------------

;;  ARITHMETIC

;;  fft     (x200)
;;1.8899998664855957 real 1.8119999999999976 user 0.0 sys
;;  ----------------------------------------------------------------
;;  fib     (x1)
;;1.7780001163482666 real 1.75 user 0.0 sys
;;  ----------------------------------------------------------------
;;  fibc    (x50)
;;1.7349998950958252 real 1.7040000000000006 user 0.0 sys
;;  ----------------------------------------------------------------
;;  fibfp   (x1)
;;8.138999938964844 real 7.780999999999999 user 0.0 sys
;;  ----------------------------------------------------------------
;;  mbrot   (x10)
;;3.375999927520752 real 3.344000000000001 user 0.0 sys
;;  ----------------------------------------------------------------
;;  nucleic (x1)
;;1.9010000228881836 real 1.8590000000000018 user 0.0 sys
;;  ----------------------------------------------------------------
;;  pnpoly  (x10000)
;;0.7100000381469727 real 0.7029999999999959 user 0.0 sys
;;  ----------------------------------------------------------------
;;  sum     (x1000)
;;0.4719998836517334 real 0.45300000000000296 user 0.0 sys
;;  ----------------------------------------------------------------
;;  sumfp   (x600)
;;1.9199998378753662 real 1.9059999999999988 user 0.0 sys
;;  ----------------------------------------------------------------
;;  tak     (x200)
;;0.7339999675750732 real 0.7349999999999994 user 0.0 sys
;;  ----------------------------------------------------------------

;;  MISCELLANEOUS

;;  conform (x4)
;;0.009999990463256836 real 0.015000000000000568 user 0.0 sys

;; wrong result: ("(b ^ d)" "c" "(a ^ c)" "d" "any" "none")
;;  ----------------------------------------------------------------
;;  earley  (x20)
;;2.930999994277954 real 2.9070000000000036 user 0.0 sys
;;  ----------------------------------------------------------------
;;  graphs  (x15)
;;2.378000020980835 real 2.3429999999999964 user 0.0 sys
;;  ----------------------------------------------------------------
;;  mazefun (x100)
;;0.8310000896453857 real 0.8290000000000006 user 0.0 sys
;;  ----------------------------------------------------------------
;;  nqueens (x150)
;;0.6979999542236328 real 0.6869999999999976 user 0.0 sys
;;  ----------------------------------------------------------------
;;  paraffins (x100)
;;1.2379999160766602 real 1.2340000000000018 user 0.0 sys
;;  ----------------------------------------------------------------
;;  peval   (x20)
;;1.0770001411437988 real 1.0790000000000006 user 0.0 sys
;;  ----------------------------------------------------------------
;;  ray     (x1)
;;3.0359997749328613 real 3.0 user 0.015 sys
;;  ----------------------------------------------------------------
;;  scheme  (x3000)
;;1.5360000133514404 real 1.531000000000006 user 0.0 sys
;;  ----------------------------------------------------------------
正直moshより速くなったと言っても問題ない気がしてきた。browseが少し負けてるか。何でだろう?boyerの桁が違うのはmoshはdisplay closureを使ってるので名前付letのループがきついのだろう。
GaucheとYpsilonをここに載せないのはあれらは異次元の速さだから。まだしばらく勝てそうにない。

ドキュメントの整備が終わったら0.2.0をリリースしよう。

2011-10-04

call/ccを何とかしたい

何が問題か?call/ccが遅い。
どの程度?Gambitベンチマークのctakが7秒(Core2Duo 3GHz)

とりあえず、前回読んだ論文の内容を実装するのは実は諦めていて、というか上記のベンチマークのような使い方をすると(んなことせんだろうけど)、結局ヒープ割り当てが増えるのと、BoehmGCを使っている以上、一度割り当てたメモリの部分開放ができないので(必要か?)ちょっと断念。
なんか目処が立ったらもう一回チャレンジする。

じゃあどうするか。とりあえずGauche、Ypsilon方式に作成時にヒープにコピーして呼び出しにはコピーをしない方法にしたい。
問題は静的リンクじゃないからスタック(というかフレーム)を単純にコピーしただけだと問題が起きること。
最近行った改善(改悪かも)で、Sagittariusのスタックは以下のように使用されるようになった。
letでスタックを伸ばすイメージ

before            preparing        after
+----------+< SP  +----------+< SP +----------+< SP
|  var 2   |      | v for F1 |     | new var 3|
+----------+      +----------+     +----------+
|  var 1   |      |   F1     |     |  var 2   |
+----------+< FP  +----------+     +----------+
|  Frame   |      |  var 2   |     |  var 1   |
+----------+      +----------+     +----------+< FP
                  |  var 1   |     |  Frame   |
                  +----------+< FP +----------+
                  |  Frame   |
                  +----------+
* F1 = 積みかけのフレーム(let変数の初期化時など)
こうしたおかげで、letをどれだけ書いても単にスタックが伸ばされるだけでほぼノーコストでローカル変数にアクセスできるようになった。
問題は、これを実現するために積みかけのフレームさえも(ダミーとしてだが)ローカル変数としてカウントしていること。実際、preparingの中でもう一つフレームが足されてかつ、F1の上にある値にアクセスしようとした際(ありえるのかは知らんが、やる必要があったのでありえるのだろう)には、LREF(3)ではなくLREF(10)になる。(現状フレームは7ワード占拠する)。
となると、どのフレームに何が引っ付いているのかというのが非常に把握しづらくなる。GaucheのようにARGP(引数フレームポインタ?)を導入してやるのもありなのかもしれないけど、静的リンクが無いからそのフレームより上(下?)にあるものを参照するときに困りそう。
さて、どうしたものか・・・

脱Display closure

正確には脱ではなく、減ではあるが。

call/ccを速くしようとしていた際に、display closureをletごとに作ると不便が生じることがわかったのでletではスタックを伸ばしてdisplay closureを作らないことにした。
そのおかげで、LET_FRAME, POP_LET_FRAME, DISPLAY, MARKの4命令が不要になり、またSHIFTJの意味合いも変わった。
上記の4命令は元をたどればmoshからもらったものだったので、脱moshともいえるかもしれない。(まぁ、moshにPOP_LET_FRAMEとMARKはなかったのだが)
Display closureをやめたからといって静的リンクにしたわけでもなく、本当にスタックを伸ばした感じ。なので、ちょっとletが深くなるとLREF(20)とか見えるようになった。いいか悪いかはしばらく使ってから確かめる。

この変更のおかげでGambitベンチマークのboyerが2秒から0.6秒まで短縮(Core2Duo 3GHz)。逆にctakは2秒増加という結果になり、call/ccがより重たいものになった(その他call/ccを使わないものは劇的ではないが改善された)。理由はいたって簡単で、ヒープの使用量が減り、スタックの使用量が増えたから。また、let, letrec, receive, 及び名前付letなどのループのコンパイル時間が短縮された(はず)。
call/ccの改善だったのに遅くなったという面を除けば悪くない。

2011-09-29

9月が終わる

10月3日はライデンの80年戦争終了記念日(だよな・・・?)
毎年この時期になると街はお祭り準備でバスやらの公共交通機関が半分麻痺状態になる。まぁ、職場はロッテルダムなのでライデンのバスはあんまり使わないんだけど。

今年で3度目。もう少しで今年も終わり。う~ん、一年は短い。もっといろいろやりたかったんだけどなぁという気もしているが、あと2ヶ月でどこまでできるか?

なんとなくお祭り前夜をみて思った。

2011-09-24

call/cc実装論文

Chez Schemeに使われている(らしい)call/ccの実装論文を読んでいるのだが、これで動く理由が分からない。
正確にはこれをSagittariusに適用できるか分からないか。

これが正しい理解かは微妙だが、大雑把に言えば継続が捕捉された時点のスタックポインタ以前のスタックを凍結、スタックフレームに以前のスタックポインタを足す。っで、捕捉された継続が呼び出されたら凍結されたスタックを現在のスタックに置き換える。
確かにこの方法なら作成時にスタックのコピーが無いので作成はほぼノーコスト。でもこれってcall/ccから脱出するとき困らんかな?
call/ccから脱出するときにどうしてもスタックのアンダーフローが検出されるので捕捉された継続を戻す必要がでる気がする。それともなんらか別の方法でその辺は何とかしてるのかな?

思考レベルだがこれを適用する際に問題になりそうなのは積みかけのフレームだと思ってる。例えば以下のケース:
(+ 1 (call/cc (lambda (c) 2)) 3)
というのがあったとして、call/ccが呼ばれた際のスタックは以下のようになる。(呼ばれた時点で、call/ccに渡されたlambda内ではない)
+-----------------+ <-- sp
| (lambda (c) 2)  |
+-----------------+ <-- fp
| Frame(call/cc)  |
+-----------------+ 
|       1         |
+-----------------+
|  Frame(+)       |
+-----------------+
Sagittariusでは上記のすべてが別ヒープにコピーされて、継続が呼び出された際にスタックに戻される。スタックVMで実装する際の一番重たい実装方法だ(よね?)
っで、上記の論文を考えると、数値2が返された段階でアンダーフローが検出される。その際上記のスタックは凍結されているので動かせない、つまり新スタックフレームに既に積まれた数値1とFrame(+)を積む必要がある。
あぁ、アンダーフローが検出された際って新スタックフレームは空であることが保障されているのでフレームポインタのことを考えなければ単にコピーするだけか。ヒープ割り当ててコピーする処理がコピーだけになったと思えば、悪くないだろうか?
理想としてはcall/ccの呼び出しで捕捉された継続が呼び出されないと分かっているなら(上記の例みたいな)、スタックを凍結せずそのままってのが一番だわな。この辺はコンパイラに頼るほかないが。

ブランチ切ってやってみるか。

2011-09-19

結局

Display closureをスタックに移すとcall/ccが激重になることが分かったので、ちょっと整理する程度にとどめた。おかげでインストラクションの意味がかなりすっきりしたと思う。
Gambitのベンチマークもbrowseとctakを除けばほぼmoshと同等かそれ以上に数値を記録しているのでまぁいいだろう。
(GaucheとYpsilonが異次元の速さなのはなぜだ?)

中身がだいぶ変わったし、マクロの書き換えもしたということで、ドキュメントの整備が終わったら2.0としてリリースしてしまおう。(このタイミングでもないと1.xのまま突き進みそうだし)
その間にcall/ccの高速化もしたいが、まぁ無理だろう。

2011-09-17

日本人がオランダに長く居すぎてしまったと感じるとき その2

調子にのってというかふと思いついたのでその2。
  1. 財布の中に20ユーロ入っていればOKと思うとき
  2. 雨が降っても傘をささないとき
  3. ビデオレンタル屋でAVがワゴンで売られていてもなんとも思わないとき
  4. etos(日本で言う杉薬局みたいなの)に大人の玩具が置いてあってもなんとも思わないとき
相変わらず思いつかなかった。解説が必要なのはは1かな?
ATMカードがあれば大抵の場所で支払いOKなので、そんなに持ち歩かなくてもいいんです。英語はDebitだったかな?オランダではPINっていいます。

いきなり雨に降られて濡れ鼠になりつつビデオレンタル屋にいったのでふと思いついた。AVはマジでワゴンで売られてます(普通の本屋にもあるという)。でもレンタル用のコーナーは一応分けられてる不思議。

2011-09-16

とりあえず

boyerが走るくらいの物まではできた(R6RSのテストはSEGVる)。
結果としては2倍から速く、GCも12回と明らかに違う。やる価値はありそうだ。

ここで問題が一つ。
今までヒープに置いていたものをスタックに置くようにしたのでものすごい勢いでスタックを食いつぶす。let*とか使ってると悲惨なことになる。さらにスタックの拡張が不完全だったというのもあり食いつぶすとSEGVる。
スタックの拡張については別に見直す必要があるが(おそらくcall/ccもだろう、うぅ・・・)、それより前に不要なDisplay closureの削除をしたい。
とりあえず思いついたのが、POP_LET_FRAMEされて且つSHIFTJでジャンプしないものはdisplay closureを辿る必要がないので削れそう。
作成時にリンクさせてしまうので、DISPLAYインストラクションにそれが末尾かどうか知らせる必要があるか。
ジャンプがあるかどうかはMARKインストラクションでマークされたdisplay closureがあればジャンプされる可能性があるとすればいいだろう。

スタックの拡張はちょっと考えないとなぁ。保存する前にごちゃごちゃやるとかだろうか?

2011-09-15

Display closureをスタックに置きたい

さすがの遅さにいらいらしたのと、これを解決しないとGaucheやYpsilonに手も届きそうにないので。

事の発端。
Gambitのベンチマークにboyerというのがあるのだが、これの速度が半端なく遅い。(さらに遅いのにbrowseがあるが、原因は一緒だろう)
あまりの遅さにGaucheの統計情報にGC回数を追加してビルドしたものと比較してみた。
GC回数脅威の320回!!!
(ちなみに、Gaucheは8回くらい)
3桁超えてきたかという感じ。

原因は以前も書いたDisplay closureで、letが連なってかつ外側のletの変数を参照してると作られる。要するに自由変数を解決するための仕組み。
これを毎回作っていたのではメモリによろしくないことが数字で出てしまっているので、なんとかスタックに作れないかなぁと試行錯誤中。

メモ:

  1. Display closureはインストラクションDISPLAYで作成される。
  2. DISPLAYは必ずLET_FRAMEの下に現れる。
  3. letが末尾呼び出しならPOP_LET_FRAMEでフレームは取り除かれる
  4. 3以外なら、ENTERとLEAVEが呼び出される
スタックにつむためには以下のことを考える必要がある。

  1. スタック領域に場所を確保し、積み上げた自由変数を移し変える
  2. 空いた場所にスライドさせる
  3. POP_LET_FRAMEが呼ばれた際にスタックにあるDisplay closureを移動させ、フレームポインタを移動させる
  4. LEAVEではDisplay closureが作られていたら除去する必要がある
ここまでやったがうまく動かない。どこかに見落としがある。どこだろう?

2011-09-14

私がSpring Webflowを使いたくないいくつかの理由

いい加減腹立ってきたわ、この糞仕様に!!

ということでどこがまずいか、どうして使ってはいけないかを書き出してみる。(だからといって現状が変わるわけではなく、単なる愚痴である)
  1. スコープまたぎができない
    • これは以前書いたけど、Ajaxとの親和性が悪すぎる
  2. とりあえずデバッグレベルですべてのデータを吐き出す
    • パスワードとか見せたくない(これも以前書いた)
  3. Default***クラスを直接newしてる
    • Defaultそのまま固定です。ユーザーは振る舞いをカスタムできません。
  4. Modelを使わないと入力チェックが非常に面倒になる
    • 2と3番の問題でフォームデータ直接チェックしたくなったけど、どうも無理臭い
  5. 設定ファイルの記述がドキュメントにほとんどない
    • カスタムはあまりするなということか?
  6. そのくせデフォルトの設定はあまりにひどい
    • 主に2,3,4番だわね
めんどくさくなったので、これくらい。使えば使うほどげんなりするのも仕様です。ってか、ほかのプロジェクトとかどうしてるんだろう?特に2番。
リリース後はデバッグログなんて出さないのかな?パッケージソフトには厳しいお言葉過ぎるよ。

ライブラリのインライン展開

1つのライブラリを最適化できないかなぁと思って、とりあえずインライン展開を試みてみた。
(大別するとグローバル最適化?でも、R6RSだとライブラリは1つのS式だから、そうでもないか)

とりあえず試したのは以下のこと。
exportされていない関数でかつ非再帰、相互参照なし、ライブラリ内で代入されていないものを抽出。
最適化時に上記の関数でかつそこそこサイズが小さいものをインライン展開。
たったこれだけ!

1個の巨大なS式と思えばまぁこんなものだろう。
で、効果があるのかちょっと検証。
以下のプログラム。
(library (test)
    (export process set-parameter!)
    (import (rnrs))

  (define *paramter* #f)

  (define (set-parameter! param)
    (set! *paramter* param))

  (define (get-paramter name default)
    (or (and *paramter* (hashtable-ref *paramter* name default))
         default))

  (define (get-count)
    (get-paramter 'count 1))

  (define (get-process)
    (get-paramter 'process (lambda args args)))

  (define (get-args)
    (get-paramter 'args #f))

  (define (process)
    (let ((n (get-count)))
      (let loop ((i 0))
    (unless (= i n)
      (let ((proc (get-process))
        (args (get-args)))
        (apply proc (list args)))
      (loop (+ i 1))))))
)


(import (test))
(define param (make-eq-hashtable))
(hashtable-set! param 'count 500000)
(hashtable-set! param 'args '(a b c d e f))
(hashtable-set! param 'process (lambda (a . b)
                 (fold cons a b)))
(set-parameter! param)
(time
 (process)
)
ちょっと作為的過ぎるだろうか?イメージとしてはオブジェクト指向のプロパティな感じということで。

実際にインストラクションのダンプを取るとprocessの中にget-count、get-process、get-argsの姿はない。

次に速度を比較。
インライン展開あり:
$ ./build/sash.exe -L. test.scm
;;  0.632000 real    0.641000 user    0.000000 sys
インライン展開なし:
$ ./build/sash.exe -fno-library-inline -L. test.scm
;;  0.681000 real    0.672000 user    0.000000 sys
40~50ms速いけど、50万回まわしてこの結果か。一応入れておくか程度だな。うっかりすると遅くなりそうで怖いが・・・

何より悲しいのはこれはGambitのベンチマークではまったく意味がないところか。

2011-09-12

Prefixつけたimportに潜む罠

R6RSでコード書いてて、
(import (prefix (rnrs) r6:))
って書く人はまぁいないだろう。onlyとかrenameならあるだろうけど。
これにこっそりと罠が潜んでいた。(というほどでもないんだけど)
たとえばこんなコード。
(define-syntax test
  (syntax-rules ()
    ((_ a b ...)
     (list a b ...))))
なんのことはないアホみたいなコードだ。
これを上記のprefix付で書くとこうなる。
(r6:define-syntax test
  (r6:syntax-rules ()
    ((r6:_ a b r6:...)
     (r6:list a b r6:...))))
でもこれYpsilonでは通らない。Ypsilonではこう書く必要がある。
(r6:define-syntax test
  (r6:syntax-rules ()
    ((_ a b ...)
     (r6:list a b ...))))
こうするとmosh及びnmoshでは通らない。でもPetite Chez Scheme(v 7.9.4)では通った。psyntax及びAndre van Tonderのsyntax-caseは厳格だということなのだろう。
ちなみにSagittariusも通らない。通るようにすることもできるんだけど、どうしようかなぁ?
こんなコード書かれないよなぁ・・・

Boehm GCが遅かった

Twitterで「速度が出ない」とつぶやいたが、原因が分かった。
まぁ、結論はすでにタイトルに書いてあるのだが、何が違ったかということをば。

調べたこと。
Gambitベンチマークのbrowseがやたら遅い。moshで5秒、Gaucheで1.7秒(速!)、Ypsilonで2.5秒、Sagittariusは12秒という結果だった。(Windows XP, Cygwin, Core2Duo 3.00GHz)
Display closureという構造をとっているmoshと比べても2倍遅いのはおかしいということで調査。
インストラクションの精査から始めて、結局以下のコードが遅くなることに気づいた
(do ((i 0 (+ i 1)))
     ((= i 10000)) ;; iを100000にすると遅くなる
  (cons 'a 'b))
何が問題だったか?
(cons 'a 'b)をコメントアウトするとmoshとの速度にほとんど差がでない。10000の時でもほとんど差が出ない。
ここでGCを疑う。
とりあえずGCの回数を出力してみたら、10000回だと9回で100000回だと10回になっていた。GCが遅い。

ここで出た疑問。なぜmoshでは遅くならないか?現状ではSagittariusもBoehm GCを使っている(絶対そのうち置き換えてやる!)が、Sagittariusでのみ問題になっている。
コンパイルオプションを調べた。moshのCygwinでのオプションは--disable-shared CFLAGS=-DGC_THREADS win32_threads=trueになっていた。
SagittariusはBoehm GCをバンドルしてないが、win32_threads=trueとかCFLAGS=-DGC_THREADSを渡してコンパイルした記憶がない。
上記を渡してリコンパイルしてみた。
5秒短縮!!!
その他のベンチマークもmoshと同等かそれ以上(遅いのもある)という結果になった。

原因探るのに2日かかったがこんな落ちとは正直思っていなかった。

追記:
自宅のX60ノートで同様にリコンパイルしてリンクしなおしたのだが、速度差はなかった。(上記のは会社のマシン^^;)
どうやらまだまだ別の場所に原因がありそうだ。

追記の追記:
やっぱりGCっぽい。別にコンパイルオプションがあるのだろうか?

2011-09-08

静的リンクにしておけば・・・

ベンチマークを取った際に2倍以上Gaucheに放されるテストが多数あったので原因を追求してみた。
端的にはこれ↓
(cond-expand
 (sagittarius
  (import (sagittarius compiler))
  (define flush flush-output-port)
  )
 (gauche
  (define compile-p2 (with-module gauche.internal compile-p2))
  (define compile-p3 (with-module gauche.internal compile-p3))
  )
 (else
  (exit)))

(define sexp '(define (lookup key table)
(let loop ((x table))
 (if (null? x)
     #f
     (let ((pair (car x))) ;; 【*1】ここが問題
(if (eq? (car pair) key)
   pair
   (loop (cdr x))))))))

(compile-p2 sexp)
(flush (current-output-port))
(compile-p3 sexp)
この結果が以下
$ sash -I"(srfi :0)" test.scm
($define () lookup
  ($lambda[lookup.0] (key[1.0] table[1.0])
    ($call[embed] ($lambda[loop.2] (x[3.0])
                    ($label #0
                      ($if ($asm (NULLP)
                             ($lref x[3.0]))
                        ($const #f)
                        ($let ((pair[2.0] ($asm (CAR)
                                            ($lref x[3.0]))))
                          ($if ($asm (EQ)
                                 ($asm (CAR)
                                   ($lref pair[2.0]))
                                 ($lref key[1.0]))
                            ($lref pair[2.0])
                            ($call[jump] ($call[embed] ($lambda[loop.2] (x[3.0])

                                                         label#0)
                                           ($lref table[1.0]))
                              ($asm (CDR)
                                ($lref x[3.0]))))))))
      ($lref table[1.0]))))
size: 5
0: CLOSURE #
  size: 37
  0: LET_FRAME(3) ;; (let loop ((x table)
  1: LREF_PUSH(0) ;; key
  2: LREF_PUSH(1) ;; table
  3: DISPLAY(2)
  4: LREF_PUSH(1) ;; table
  5: POP_LET_FRAME(1)
  6: MARK
  7: LREF(0) ;; x
  8: BNNULL 5 ;; (if (null? x) #f (le
  10: CONST #f
  12: JUMP 23 ;; #f
  14: LET_FRAME(4) ;; (let ((pair (car x))
  15: FREF_PUSH(1) ;; key
  16: LREF_PUSH(0) ;; x
  17: FREF_PUSH(0) ;; table
  18: DISPLAY(3)
  19: LREF_CAR(0) ;; x
  20: PUSH
  21: POP_LET_FRAME(1)
  22: LREF_CAR(0) ;; pair
  23: PUSH
  24: FREF(2) ;; key
  25: BNEQ 4 ;; (if (eq? (car pair)
  27: LREF(0) ;; pair
  28: JUMP 7 ;; #f
  30: FREF(1) ;; x
  31: CDR ;; (cdr x)
  32: PUSH
  33: SHIFTJ(1 1)
  34: JUMP -28 ;; #f
  36: RET

2: DEFINE(0) # ;; (define (lookup key
4: HALT
もう一つはGauche
$ gosh test.scm
($define () lookup
  ($lambda[lookup;0] (key[1;0] table[1;0])
    ($letrec ((loop[2;0] ($lambda[loop;0] (x[3;0])
                           ($if ($asm (NULLP)
                                  ($lref x[3;0]))
                             ($const #f)
                             ($let ((pair[2;0] ($asm (CAR)
                                                 ($lref x[3;0])))
                                    )
                               ($if ($asm (EQ)
                                      ($asm (CAR)
                                        ($lref pair[2;0]))
                                      ($lref key[1;0]))
                                 ($lref pair[2;0])
                                 ($call ($lref loop[2;0])
                                   ($asm (CDR)
                                     ($lref x[3;0]))))))))
              )
      ($call ($lref loop[2;0])
        ($lref table[1;0])))))
main_code (name=%toplevel, code=0x1043bc00, size=5, const=2, stack=0):
args: #f
     0 CLOSURE #      ; (lambda (key table) (let loop ((x table) ...
     2 DEFINE(0) #; (define (lookup key table) (let loop
 ((x ...
     4 RET
internal_closure_0 (name=lookup, code=0x1043bc18, size=6, const=1 stack=8):
args: #f
     0 LOCAL-ENV-CLOSURES(1) (#); (let loop ((x table)) (if (null? x)
#f ( ...
     2 LREF10-PUSH              ; table
     3 LREF0                    ; loop
     4 TAIL-CALL(1)
     5 RET
internal_closure_1 (name=loop, code=0x104c6f78, size=17, const=0 stack=8):
args: #f
     0 LREF0                    ; x
     1 BNNULL 4                 ; (null? x)
     3 CONSTF-RET
     4 LREF0-CAR                ; (car x)
     5 PUSH-LOCAL-ENV(1)        ; (let ((pair (car x))) (if (eq? (car pair ...
     6 LREF0-CAR                ; (car pair)
     7 PUSH
     8 LREF(3,1)                ; key
     9 BNEQ 12                  ; (eq? (car pair) key)
    11 LREF0-RET                ; pair
    12 LREF10-CDR               ; (cdr x)
    13 PUSH
    14 LREF20                   ; loop
    15 TAIL-CALL(1)             ; (loop (cdr x))
    16 RET
適当にスクロールして見てください。
一つ目の結果はそこまで変わらないのに、2つ目のインストラクションがまずいことに。
原因はSagittariusはmoshのようにDisplay closureを持っていて(これは3imp.pdfにあるやつ)、自由変数が現れると作られるのだが、この処理が重い。参照は早いが作成は遅いというもの。
Gaucheは逆に静的リンクで、作成は軽いが参照が若干重いというもの(だと思う)。
問題になるのは【*1】で示した場所で、ここでletをはさむためkeyとtableが自由変数になる。
ここで【*1】のletを削って、(eq? (car pair) key)を(eq? (car (car x)) key)とすると自由変数が無くなりちょっと処理が軽くなる。

この辺最適化でなんとかできるのかなぁ?stalinでも読むか。

2011-09-07

Fatal bug

I have checked the traffic of this blog, like from which country or which searching words. Somehow I got some traffic from Hungary or Ukraine. (I guess form the US are not non-Japanese speaker...)
So I believe it's good to write in English, very sometimes, even once in a blue moon. Sorry, I'm so lazy.
(If somebody eagerly requests me to write in English, I might do it.)

I've just found a fatal bug on my Sagittarius Scheme. How fatal? I need to say VERY!
Here you can see how serious it is.
(define (map) #f)
(display 'buzz) ;; -> COMPILE ERROR!
This is because I was too lazy to implement library system properly. In current implementation, each library has a hash table to store its variables or macros. The problem was it shares global location object by identifier name.
What does it mean?
It's really simple, even though it looks it has library system, it shares all variable in global. That's why the above problem was occurred.

The bug was actually found because I was trying to run Gambit's benchmarks. On its benchmarks, there is a function named "reduce". This is the same name as SRFI-1 has and the compiler is using it.
I am re-implementing the library system with hierarchy of libraries so that Sagittarius can separate global location object by library. It's almost done, I just need to re-write compiler a little bit and support rename or prefix stuff.

The most surprising thing was I even didn't notice this bug even though I was using it whole time. (Of course I don't re-implement those basic function for studying. I guess that's why.)

2011-09-06

竹内関数

最近ベンチマークとってないなぁと思って図ってみた。
使用コードは下記。

(import (rnrs))

(define (tak x y z)
  (if (not (< y x))
      y
      (tak (tak (- x 1) y z)
       (tak (- y 1) z x)
       (tak (- z 1) x y))))
(let ((args (command-line)))
  (let ((n (string->number (cadr args))))
    (tak (* n 2) n 0)))

図ったのは以下の3つ。
Sagittarius pre0.1.4
mosh 0.2.6
Ypsilon 0.9.6update3
結果は以下の通り
Sagittarius

real    0m0.725s
user    0m0.702s
sys     0m0.046s

real    0m0.723s
user    0m0.733s
sys     0m0.015s
-------------------
mosh
GC Warning: Repeated allocation of very large block (appr. size 69632):
        May lead to memory leak and poor performance.

real    0m0.794s
user    0m0.780s
sys     0m0.015s
GC Warning: Repeated allocation of very large block (appr. size 69632):
        May lead to memory leak and poor performance.

real    0m0.797s
user    0m0.796s
sys     0m0.015s
-------------------
Ypsilon

real    0m0.931s
user    0m0.015s
sys     0m0.030s

real    0m0.920s
user    0m0.015s
sys     0m0.015s

両方とも超えてる!最近施したインストラクションのチューニングが効いてるのかな。
竹内関数だけしかベンチマークとってないのであんまり参考にはならないけど・・・
まじめにベンチマークとろうかな。

追記:
試しにgambitのベンチマークを走らせて見たら爆死した。遅い上に途中でエラーで落ちよる。
まだまだだね(こしまえ風)

Spring webflowの話

サーバーサイドJavaをやってる人なら避けて通れないSpring framework。その中の一つにwebflowがあるのだが、これではまっている話。

話は実は2つある。

1つ目。
ステートの対するレスポンスの話。
webflowはフローチャートをwebで簡単に実装するためにあるようなもので、状態遷移の管理のフレームワークだと思えばいいはず。っで、はまったのはそれ+Ajaxで起きた。
ある状態からある状態に遷移する際にAjaxを使って一部だけを更新しようとした際に、以下のように書いてはいけない
<view-state id="state1" >
  <transition on="trans1" to="trans2" / >
</view-state>

<action-state id="trans2" >
  <!-- do something -->
  <render fragments="frag1" />
</action-state>
Ajaxを使っている場合は状態遷移を起こすとレスポンスがおかしなことになるっぽい。上記のは単にtransitionタグに入れてやればいいんだけど、似たような処理を一箇所にまとめるということができない。見事にはまった。
正直現状のバージョン(2.2を使用)はAjaxとの相性が悪い気がする。というか、そもそもこれ自体がAjaxを使いことを想定していないような感じ。

2つ目。
現在進行形。
webflowのログの話。
webflowは当然だがログを吐く。もちろんログの制御はlogback.xmlとかその辺のでできる。っが問題があって、どのレベルでも吐いてほしくないログというのはどこにでも存在する。たとえばパスワード。
どの世界にも平文のパスワードを吐き出してほしいと思う開発者はいないはず(まぁ、よからぬことを考えているなら別だが)。
っが、webflow君はあまりそんなこと気にしない。正確にはflowScopeに格納されたプロパティをDEBUGモードだと全部吐き出す素敵仕様。そもそもスコープにパスワードを入れるなということなのかもしれないが・・・

いつまでたってもSpringが好きになれない。すごいフレームワークだとは思うんだけど、なんだかなぁ感がいつまでたっても拭えないからに違いない。

2011-09-04

スペアリブ食べ放題

Grouponで(ずいぶん前に)キューポン買っていたのについに行ってきた。アムステルダムに行くのが今まで億劫だったというのがおもな原因だ。
予約が6時で、5時半にアムスに到着。トラムにのって目的地周辺へ。っで、ここからが問題で、目的地の通りはあるのにその番号がない。どうなってるんだ?と思いながら散策。っが、見つからない。
そうこうする間に6時。しゃーないと思い道を聞きつつ探す。この通りなぜか2本あって、トラムから見える一本目とその裏にもう一本。当然裏の方。見つかるかよ!
店自体は普通のレストラン。アルゼンチン料理のはずだが、なぜかインド人っぽい人が経営。よくある話ではあるが。店員さんは非常にいい人だった。
キューポンにはマグロのステーキ、鮭のステーキ、チキンステーキかスペアリブ2時間食べ放題を選べるとある。ので僕はマグロのステーキで、彼女がスペアリブを注文。1切れもやらんと息巻いていたなぁ・・・
出てきた料理。
マグロのステーキ
付け合せのポテト
本丸スペアリブ
でかかった。マグロもきっと400gくらいはあっただろう。スペアリブが3つって。
えぇ、2つほど僕が食べましたよ。何時の間にやら皿に乗ってるんだもん!!
結局食べ放題なのにお代わりせずでした。
どうでもいいが、えらい痩せてる黒人の女の子(高校生くらいに見えた)が普通に4つめを頼んでいたのにびっくりした。あの体のどこに入るんだろう?

2011-09-02

bloggerのインターフェースが変わった

別にだからどうということはないのだが、統計なるものがあってページビューとか訪問元はどことかが見えるようになった。 今までその辺まったく気にしてなかったのだが、月に100を超えるページがあったりして面白い。(世間一般よりははるかに少ないけど) っで、たいていそのページは昔書いたC++のことだったりJavaのことだったり、ajaxのことだったりするわけだ。 中身を読み返してみると、「せっかく検索してたどり着いたのにこんなごみ記事でごめん」と思うくらい自分のチラシの裏だったりするのでちょっと申し訳なく思ってしまう。 最近Springを使っているのでその辺の話も書いていこうかな。使い方ではなく、はまった話が中心になるけど。

TODOリスト

主にSagittariusでやっておきたいなぁと思ったTODOリスト。
  • let-valuesおよびlet*-valuesの組み込み構文化
    • 現状は(rnrs base)でsyntax-rulesによるマクロで定義されてるが、よく考えれば組み込みにできそう。
  • コンパイル時ラムダリフティング
  • srfi-98の対応
    • ぶっちゃけファイル作るだけなんだが、絶賛放置中。
  • Threadの見直し
    • かなり不安定なので。ただ、あんまり使ってないから優先度が低い。
  • 組み込みCharset
    • 現状はsrfi-14を使用。ただ、正規表現の実装で似たようなものがあるので、統合したい。そうするとどうしても組み込みで必要になる。
とりあえずこんなところ。多分まだある。

追記:
let-valuesとlet*-valuesの組み込みシンタックス化完了。思ったとおり簡単だった。

2011-08-31

マクロを書き直した

基本的な方針はバージョン0.1.3までと一緒だが、マクロ展開時に渡す環境をVMに持たせて参照できるようにした。また、今まで行っていた環境のスワップやパターン変数の保存などの入り組んだ処理を全部取っ払ってすっきりさせた。
そのおかげか、R6RSテストスイーツのcontributeとletrecの参照テストを除く8881個のテストが通った。(今まではbound-identifier=?のテストが一つ通ってなかった)
ここまでは順調だった(というっても2週間くらいかかったけど)が、やはりスコープは曲がらない。
ちょっとだけゆがむ程度。syntax-caseは不完全なんですって言い張ってしまうことにしよう。個人的な意見だとマクロ展開フェーズを持たないとあれはきついのではないかと思う。っが、単に勉強不足なのかもしれない。(参考資料も無いけどね!!)

このマクロ書き直しに伴ってキャッシュを少し修正。今まで識別子が持っていた環境内にあるマクロの書き出しを行っていなかったが、するように変更。また、不正オブジェクトの検出を強化。そのため、(core syntax)ライブラリが不正になるようになってしまった。
(なんでハッシュテーブルが内部にあるんだよ?)
オブジェクトの書き出しは本当にどうしようか悩むところで、絶対的にすべてのオブジェクトをキャッシュすることが不可能なのが分かっているからだ。基本的にはリスト、ベクタ、文字列、シンボル、識別子、マクロ、それにライブラリ(名前とimport、exportスペックだけ)で問題ないはずなんだけど、どこかのタイミングでそれ以外のものが入った場合どうしようかなぁと。今のところ分かっているのがハッシュテーブルで、ハッシュ関数が組み込みのものまでは入れてもいいかなぁとは思うものの、面倒だなぁというのが勝っている感じ。実際今のところ1つ2つしかないし。

どうでもいいが、キャッシュを使うとライブラリの読み込みがやはり早い。

2011-08-25

マクロの書き直し

をしているのだが、それに伴ってキャッシュが壊れていることが分かった。

事の発端は識別子にマクロ展開時に必要になる情報を足したところから始まる。それこそコンパイル時の環境をそのまま保存しようとしたのだが、どうやらその際に問題が起きる。
ただ、分かっているのはその壊れたキャッシュを読み込みとセグるということだけで、それがキャッシュの書き込み時の問題なのか読み込み時なのかが分からない。
一つ分かっていることとして、起きる場合は必ず一つのオブジェクトを読み込む際に一つ余計に読み込んでいるということ。ここから察するに、ベクターもしくはリストの読み込みもしくは書き込み時に問題が起きていると思われる。
そうは言っても、すべてのキャッシュで起きるわけではなく、正直何故起きるのかさっぱり分からないが、識別子にマクロ用の環境を持っている物に対してのみ起きる(ような気がする)

マクロの実装と同時並行で発生するのでかなり面倒だ。

2011-08-22

マクロ戦争再び

戦争ではないが気分的に。
とりあえず(自分に)必要そうなライブラリは揃った感があるので、先送りしていた問題に取り掛かろうかなぁと気分。

それに伴いマクロの展開とその環境を少しまとめて思い出しておこうというもの。

≪環境≫
マクロには2種類の環境があって、作成時にマクロ自体が持つmac-envと展開時にコンパイラから与えられるuse-envがある。ちなみに名前はChibi Schemeから。
前者の環境が何故必要かというと、例えばパターン変数などを一意にするために用いる(はず、の予定)
後者の環境はローカル変数の識別とか展開時する際にシンボルを識別子に変換するなどに使い

≪方針等≫
基本的には既存の物と一緒。マクロ(この場合はsyntax-case)をコンパイルし展開器を作成。展開器は単に関数を呼ぶだけ。問題となるのは、現状のままだと与えられたS式にmac-envとuse-envをくっつける形になるので、syntax-caseに直接S式を渡すものに関しては環境が取れない。
この辺をどうした方がいいのかちょっと悩み中。そもそも直接S式を渡された場合に環境は必要なのかとか。
また、現状でもそうだが、syntax-caseとsyntaxのコンパイルを別にする必要がある。

困ったことにR6RSは非常に人気が無いのかsyntax-caseの実装が知ってる限りでは3つしかない。1つがnmosh、Larcenyなどで使われているSRFI-78(だったと思う)をベースにした実装。2つ目が超有名なpsyntax。mosh、Chickenなど多数がこれ。最後がYpsilonの独自実装。
Racketも独自実装だった気がするが、ちょっとうろ覚え。
おかげで基本的にこれを解説しているサイトがない!論文もpsyntaxが基本とかで結構げんなり気味。あと、R6RSがマクロ展開とコンパイルを分けることを要求しているので、どれもそこまで参考にならないという落ちつき。
とりあえず比較的読むのが楽だったYpsilonの実装を参考にしているが、う~ん。

気合と根性で空中分解するしかないか・・・

Sagittarius version 0.1.3 has been released

As I mentioned before, I could not manage to write the document... I will do it by next release!!(I hope)
You can download it here

From this release, it has enough libraries(for me). So I will rewrite macro expansion which does not compromise R6RS syntax-case. (If I just use er-macro-transformer, it works perfectly though)

2011-08-17

ドキュメント書かなきゃ

と思いつつ遅々として進まない。
でも、いろいろ追加しちゃったからそろそろ0.1.3のリリース時期かなぁとか思いつつ。

0.1.3で追加される機能
* Cryptographicライブラリ
* 上記の付属する数学ライブラリ(ハッシュ関数とか、乱数とか)
* REPL
* lalrパーサージェネレータ
* ASN.1ライブラリ

(FFIって前回のリリースだったかな?)
最後のASN.1ライブラリができて、PKCS#1 v1.5パディングがRSAの署名と検証に追加できたらリリース予定。それまでにドキュメントが間に合えばそれも・・・
(多分無理-_-)

REPLを除いたライブラリはすべてSagittariusでしか動かないという素敵仕様(なのか?)。他の処理系との互換性なんてまったく考えなくなってきた。やべぇ。なにしろGauche由来のキーワードを使用したdefine(define-with-keyとして(sagittarius control)ライブラリで提供)が便利すぎて、ちょっと手放せない。こいつのおかげである処理の振る舞いを変更することが明示的かつ簡便に行えるので正直互換性を失っても有り余る便利さ。
(まぁ、他の処理系で何か書いているわけではないので、そんなこと言うのかもしれないが。自分用だしいいよね?)

以前書いたsyntax-caseの問題は0.2.0以降に先送りの方向にしている。自分で使う分には問題にならないし。

ちょっと先取り0.1.3
暗号ライブラリの部分を先取り。気に入ったら使ってくださいという宣伝。DES、DES3、RSA辺りはまじめにテストしてあるけど、AESとかは未テスト。仕事で使わないんだもん。
(import (crypto) (sagittarius control))
(define des-key (string->utf8 "8bytekey")) ;; DES鍵は8バイト
(define des-cipher (cipher DES des-key :mode MODE_CBC :iv (make-bytevector 8 0))) ;; :ivはCBCモードでは必須
;; その他:padder :rounds :ctr-modeがキーワードとしてある。必要になりそうなのは:padderとMODE_CTRを指定した際の:ctr-modeかな。
(let1 enc (encrypt des-cipher (string->utf8 "test message"))
  ;; encは上記のDES鍵で暗号化されたbytevector
  (display (utf8->string (decrypt des-cipher enc))))
こんな感じで使える。cipherが使いまわせるのはJavaと同じ感じにしたため。一度作成すると鍵の交換が現在できないけど、必要そうなら入れるかも。
共通鍵方式の暗号化はほぼlibtomcryptの機能なので、他の暗号方式でもよほどOKだと思う。
RSAも上記と同じAPIで指定するキーワードが少し違う。RSAでは鍵の生成も可能(共通鍵の方は用意してない)。問題は1024ビットの鍵を生成すると30秒くらいかかることか。(もちろんマシンスペックによるが)。
この辺は上記のASN.1ライブラリができたら保存と読み込みをサポートする予定。

暗号ライブラリがどれほどの需要があるかは分からないが、僕は年がら年中使うのですごく有用。DES3(DESede)はちょいちょい使ってるし。

仕事で使う使い捨てのスクリプトがSagittariusでかかれるようになってきてるなぁ、いいことだ。

2011-08-16

母が訪ねてきていた

ということで、先週の木曜日から1週間弱(6日)と短いがちょっと遅めの夏休みを取っていた。
(1週間弱で短いというのはヨーロッパ感覚です。休暇は2週間から!!)

だからといって母は去年既に訪れているのでいわゆる、とりあえず行っておけという場所は既に訪問している。なので今回は週末3日間はドイツに行ってきた。
ドイツはデュッセルドルフという人口59万人の都市。市街地だけなら1日あれば周れる。観光バス、観光フェリーもある。とりあえず、バスとフェリーは乗っておいて、市街地+日本人地区を周る。
デュッセルドルフの街は第二次大戦後、日本の各企業が欧州の貿易拠点に選んだらしく、一つの通りが丸ごと日本人地区になっている。いわゆるチャイナタウンの小規模バージョンみたいな感じ。ここでは日本語しか話せないおっさんがパン屋やってた。(いや、ドイツ語も話せるのかもしれんが、日本語だけで対応された)
まぁ、ここだけにいてもしょうがないので、アルトシュタット(Altstadt)に行く。どうでもいいが、ホテルの受付で案内を聞いたときに「old city」って言われてなんだろうと思っていたのだが、Altstadtの直訳がそれだった。
カフェがひしめき合ってるイメージでそこらじゅうにスポーツカフェ(バーか?)があった。どうもサッカーの試合があったらしく、どこも混んでいて人多すぎ状態に。適当にお茶して、フェリーの時間を待つことに。
(写真撮っておけばよかったかなぁ)
っで、フェリーの時間なので乗る。ちなみにフェリーはいまいちだった・・・行く方はバスだけの方がいいと思う。

観光バスはいわゆるHop On Hop Offバスなので適当に乗って適当に降りれる。でも、降りずに一周。ガイド音声に日本語もあるのだが、なぜか途切れ途切れになっていまいち分かりづらかった。話が飛ぶ感じ。「ここ、デュッセルドルフではアルトビールが・・・さてここでデュッセルドルフの歴史を振り返ってみましょう」みたいなの。多分、バスの運ちゃんがドイツ語の音声ベースで切り替えてるとみた。

特に「ドイツ!!」って感じのものはあんまり感じず、普通の街って感じ。少し外れまで行けば城とかあったみたいだけど、そこまでは周れず。
アルトシュタットだけでもいい感じなんだけど、ヨーロッパ中こんな感じだから割と飽き気味^^;
さすがに2泊3日(正味1日半)ではきついか。

余談だけど、ドイツのホテルでみた英語が微妙というか割りとおかしい部分が多々あった。あと、Test your English Freeなんてうたってる英会話の学校もあった。ドイツ人でも英語は苦手なのかもしれない。

ここからはちょっとだけオランダの話。
ドイツに行く前にZaanse schansというオランダの昔ながらの風車とかチーズとかを再現(?)している街に行ってきた。無料の明治村みたいな感じ(名古屋近郊の人しか知らんか)。
普通に人が住んでて、古い風車使って仕事してたり、伝統的なチーズの運び方したと意外と面白かった。キンデルダイクは(実は)行ったことないけど、ここも風車を見るならお勧め。

眠くて頭がまわらないから文章の構成がおかしいが、まぁいいか。

2011-08-06

EMSA-PSS-ENCODE実装

PKCS#1に詳細に処理が書いてあるのでそれをなぞるだけ。難しいことは何もない。(メモリの無駄遣いは多分にあるが、それは後回し)
問題は、これが正しいかを検証する方法が現状ではないことだ。このエンコード方式はランダムな数値を使ってるので
EMSA-PSS-VERIFYを実装して検証すればいいのだろうか?

2011-08-05

PKCS#1を読む

RSA署名に関してとりあえず必要な部分だけではあるのだが、
§8.1.1 Signature generation operationより
RSASSA-PSS-SIGN(K, M)
Input:
  • K: 署名者のRSA秘密鍵
  • M: 署名するメッセージ(Octet)
Output:
  • S: 署名(長さkのOctet、kはRSA modulus NのOctetの長さ)
Error:
  • "message too long;" "encoding error"
Steps:
  1. メッセージMに対してEMSA-PSS エンコーディングを施し(§9.1.1 (後述))、長さが[(modBits - 1)/8]のエンコードされたメッセージEMを作成。OS2IP(EM)(§4.2)で取得できるビット長が最大でmodBits-1のもの。modBitsはRSA modulus Nのビット長。
    EM = EMSA-PSS-ENCODE(M, modeBits - 1)
    EMのOctet長はmodBits - 1が8で割り切れる場合は、kより1小さく、そうでないならばkと等しい。もしエンコーディング処理が"message too long,"を返したら、"message too long"を出力し停止する。"encoding error"についても同様に処理する。
  2. 署名
    1. エンコードされたメッセージEMを数値mの変換(§4.2)
      m = OS2IP(EM)
    2. RSASP1署名プリミティブ処理をRSA秘密鍵Kと数値m施し(§5.2.1)、署名数値sを得る
      s = RSASP1(K, m)
    3. 数値sを長さkのOctet署名Sに変換する(§4.1)
      S = I2OSP(s, k)
  3. 署名Sを出力
Octet配列から数値への変換はすでにあるので特に気にしない。問題はEMSA-PSSエンコーディングか。(encodingって日本語訳なんだろう?)
ということで
§9.1.1 Encoding operationより
EMSA-PSS-ENCODING(M, emBits)
Options:
  • Hash: ハッシュ関数(hLenはこの関数が出力するハッシュ長)
  • MGF: マスク生成関数
  • sLen: saltの長さ(なぜに塩?)
Input:
  • M: 対象のメッセージ
  • emBits: OS2IP(EM)で取得できる数値の最大ビット長。少なくとも8*hLen + 8*sLen + 9必要
Output:
  • EM: エンコードされたメッセージ。長さはemLen = emBits/8
Errors:
  • "encoding error"; "message too long"
Steps:
  1. Mの長さがハッシュ関数の受け付ける最大長よりも長い場合(SHA-1なら2^61 - 1)、"message too long"を出力して終了
  2. 変数mHash = Hash(M)、ハッシュ長hLenの文字列
  3. emLen < hLen + sLen + 2ならば"encoding error"を出力して終了
  4. ランダムな文字列saltを生成。もしsLen=0ならば空文字
  5. 変数M'を定義
    M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt
    M'は長さ8 + hLen + sLenで、先頭から8OctetがゼロのOctet文字列。
  6.  変数H=Hash(M')、ハッシュ長hLenの文字列
  7.  emLen - sLen - hLen - 2で構成されたOctet文字列PSを生成。PSの長さは0でも良い。(構成って中身ってこと?それとも長さ?)
  8. 変数DB=PS || 0x01 || salt; DBの長さはemLen - hLen - 1
  9. 変数dbMask =MGF(H, emLen - hLen - 1)
  10. 変数maskedDB = DB ⊕ dbMask.(⊕= xor)
  11. maskedDBの左から8*emLen - emBitsを0埋めする。
  12. 変数 EM = maskedDB || H || 0xbc
  13. EMを出力
結構厳密に決められていた。でもマスク生成関数MGFって何?§B.2に記述があった。
§B.2.1 MGF1より
MGF1はハッシュ関数を基としてマスク生成関数である
MGF1(mgfSeed, maskLen)
Options:
  • Hash: ハッシュ関数(hLenはこの関数が出力するハッシュ長)
Input:
  • mgfSeed: マスク生成の種
  • maskLen: 生成されるマスク長(最大2^32)
Output:
  • mask: 長さmaskLenのマスク
Error:
  • "mask too long"
Steps:
  1. maskLenが2^32より大きいなら"mask too long"を出力し終了
  2. 変数Tを空文字として定義
  3. 擬似コード
    For counter = 0; counter > maskLen/hlen - 1; counter++
    
    1. counterを長さ4のOctet文字列に変換
    2. 種mgfSeedのハッシュとCのハッシュをTに連結
      T = T || Hash(mgfSeed || C).
  4. 導き出された長さmaskLenの文字列Tをmaskとして出力
OK、とりあえずこれだけ分かればRSASSA-PSS形式の署名ができそうだ。

      2011-08-04

      RSA鍵生成

      DES鍵の実装は終わっていて、現在RSAの鍵生成、暗号化、復号化の実装が完了。署名と検証はハッシュ関係がいるのでもう少し後。でもそれが一番重要だったりするのだが・・・

      っで、とりあえずテストも通って(テストケースは単純なのを書いただけだが)、ちょっと長め(というか普通の長さ、実用的には短い)鍵を生成して時間を計ってみた。
      生成するだけで20~30秒もかかりやがる!!!
      Javaだと5秒位で終わるというのに・・・

      鍵生成でボトルネックになるのは2つのでかい素数を探すところなのだが、こんなの何かいい方法があるのか?
      素数判定にはミラーラビン法を使用。決定的じゃないからちと怖いがまあいいだろう。AKS法は時間がかかるとのことだったし、ミラーラビン法ならWikipediaにRubyによる実装もあったし。

      やはり鍵は生成したあとどこかに保存して、使用時に読み込む方式にした方がいいな。まじめにASN.1を実装しようかな。バイナリが扱えれば何とかなるだろうし。
      RSAとDSAが実装完了になるとSSLの実装ができるようになるはずなので、.derファイルとかのことを考えればあった方がいいか。
      先は長い・・・

      2011-07-31

      Cryptographic

      暗号技術のこと。
      仕事柄よく使うのでSagittariusにもほしいなぁと思い実装中。わざわざJCE使って鍵生成とか、暗号、複合をするのは結構面倒なのでというのが主な動機。
      とは言っても中身自体を実装するのは骨が折れるのでlibtomcryptをバンドルしてそのラッパーみたいな感じにしている。
      ちなみにこのlibtomcryptがすごくよくできていて、どうしたらこんなきれいな設計できるのかちょっと嫉妬。ドキュメントもすごくしっかりしているという。う~ん、見習わないと。

      そんな素敵ライブラリなのだがECBとCBCモードで必要になってくるパディングは自前でやらないといけないということなのでとりあえずPKCS5のパディングを実装することに。理由はJCEのデフォルトだからというだけ。
      っでググって見るとあんまり日本語の資料に引っかからない。しょうがないので仕様書の方を確認してみた。
      仕様書はここ→RSA Laboratories - PKCS #5: Password-Based Cryptography Standard
      おもむろにバージョン2.1のPDFを開いて「Padding」で検索。
      3頁の項目4に記述を発見。
      4. Concatenate M and a padding string PS to form an encoded message EM:
      EM = M || PS ,
      where the padding string PS consists of 8-(||M|| mod 8) octets each with value 8-
      (||M|| mod 8). The padding string PS will satisfy one of the following statements:
      PS = 01 — if ||M|| mod 8 = 7 ;
      PS = 02 02 — if ||M|| mod 8 = 6 ;
      ...
      PS = 08 08 08 08 08 08 08 08 — if ||M|| mod 8 = 0.
      The length in octets of the encoded message will be a multiple of eight and it will
      be possible to recover the message M unambiguously from the encoded message.
      (This padding rule is taken from RFC 1423 [3].)
      要するに8からデータの長さを8で割った余りを引いてその値を新しいデータに値の数だけ足すということ。
      余りが0だった場合は8を8個末尾に追加。

      なるほどね。

      ちなみに、こんな感じになる予定。
      (define key (string->utf8 "8bytekey")) ;; DES requires 8 byte key
      (define des-cipher (cipher DES key :mode CBC_MODE :iv (make-bytevector 8) :padder pkcs5-padding)
      (encrypt des-cipher (string->utf8 "data to encrypt"))
      
      「:padder」キーワードは手続きを受け取り、パディングとアンパディングを行う。
      キーワードを「padder」にしようか「padding」に使用かは悩み中だが、多分「padder」でいいだろう。

      2011-07-27

      Syntax-caseめぇ・・・

      本当に実装者泣かせだ(僕だけ?)
      これが動かない
      スコープを曲げる
      以下引用
      (define-syntax let/scope
        (lambda(x)
          (syntax-case x ()
            ((k scope-name body ...)
             #'(let-syntax
                   ((scope-name
                     (lambda(x)
                       (syntax-case x ()
                         ((_ b (... ...))
                          #`(begin
                              #,@(datum->syntax #'k
                                   (syntax->datum #'(b (... ...))))))))))
                 body ...)))))
      
      (let ((x 1))
        (let/scope d1
          (let ((x 2))
            (let/scope d2
              (let ((x 3))
                (list (d1 x) (d2 x) x))))))  ;; ⇒ (1 2 3)
      これがunbound variable xで&assertionを投げる。
      (キャッシュを使ってるともっとひどくて、マクロ展開時に死ぬ。何故だ?)

      なぜ動かないか、「足りないからだ、憎しみ(識別子の中の環境のこと)が・・・」(by うちはいたち)

      問題になるのはdatum->syntaxに渡されるテンプレート用の構文オブジェクト = 識別子。
      Sagittariusでは他のR6RS処理系と違って、「k」は使い回しされる。かつ、「k」はここでは「let/scope」そのものになり、大域に出現した識別子となる。(多分これがもとでbound-identifier=?が上手く動かない)
      問題はここからで、グローバルな識別子はそれ自身に環境を持っていない。持つ必要がないから。
      そうすると、datum->syntaxが渡されたテンプレートに梱包されている環境(グローバルなので空)を元に新たな識別子を作り、結果、上記の(d1 x) (d2 x)はグローバルな呼び出しとなる。
      突き止めるまでに1時間くらいかかったが、原理は分かった。問題は、ない情報をどこから引っ張り出してくるかということになる。
      無知な頃に(今でもだが)作った(パクった?)部分が多々あるので、コードの読みにくさもあいまってマクロ周りは出来がひどい。リファクタリングも兼ねて一度見直すべきだろうか?

      LGPL vs MITライセンス

      SagittariusはMIT(修正BSD?)ライセンスで配布しているのだが、そのなかにLGPLで書かれたスクリプトを入れることは可能なのだろうか?という疑問。
      MITライセンスはほぼ何でもありというライセンスなのだが、LGPLはライブラリが静的にリンクされる場合に限りGPLと同様のコピーレフトを持つと解釈できる。
      でも、スクリプトファイルをそのまま(若干の改変あり)で配布する際、こいつは動的にリンクされるので、このファイルだけを公開すればMITライセンスと共存できる、はず。
      そもそも、スクリプトファイルの実行って動的リンクなのか?

      バグつぶし

      ドキュメントを書いていると、R6RSから少し拡張した機能がきちんと働いているかテストしていないことに気づいたり、一貫性の問題で新たに機能を足したりすることがある。
      例えば、vector-refはR6RSでは2つの引数を取るが、Sagittariusではオプション引数を取ることができる。第2引数がベクターの範囲外だった際にその引数の値が返されるというものである。アイデアはGaucheからもらってます。
      っで、その機能を使ったことないなぁと思ったので試してみたらまったく上手く動いていない。その上、範囲外の値を渡したらセグったという落ちも。(RacketのR6RSテストスイートはよくできてるし、割とそれに頼りまくっているが、エラーケースはあんまりテストされてないっぽい)
      こういうことが実は結構あって、ドキュメント書きながらコンソールまたはEmacsで動作を確認→不具合を見つける→修正という作業が頻繁に起こる。以下に適当に作ってるかわかるなぁ・・・リリースしたくせにWindows版では動いてない機能とか・・・(socketのことだよ!)
      不具合が見つかって修正されるのはいいことなのだが、これくらいのものはもっと以前に見つけてないといけない気がする。テストケースを足していく必要があるということか。

      2011-07-21

      FFIが動いた!!!

      libffiを使っているので、ある意味当たり前なのだが、こんなコードが動いた。
      (define kernel32 (open-shared-library "kernel32.dll"))
      ;; 構造体もサポートしてるぜ!!
      (define-c-struct systemtime
        (unsigned-short year)
        (unsigned-short month)
        (unsigned-short day-of-week)
        (unsigned-short day)
        (unsigned-short hour)
        (unsigned-short minute)
        (unsigned-short second)
        (unsigned-short milliseconds))
      
      (let ((proc (c-function kernel32 void GetLocalTime (void*)))
            ;; ughh
            (sys (c-malloc (size-of-c-struct systemtime))))
        (proc sys)
        ;; アクセスの仕方は考えた方がいいかもしれない。ちょっとダサい。
        (print (c-struct-ref sys systemtime 'year)) ;; 2011
        (c-free sys))
      
      ちなみにコールバックも兼ね備えていて(まぁlibffiのおかげだが)、こんなこともできる。
      (define lib (open-shared-library "add.so"))
      (define array (u8-list->bytevector '(6 5 3 4 1 7 2)))
      (let* ([qsort (c-function lib void quicksort (void* size_t size_t callback))]
             [compare (c-callback int (void* void*) 
             (lambda (x y)
               (- (pointer-ref-c-uint8 x 0)
           (pointer-ref-c-uint8 y 0))))])
        (qsort array (bytevector-length array) 1 compare)
        (display array)
        (free-c-callback compare))
      
      add.soなんてのを使ってるのは、cygwin上でのlibcがどれだか分からなかったため。。。
      単にqsortの自力実装なんだけど、まぁ動く。
      実行結果が、ソートされてないのはmoshもだったので気にしない(でいいのか?)

      できればlibffiをバインドしたいんだけど、あんなに複雑なconfigure.acをCMakeLists.txtにできる気がしないので、後回しということにしてしまう。。。

      2011-07-17

      キャッシュが遅い

      キャッシュを作って早くなったと思っていたのだが、実は遅かった。
      コマンドラインオプションに--disable-cacheがあるのだが、それを使ったのと、普通にキャッシュを呼んだのとで0.3秒ほど違いがでる。単に(import (rnrs))と書いたファイルを実行するのにだ。
      プロファイルを取ろうとしたらgprofがOut of Memory errorとか言いやがるので、timeコマンドの意味を調べてみた。
      userが計算時間、systemがI/O、realがトータル。
      これを頭に入れて再度時間を計測。

      $ time ./build/sash test3.scm
      ./build/sash test3.scm 1.37s user 1.06s system 93% cpu 2.603 total

      $ time ./build/sash --disable-cache test3.scm
      ./build/sash --disable-cache test3.scm 1.92s user 0.20s system 89% cpu 2.368 total

      I/Oか。計算時間もそこまで差が無いように見えるなぁ。
      3倍早くなった気がしたのは、作成時間がかかってただけだな。
      見直すか。

      追記:
      見直した。ファイルの内容を一発で取ってきてI/Oの回数 = ファイルの数にしてみた。結果

      $ time ./build/sash test3.scm
      ./build/sash test3.scm 0.87s user 0.19s system 89% cpu 1.184 total

      倍早くなった。計算部分はキャッシュ自体を小さくするか、より効率的にするしかないな。
      ただ、この方法はかなりメモリ効率が悪い。何とかしたいところではある。
      (そうは言っても、コンパイルにものすごくメモリを食うので、結果的にキャッシュを使った方がメモリ使用量は少ないが)

      2011-07-15

      Lady Gaga

      ちょうど今TVに出てて、生(かどうかは知らんけど)で歌っていた。
      正直今まで、色物な歌を歌ってる奇抜な衣装のねーちゃんくらいのイメージしかなかったんだけど、歌唱力が半端なかった。
      PVとかCDの音だとそこまで分からないんだけど、マジでうまい。同じレベルの歌手があまり浮かばないくらい。
      結構踊りも激しかったんだけど、全部歌ってたし、すげ~なぁ。トップアーティストってのはこういうのなんだなぁとちょっと感動した瞬間だった。
      曲自体はキャッチーだけど、あんまり好きではなかったりするが・・・(^^;

      2011-07-12

      R6RSのレコードにあるprotocolが地味に便利だった件

      あまりに複雑すぎる(とどっかで見た)と不評なR6RSのレコードだが、単に使うだけなら結構便利な件。
      特に今までつかってなかったけど、protocolが結構便利。
      例えばこんな例:
      (define-record-type something
        (fields (mutable f1)
                (mutable f2)
                (mutable f3)))
      
      上記のレコードsomethingはfieldsとしてf1~f3まで持つけど、初期化時にコンストラクタに値を渡さないかもしれない。上記の例では必ず
      (make-something #f #f #f)
      
      なんて書かないといけないが、protocolを定義すると事情が変わってくる。
      (define-record-type something
        (fields (mutable f1)
                (mutable f2)
                (mutable f3))
        (protocol (lambda (p)
                    (lambda args
                      (let-optionals* args ((f1 #f)
                                            (f2 #f)
                                            (f3 #f))
                        (p f1 f2 f3))))))
      
      let-optionals*は探せばいろいろあると思うけど、Sagittariusでは(sagittarius control)ライブラリで提供。こうすると上記のmake-somethingがこんな風に書ける
      (make-something 'value)
      
      ここまでは、まぁ割と当たり前なんだけど、Sagittariusの一つの特徴としてキーワードを認識する。っで、キーワードを使ってこんな風に書ける。
      #!compatible ;; キーワードはr6rsモードでは動かない
      (import (rnrs)
              (sagittarius control)) ;; let-keywords let-keywords*もこの中
      (define-record-type something
        (fields (mutable f1)
                (mutable f2)
                (mutable f3))
        (protocol (lambda (p)
                    (lambda args
                      (let-keywords args ((f1 #f)
                                          (f2 #f)
                                          (f3 #f))
                        (p f1 f2 f3))))))
      
      (make-something :f3 'value)
      
      let-keywordsはGaucheからもらってます。これが地味に便利。フィールドが少ないうちはそんなに感じないけど、10個くらいあって、7番目だけ値を入れたいなんて場合にこれが活躍してくる。難点は、全部に値を設定したい場合に一々キーワードを書かないといけないところか。ま、トレードオフだろう。

      2011-07-11

      Harry Potter

      最初の映画を見てたりするのだが、今更ながらに気づいたこと。
      ずっとハーマイオニーだと思ってたのだが、彼女の名前はHermioneでよく聞くと、ハーミオニに近いこと。
      っで、オランダ語の字幕にはHermiolineと何故かLがくっついてること。

      どうでもいい話。

      ところで、ハリーポッターって何がハリーを特別にしてるの?家系?映画見ててもその辺全然説明無くて、いきなりセレブだって言われるからさっぱり分からん、

      2011-07-10

      ドキュメントを書き始めた

      いろいろ何を使おうか悩んでいたが、別にtexinfoとか自前で何か作るとかは馬鹿らしいなぁと思ったので、結局OpenOffice.orgのWriterにした。
      それでいいのか?という疑問は未だに付きまとうけど、単なるAPIドキュメントという側面だけではないようにしたいと思うと割としっかりしたドキュメント作成用のソフトが要るようになる。わざわざテキストファイルで書いてくTexとかはあまりに面倒だし、今から覚えるのも億劫だったという理由から。
      最終的にはOOoフォーマットからLaTexか何かに変換してHTMLを出力という形に持っていこうとは思っている。OOoフォーマットだとソフト持ってないと読めないし。

      ドキュメントと書いてて思ったのが、かなりの分量が必要になるということ。R6RSで規定されている関数とかも全部書いているからなのだが、かなり凹みそうになる。なぜそんな部分まで必要になるかというと、R6RSで規定されている関数を拡張しているものがあって、単純に仕様書にポインタを指すということができないから。
      割とコピペで済ませる部分が多いが、これをやってて仕様を満たしていない部分を発見したりするから割と有用かな(今まで必要な部分しか読んでなかったというのもあるが)。時間かかるけど。

      2011-07-09

      とりあえず一旦完了

      Compiled Cacheの実装が一旦完了した。キャッシュから読み込むと3倍くらい早いのでまずまずだろう。
      いくつか問題点があって、
      ・依存関係にあるキャッシュが変更された際に上手く動かない
      ・キャッシュ自体が壊れていた場合に上手いことエラーハンドリングしない
      などというところがとりあえず分かっている。
      が、問題だなぁと感じてから直すことにする。

      ほしいと思って投稿してからほぼ1週間これの実装にかかったわけだ。結構かかったな。
      まぁでも、それなりに待てる時間まで実行時間が短縮されたので無駄ではなかっただろう。

      2011-07-07

      はまってたこと

      ずいぶん長い間気づかなかったが、Cygwinのマウント形式に長いことはまっていた。
      Cygwinはバージョン1.7からfstabが使えるのだが、それを最初に記述した際に、NTFSをtextでマウントしていたみたい。
      っで、賢いのかバカなのか分からないけど、Cygwin上でread(freadではない)を行った際に、\r\nが\nに自動的に変換されていた。
      バイナリで扱ってほしいのに、そんなものまでテキストにされていたとは。

      Twitterでつぶやいた1バイトスキップされる問題はこれだったということ。こんなの中々気づけないよ。

      2011-06-30

      思案中のこと

      Sagittariusが遅い。
      結構まともなライブラリを作ろうとしているのだが、それのテストというか1回走らせるのに2~3秒、長いときで5秒以上待たされる。ライブラリを除いた実際のコードは数行なのにだ。
      これは、まぁ、原因まで全部分かっていて、コアの機能以外は全部そのときに読み込んで、コンパイル、実行しているからだ。さらに、マクロ展開の関係もあって、コンパイラがかなり重い。

      っで、FASLを導入してみようかと思ったのだが、これって単にS式をバイナリにしただけなのね。それでは結局上記のルーチンが走るので美味しくない。どうやら本当に必要なのはCompiled Cacheのようだ。

      名前などどうでもよくて、それが要ると思って考えていたのだが、いろいろ解決しなければならない問題が既にある。

      ・ライブラリ単位にするか、ファイル単位にするか。

      どちらも問題があるが、ライブラリ単位がすっきりしてると思うのでそっちで。

      ・複数ライブラリが1ファイルにある場合はどうするか?
      ・ライブラリ外でモジュールを読み込んでからライブラリを作成しているファイル(実際既にある)ものはどうするか?
      ・どの段階でキャッシュするか?
      ・実行ファイル内(この場合は起動ファイルを指す)で、ライブラリを定義した場合にそれをキャッシュするか?

      思いついたというか、考えている段階で答えが浮かばなかったのがこの辺。
      4つ目は最悪キャッシュしてしまえばいいか。っで、import時に引っ掛けると。ただ、キャッシュ時間が問題になりそうだ。

      さて、どうしようかな。

      日本人がオランダに長くいすぎてしまったと感じるとき

      これの逆版
      あんまり思いつくこともないんだけど、面白そうだから。

      1.電車やバスが10分以上遅れてもなんとも思わない。
      日常的に10分くらい遅れる。というか時刻表はたんなる目安でしかない。

      2.自転車が歩道を走ってると警察を呼びたくなる。
      自転車用の道があるんです。

      3.シリコン製のへらを日常的につかう。
      けちなオランダ人は、洗う必要がなくらいきれいにビンの中身を食べます。

      4.大麻のにおいが嗅ぎ分けれるようになった。
      一応、コーヒーショップの外では吸ってはいけないはずなんだけど。

      5.休暇が1週間だと短いと感じるようになった。
      まとめて取れるんです。

      6.残業する人を見ると可哀想だなぁと思うようになった。
      まぁ、滅多に見ないけど。

      7.自分は背が低い方だと思うようになった。
      成人男性の平均身長185cmだったかな?

      ネタが尽きた。
      7個とは微妙すぎる数字だ。
      なんかあったらまた書こう。

      単に行き詰っていたので息抜きに書いてみたとも言う。

      2011-06-28

      発見し難いバグ

      原因を特定しにくいと言うべきか。
      まぁそんなバグに悩まされている。Javaや元々GCがあるような言語なら問題ないのだが、(ここまで言ったら何の問題かすぐ分かるが)、メモリー管理の問題である。

      現状SagittariusではBoehmGCを使っている(そのうち取り替えてやる!と思っているが、GCはむずい。。。)。別にGCが悪いというわけではなく、どこかでメモリー破壊が行われている可能性があるとう問題。
      読み込んだシンボルが、ある特定のプロセスを踏むと変更されている。発現性は100%なのだが、場所の特定ができない。この前、正規表現を実装している時に同じ現象が起きた。そのときは割り当てたメモリを越えて値を埋めていたので起きたのだが、その際の原因は非常に簡単で、メモリのサイズの計算が行われていなかったというもの。
      例えば、mallocでintの配列10個確保するのにこう書いてたという問題:(int)malloc(10)
      JavaやC++やってるとnew演算子が便利すぎて忘れる問題の一つだと信じている。信じたいとも言う。っが、今回はどうも違うらしい。メモリ割り当ての箇所を全部チェックしたが問題ない。ということは、割り当て時の不具合ではなく、使用時の不具合なので、それが使用されている箇所を全部洗いなおす必要がある。正直、砂漠の砂を数える気分になる(そんなにはないが)

      どこかにいいメモリ破壊検出ツールはないだろうか?

      2011-06-26

      (match)ライブラリで見つかった気になるバグ

      Sagittarius Schemeは一応Andrew Wrightのパターンマッチが使えるのだが、それを使用したプログラムで起きた気になるバグの話。

      現在Sagittarius SchemeではCとSchemeを繋ぐ仕組みとして.stubファイルを使用している。ちなみに、アイデアはGaucheからいただきました。っで、そのファイルをcgenというSchemeで書かれたプログラムに処理させてCのファイルを生成する。利点はわずらわしい引数の数のチェックとか、型チェックとかをそれなりに省略できることと、Schemeのマクロチックにマクロを記述でき、かつそれらが確実にインライン展開されること。実際、.stubファイルから生成された.cファイルはサイズが4倍程度に膨らむ。つまり記述量が4分の1ですむということ。
      話がそれた。そのプログラムでは上記のmatchを使っているのだが、最近変更したsyntax-rulesをsyntax-caseで実装するというので問題が起きた。matchの中で'_が単なるシンボルとして比較されていたのだが、syntax-rulesで生成された後のS式は全部ラップされて識別子になっている。っで、'_の比較が失敗するというもの。
      何が気になるかというと、これからもしmatch相当の何かを書くことがあった場合、この辺を気にしないといけないということと、他のR6RSの実装ならその辺を気にしなくてもいいということ。正直、他の処理系との移植性は大分あきらめた部分はあるのでどうでもいいのだが、'_とか'...が識別子にラップされてもいいのかなぁとちょっと不安になってたりする。
      現状動いているものを変更するのは怖いので(単に面倒くさがり)、とりあえず現状のまま様子をみて、問題が発生したら変えるということにしよう。

      P.S.
      Twitterをはじめました。アカウント名は@tk_ripleです。こういうところでさらしていいものなのかもわからないのですが、どうせ見てる人も少ないということで。

      P.S.2
      最初に登録したユーザー名から上記に変更してます。こんなものも変更できるのね、Twitter。

      2011-06-25

      めがね

      前の眼鏡を5年以上使っていたので、新しいのを買った。
      Tommy Hilfigerというブランド物である。まさか自分がブランドの着いたものを買うとは夢にも思わなかった。

      つけた自分を鏡で見たときに、なんともトレンドな感じがして違和感を覚えたが、まぁいいか。

      以下は余談。
      こっちでは視力の指標に+-数字というのを使う。
      例えば僕の視力は-6(左)と-8(右)という結果だった(ちなみに、マイナスは近視でプラスは遠視)。今まで日本の0.1とかと違って自分がどれくらい目が悪いのか分かり難い。
      慣れの問題ではあるのだが、単位が変わるというのはいつでもそれに対応するのに困る。

      2011-06-18

      ぐぬぬ

      syntax-rulesが遅いのでsyntax-caseで実装したものを試してみたら、動かない。いや、動くんだけど、正しく動かない。
      その理由も分かったのだが、解決できる気がしない・・・

      理由は分かれば単純なのだが、例えばこんなの。
      ;; testはR6RSテストスイートのマクロ
      (test (let ((x 'outer))
         (let-syntax ((m (syntax-rules () ((m) x))))
           (let ((x 'inner))
             (m))))
       'outer)
      
      これは割りと典型的な例で、マクロの中でマクロを生成すると起きる。(実は違うかもしれない)
      この例だと(m)が返す値はouterじゃないとまずいのだけど、innerの方を返す。何故か?
      syntax-caseがパターンとテンプレートをコンパイルする際にすべてのシンボルを識別子へと変換する。その際に、すべてのxが同一の識別子に変換されるので、let-syntax内にあるxとinnerそ束縛しているxが同一のものになってしまう。
      これを回避するうまい方法が思いつかない。マクロの展開フェーズとコンパイルのフェーズが分かれていれば、マクロを展開する際に使用する環境やらマクロ展開フェーズやらがいろいろ知っていればいいのだが、現状の実装ではletが変数を束縛するということを知っているのはletに束縛された構文オブジェクトだけなんだよね。
      また、マクロを展開してからコンパイルするわけではないので、マクロ展開中に変数束縛を作るマクロが出現した際に結局おかしくなる。
      こうなってくると僕の足りない脳みそでが悲鳴を上げてくる。
      現在の実装はYpsilonのものをSagittariusでも動くように適当にいじったものだが、根本的な理解をしていなかったため結構穴がある(例えば上記のものとか)。
      この穴をふさぐためには実装のしなおしだろうか?しかし、そもそも今のモデルで上記のような問題を解決できるのだろうか?

      ・・・

      考えるか・・・

      2011-06-16

      Socketが動いた!!

      Socketの実装が完了。まだテストケース書いてないけど・・・

      とりあえず簡単なechoサーバーを書いてみた。
      (import (sagittarius socket))
      
      ;; creates echo server socket with port number 5000
      (define echo-server-socket (make-server-socket "5000"))
      
      ;; addr is client socket
      (let loop ((addr (socket-accept echo-server-socket)))
        (call-with-socket
         addr
         (lambda (sock)
           ;; socket-port creates binary input/output port
           ;; make it transcoded port for convenience.
           (let ((p (transcoded-port (socket-port sock)
                ;; on Sagittarius Scheme native-transcoder
                ;; uses utf8 codec for ASCII compatibility.
                ;; For socket programming it might be better
                ;; to specify eol-style with crlf.
                ;; But this sample just shows how it goes.
                (native-transcoder))))
             (call-with-port
       p
       (lambda (p)
         (put-string p "please type something\n\r")
         (put-string p "> ")
         ;; gets line from client.
         (let lp2 ((r (get-line p)))
           (unless (eof-object? r)
             (print "received: " r)
             ;; just returns message from client.
             ;; NB: If client type nothing, it'll throw assertion-violation.
             (put-string p r)
             (put-string p "\r\n> ")
             ;; waits for next input.
             (lp2 (get-line p)))))))))
        ;; echo server waits next connection.
        (loop (socket-accept echo-server-socket)))
      
      これを起動しておいて、別ターミナルからtelnet localhost 5000で繋ぐと動く動く!!
      あんまりにうれしいので、example/socket/echo.scmとか作ってしまった。初example!!

      次はFFIでその次がGUIか。
      GUIまでできたら実用的なアプリを書きたいなぁ。

      2011-06-14

      宗教観

      <<警告>>
      以下の文章は繊細な問題をダイナミックに否定しています。中東辺り発祥の宗教に属している方は読まないことをお勧めします。もし気分を害したり、聖戦だぁ!なんて言い出されても当方は一切責任を取りません。

      身近な人がユダヤ教に改宗しようとしているのの関係で、元々宗教、特に中東辺りを発祥とするものが嫌いだったのに拍車がかかった。
      とりあえず、前々から思っていたこととして、唯一神教は総じて腐っているということ。
      何故か?こいつらの過激派は自分たちの神のためといって戦争を起こす。また、他宗教の人間を邪教徒といい虐殺する権利を持っていると言い放つ。実際、現在問題になっているテロリストの多くはイスラム教だし、歴史を紐解けばキリスト教が起こした略奪戦争は多々ある。
      また、神父、宣教師、その他いろいろは神の言葉と称して人の洗脳を行う。その昔日本でも宣教師が神の教えと称し、奴隷として人々の売り買いをしたことは割と有名だと思う。そのため秀吉はキリスト教を全面的に禁止したとかしないとか(多分理由の一つだろう)

      正直中身の教えに関してはどうでもいいのだが、とりあえずユダヤ教についていろいろ知ってしまった。まぁ、そのために嫌いになったという話もあるのだが。
      とりあえず、ユダヤ教の好きになれない食べ物の話。
      彼らにはKosher(日本語訳は知らない)というルールがあって、割と厳しく食べていいものとだめなもの、混ぜて食べてはいけないものがある。中でも一番気に入らないのは、Kosherな肉というやつだ。
      正直何が普通の肉と違うのかはさっぱり分からないが、彼らの話によると、宗教上他の生き物の苦しみを体内に取り込んではいけないため、できる限り苦しまないよう殺した肉のみを食べるという。この段階で傲慢さがにじみ出てるなぁと個人的には思うのだが、まだまだある。豚は不浄な生き物なので食べない。魚はいいけど、海老とかは海の昆虫なので食べない。Unkosherなものを作った鍋は使ってはだめ、だってその鍋には苦しみが染み付いてるじゃん。
      ちなみに、彼らによるとKosherでない肉はBAD MEAT(訳は適当に自分でどうぞ)なんだそうだ。つまり、我々が食べている肉は中に苦しみが詰まっていて、食べるには不浄な物なんだと。(そこまでは言ってないかも--;)
      個人的にKosherとか単なる言い訳にしか聞こえなくて、「肉食べたいけど、教えに背くなぁ・・・よしそれっぽい規定作って食べれるようにしよう」という風にしか見えない。素直に菜食主義になれと言ってやりたい。(というかもう言ったが)

      つらつら書き出すと止まらなくなりそうなのでこの辺で。
      でも、何よりも一番怖いなぁと思ったのは、宗教は人を変えるということ。大抵の場合は悪い方に。人と話す際の3大タブーに、政治、人種、宗教があるが、まさにそうだなと実感した。彼らは自分たちの宗教に関する否定的な意見は聞きたくない。まぁ、そこまでは分かるが、問題だなぁと思ったのは、こちらが疑問に感じたことを質問しても攻撃的な態度だと多くの場合は捕らえる。何故か?理由はおそらくいたって簡単で、「自分が何の疑問も無く信じていることに対して疑問を投げかけるなど、その宗教があたかも信仰するに値しないごみくずのように扱われているかの様に感じる」のだろう。そこにもはや言論の自由はない。
      っが、困ったことに宗教人というのは多かれ少なかれ自分の信仰しているものを他人と共有したがる傾向がある。そうすると事は複雑になって、相手はこちらからは肯定的な意見しか聞きたくないが、こっちはそんなもの聞きたくないので否定的もしくは懐疑的な反応しかでない。こちらが大人の対応を見せて聞き流す、もしくは肯定的に適当な相槌を打つということをすればいいのだが、そうした際にこちらにもその宗教に対して興味があるなんて思われたくないし、肯定的な理解をしているとさえも思われたくない。この辺は個人的に譲れない部分になるので、関係が悪化しようが、瓦解しようがおそらく反応は変わらない。

      これを読んで、本当にそんなことあるのかよ!と思った方、多少の脚色はあるけど上記の意見は体験に基づくものだったいりするのです。
      もし周りに、なんらかの宗教に改宗しようなんて考えている知人がいる方、その人と良好な関係を保ちたいなら全力で止めることをお勧めします。

      2011-06-12

      マルチスレッド

      とりあえずなんとなく形になってきた。
      Windowsでも動くようにしたら後はバグ取りなんだろうけど、どこにバグがあるのかさっぱり分からない。マルチスレッドは難しいなぁ。正直、デッドロックが発生した際にそれがC側なのかScheme側なのか判別が難しい。今になって必要だったかなぁ?と少し疑問も。。。

      それはさておき、現状で少し(大分?)問題が露呈してきた。
      まず、遅い!!
      通常の状態ならそこまで遅くないのだが、(rnrs)ライブラリのimportが遅い。理由は分かっていて、(rnrs)ライブラリをimportすると40近いファイルをロード&コンパイルする。これはそもそもr6rsのスタンダードライブラリが20以上に分かれていて、Sagittariusの実装ではそれぞれがファイルになっている。っで、さらにその中に実装の記述が入っていて、初回ロードでコンパイルと実行がなされるので遅い。
      この辺、MoshとYpsilonでは(rnrs)ライブラリを組み込みにしていて、少なくともコンパイルの処理がカットされているのでそこまでの負荷にはなっていない。加えて、Sagittariusではsyntax-rulesが重たい処理なのでR6RSが要求するcase-lambdaなどsyntax-rulesで記述されているマクロのコンパイル及び、展開が非常に重い。これはsyntax-rulesをsyntax-caseで書けば早くなるのだが、何故かexportが上手くいっていないのでとりあえず保留にしてある。
      次いで、エラー処理。
      with-exception-handlerやguardマクロを使って何かを記述した際に、C側(VM含む)で何らかの例外を投げると、例外処理がされずに終了することがある。これも理由は分かっていて、Schemeで記述されたassertion-violationをC側で呼び出さないと落ちる。理由は分かっているのだが、最終的に同じ関数を呼び出すのになぜこれが起きるのかいまいち理解してない。とりあえず、VMと、CとSchemeを繋ぐ部分のエラー処理でassertion-violationを使用することで回避。
      まだいくつかあるけど、大きなのでREPLがない。
      REPLを実装すること自体はそんなに難しくないんだけど(履歴なし、変な入力を受け付けないものならSchemeで実装されたものがあったりする)、そのままでは使い辛すぎてもう少し何とかしないと、と思いつつ他の方にいってて進まない。どうしようかな。

      落ちはない。

      2011-06-02

      iPhoneのiOSをバージョンアップ

      してる最中。
      やってる途中でいろいろ問題が発生して焦ったがなんとかなりそう。
      写真とか残ってるといいなぁ・・・

      とりあえず起きた問題。
      • iTunesのアップデートでCドライブの空き容量がなくなった。
        • .Net Frameworkをアンインストール
        • Open Officeをアンインストール
        • 復元ポイントの削除
        • WINDOWSフォルダ内の$NTinstallフォルダを削除
          • 600MB -> 2.3GBまで増えた
      • エラー番号1604が発生
        • Appleのサイトに詳しいトラブルシューティングがあったので実行
      今のところ同期作業が進行中で何とかなったくさい。あとはJBするだけか。SBSettingsがないと不便すぎるのよw

      2011-06-01

      Verion 0.1.0

      とりあえずそんなバージョンでリリース。
      単にこれから加える変更は0.1.0以降と決めていただけで、当初の目的であるR6RS準拠というところまで実は行っていない。自分用のアプリが書ければいいや、という風に思っているのでそこまで気にしなくてもいいだろうというのもあるが。
      なんかの格言に、「どうにも行かなくなったらそれをバージョン1と呼べ」なんてのがあったけど(コンピュータージョークだったかな?)、そんな感じ。

      とりあえず、0.1.0から0.2.0の間でやりたいなぁと思っていることとその優先順位。
      1. マルチスレッド
      2. ソケット
      3. FFI
      4. GUI
      そこそこ実用的なことを書こうと思ったらこれくらいはいりそう。1.はsrfi-21準拠にする予定。srfi-21はsrfi-18の後継みたいなので。でも、Larcenyとかsrfi-18, 21はPOSIXを前提にしてるからサポートしないという姿勢もとってるし、変えるかもしんない。
      ソケットは、YpsilonとMoshのAPIが一緒だった気がするので、それに合わせる予定。何があるか列挙しないと。
      FFIはYpsilonから盗む。正直自分の頭で考えられるようなものじゃないくせに、あると非常に便利なので。あと、dlopen相当のものが必要になってくるはずなので、その辺も考える。
      GUIは設計から考えないといけないんだよね。JavaのSwingみたいにしようかと思ったけど、あれってオブジェクト指向じゃないと厳しい気がする。ActionListenerとか。Qtのシグナルみたいな感じにするといいかな?

      優先順位は上から高→低なんだけどどれも全部ほしいので頑張る。ただ、ソケットとマルチスレッドは実装の順番を入れ替えるかもしれない。
      まずは、こういった足すことを前提にしたCから使えるユーザーオブジェクトの作成からかな。Schemeからは実は近いのがあるんだけど(いろいろおかしなことになってきてるけど、ばっちり使われてる)、Cからそれを使うのは結構厳しい。なのでCから使われることを前提にしたものがいる。構想はあるのでとりあえずやってみる感じ。ただ、同時実装に近いものになるだろうので、第一号はソケットかスレッドのどちらかになる。難易度の低そうな方とも言う。

      上記ができたら、DBIとかの実装もできるだろうか?
      仕事で使うツールとして今の玩具の状態を早く脱したい。

      2011-05-31

      BBCニュースより

      iPhoneでBBCニュースを読んでいたときに見つけた記事。
      Japan pensioners volunteer to tackle nuclear crisis

      (リンクが切れてても知りません)

      僕のiPhoneにはBBCとNew York Timesのアプリがあるのだが、BBCの方が記事が短くていい感じなのでよく使ってる。
      (あと、割と文章が平易なので読みやすいというのもあるが)
      っで、今日上記の記事を見つけた。
      訳を載せていいのか分からないので、要約だけ。
      定年を迎えた元原発作業員の方が、同年代の同僚、友人に声をかけて自分たちが福島原発での作業を行うように働きかけている。論理的に60歳を越えたら放射能による癌が発現しにくいため(発現するまでに13~15年かかるので、それまでに寿命がくるという意味で)、若者よりもリスクが低い。だが、決して「神風特攻隊」ではなく、「生きて返ってくる」という意志の元で行う。
      大体こんな感じ?(要約苦手な上に、日本語が出てこなくなってる・・・)

      こういう方が戦後の日本を支えたのかなぁと思う一方、余生を満喫してほしいなぁとも思ったり。

      2011-05-30

      syntax-rulesの挙動がおかしい件

      の原因が分かった。
      分かってしまえば非常に単純かつ根が深い問題である。
      原因は、quoteをした際に識別子がunwrapされてしまうため、環境の参照の際に見つからなくなるというもの。
      なので、quoteが現れたときにunwrapしなければこの問題はおきない。

      なんだけど、syntax-rulesないでquoteしたシンボルをwriteもしくはdisplayを使って出力した際に、S式ではなく識別子が出力されてしまう。
      多分これを回避するためにunwrapしたんだと思うけど、それではまずいよなぁ。
      どうしたものか。

      2011-05-29

      ドイツに行ってきた

      この帽子ドイツんだ?オランダ!
      ということでドイツに行ってきた。

      といっても、ベルリンとか遠いところではなく、オランダとの国境から比較的ちかいところ。
      その辺にMovie Parkなるものがあるといことで行ってきた。
      どんなのかなぁと思っていってみたら、Universal Studioのドイツ版でした。サイズは日本のより少し小さいくらいか?でもローラーコースターもあったので、その気になれば比較的遊べると思う。
      と思うと書いたのは、僕は無理だったので・・・いや、すべてのアトラクションがドイツ語だし。大学の時に第2外国語でとったけど、きれいさっぱり忘れたし。Ich bin ein Berliner Bal だけではねぇ・・・

      そんなこんなであまり遊べず、帰りの電車の関係もありデュッセルドルフに移動。ここにはラーメン屋があるという噂を聞いたので食べれたらいいなぁ、くらいの感覚で来たのだが、これまたびっくり、一つのとおりが日本人街になってた!!
      あと、なにかのイベントがあるのか、アニメキャラクターのコスプレイヤーが一杯。よくネットで海外のコスプレはレベルが高いというのを見るのだが、ありゃPhotoshopの技術が高いのか、1割くらいの当たりの写真を使ってるかのどちらかだと思う。
      実際、9割くらいはGood looking boy/girlではなかったので(叶姉妹風。古い?)
      目的のラーメン屋も意外とあっさり見つかり、辺りからは日本語の会話が山ほど聞こえるなか、ラーメンを注文。普通か旨い部類に入ると思うけど、あんまりラーメン通ではないのでよく分からん。
      でも、醤油ラーメン、タンタン麺、餃子+飲み物2つで29.20ユーロは高くね?

      //////
      投稿を分けるのも面倒なので、こっちに。

      ドイツに、というときはデュッセルドルフに行ったときに感じたこと。
      海外で日本人に会うとどうも変な気分になる。理由はよく分からなかったんだけど、ラーメン食べてるときになんとなく分かった気がした。
      彼らも英語(もしくはドイツ語)を話す。なんとなく自分だけではないんだなぁというのを突きつけられるのと同時に何故か劣等感を感じてるんだろう。多分その劣等感は、彼らはまだ若い。僕が彼らくらいの時にはそんなことできなかった、そんな勇気もなかったというのが劣等感につながっているのだろう。
      おそらく、個人的にあまり日本人コミュニティーに近づかないのは、それも理由の一つなのだろう。いや、でもアムスの日本人コミュニティーは駐在さんメインだよなぁ。それとこれは別か?郷に入って郷に従わないのが嫌いなだけかも。

      //////
      超個人的な話。

      弱音を吐きそうになる瞬間が増えてきた。原因も分かっていて、自分ではどうしようもできない問題の部類の一つだ。これが仕事とか、言語とか、文化の違いとかならまだ楽だったんだけどなぁ。
      じっとこらえるべきか、日本に帰ることを視野に入れるべきか。後者はきっと今と同じくらいに苦しいのも分かってたりするんだよねぇ。
      去年応募したアメリカのグリーンカード当たらないかなぁ。(アメリカ嫌いなのにこんなことを願うあたり、かなりキテるなぁ)

      2011-05-26

      マクロに潜んでいる罠

      たいした物言いだがたいしたことではない。
      Sagittariusで発見して、Gaucheでも再現するという不思議なバグの話。
      以下のコード
      (define-syntax hoge
        (syntax-rules ()
          ((_ args ...)
           (letrec-syntax 
        ((find (syntax-rules ()
          ((_ a b c)
           (next a b))))
         (next (syntax-rules ()
          ((_ a b c)
           (find a b c))
          ((_ a b)
           (print a b)))))
             (find args ...)))))
      (hoge 1 2 3)
      
      findとかnextはSSAXのマクロから取っているだけで別に名前はなんでもいい。
      これを実行すると、unknown variable nextというのが出る。でも、letrec-syntaxの部分だけ取り出して実行すると動く。
      原因は分かっていて、識別子の比較が弱いためにコンパイル時環境のlookupが失敗しているのだ。
      不思議なこととしては、最初のhogeが展開されたときに作成される識別子は正しく作成されている(この場合は同じアドレスという意味)のに、lookup時はなぜか失敗する。
      コンパイル時環境のlookupはGaucheとほぼ同じなのだが、syntax-rulesの実装はまるで違う(はず)。なのに両方でこれが発生するということは、まぁ、そこなんだろう。もしくは、syntax-rules内で予定外の識別子がrenameされたかのどちらかなんだけど。

      原因を追究していたのだが、疲れたので息抜きに書いてみた。

      2011-05-25

      昨日の続き

      とりあえず、途中経過をダンプしてみた結果、どうもSagittariusのsyntax-rulesにおけるキーワードの扱いが間違っていそうである。

      どういうことか?
      昨日の段階で、arg-nameが複数回出現していると書いたが、よく見るとsyntax-rulesでキーワードを設定する箇所でばっちり全部吐き出している。つまり、展開されたarg-nameはすべてキーワードなのだ。
      ということは、これはR5RSだろうがR6RSだろうがきっちり通るはずなのだが、Gaucheでは通らなかったので勘違いしてしまったということである。

      マクロのパターン展開をもう一回見直すことに相成り候。。。

      2011-05-24

      sxmlを使えるようにしたい

      仕事柄、自分用の何かしらを書くのにXMLパーサーくらいは絶対的に必要になる。ということでSchemeでつかえるXMLパーサーということでSSAXをporting中。
      必要そうなSRFI(13、14とか)を実装して、いざSSAXを流してみたらsyntax-rulesでおかしなエラーを出してくる。
      エラーはパターン変数が重複しているというもの。
      Ypsilonで試したらきっちり動く。でも、Gaucheでは同じエラー。
      ちなみに、こんなsyntax-rules
      (define-syntax ssax:define-labeled-arg-macro
        (syntax-rules ()
          ((ssax:define-labeled-arg-macro
             labeled-arg-macro-name
             (positional-macro-name
        (arg-name . arg-def) ...))
            (define-syntax labeled-arg-macro-name
       (syntax-rules ()
         ((labeled-arg-macro-name . kw-val-pairs)
           (letrec-syntax
             ((find 
         (syntax-rules (arg-name ...)
                         ;; ここが問題
           ((find k-args (arg-name . default) arg-name
              val . others)    ; found arg-name among kw-val-pairs
            (next val . k-args)) ...
           ((find k-args key arg-no-match-name val . others)
             (find k-args key . others))
           ((find k-args (arg-name default)) ; default must be here
             (next default . k-args)) ...
           ))
        (next   ; pack the continuation to find
          (syntax-rules ()
            ((next val vals key . keys)
              (find ((val . vals) . keys) key . kw-val-pairs))
            ((next val vals) ; processed all arg-descriptors
              (rev-apply (val) vals))))
        (rev-apply
          (syntax-rules ()
            ((rev-apply form (x . xs))
              (rev-apply (x . form) xs))
            ((rev-apply form ()) form))))
             (next positional-macro-name () 
        (arg-name . arg-def) ...))))))))
      
      問題なのはarg-nameが2回出現していること。仮に展開されるとしてもarg-nameは同じものを指すので、展開後もパターン変数が重複する。仕様書を完全に理解してないけど、これはsyntax-rulesの動作として正しい。個人的に気になるのは、Ypsilonではなぜ動いたのかということと、他のR6RSの実装でもこれは動くのかということ。(もし他のR6RSの実装でも動くのなら、ちと考えないといけないかもしれない)
      っで、sxmlをライブラリとして持っているGaucheはなぜ動いたのかというところを探ってみたところ、単純にバージョンが古かった。最新版といっても2004年ととても古いのだが、Gaucheのはさらに1年古い2003年のもので、make-parserはdefine-macroで書かれていた。
      一応最新版と思われるbiglooで動く実装を覗いたら、そちらもdefine-mecroで実装してある。う~ん、define-macro自体はあるし、er-macro-transformerでの書き換えも非常に楽なんだけど、どうしたものだろう?
      とりあえず他のも見てみるか。

      2011-05-20

      VCでのコンパイルが通った

      __imp_の魔の手に苦しんだが、一応コンパイルが通った。
      ってか、__impl_って同考えてもVCの不具合にしか思えないくらいひどい仕様だ。これを使って開発しようという気をごっそりともってく、かなり高等な武器だともいえるか。
      R6RSのテストも既知のものを除いて通らないものは1つだけという優秀さであった。
      通らなかったのは、UTF16のトランスコーダーが返すEOLでcrlfが期待値なのにlfを返してるというもの。
      あんまりまじめに見てないが、まぁ、割と簡単に直るだろう。
      そろそろバージョン0.1.0のリリースも近いか?

      2011-05-19

      かなりいいところまで来た感じ

      RacketのR6RSテストのほぼすべてが通るようになった。
      とりあえず通らないテストとしては、bound-identifier=?のテストの1つと、contribのテスト1つ。contribのテストは多分syntax-rulesがsyntax-caseのサブセットじゃないと通らないんじゃないかなぁと推測。
      Sagittariusではsyntax-rulesはexplicit renamingで実装されているのでまぁいいかと。
      bound-identifier=?のはそこまで厳密にrenamingが行われていないのと、まだまだsyntax-caseの実装が怪しいのとで原因不明。そのうち直ったらいいなぁとは思うが、この手続き自体あまり使わないので放置になるかな。
      もう一つ、別環境でやったら通らなかったテストが、ポート周りのテストでstring->bytevectorとbytevector->stringを使ってるやつ。ファイルを介してテストを行うので、改行コードが処理系内部の改行コードとして処理されて通らない。ファイルを介さなければ普通にでる。気が向いたときに直そう。

      ちょいちょい気になるのが、多分どこかでメモリ破壊を起こしてるんだろうなぁという出力結果が出るときがあるのだが、どこだ分からない上に、デバッガ使うと辿れない系のやつ。
      見つけ次第つぶしてるけど果てしない。

      ここからは愚痴。
      ついでにVCでもコンパイルしようとVisual Studio 2010をインストール。っが、VCってC99に対応してないのね。結構手直しが必要になってる。特にmath.hのINFINITYとNANが面倒。あと、off_tのサイズが違うのでどうしようかな。off_t使ってるとこを全部int64_tにした方がいいだろうか?

      2011-05-08

      (再)letrec-syntaxにはまる

      実際にはlet-syntaxだが、どちらも同じなので。
      あんまりR6RSの仕様書をまじめに読んでないことが分かってしまうのだが、こんなコードにはまる。
      ;; (1)
      (let () 
        (let-syntax ((def (syntax-rules ()
              ((def stuff ...) (define stuff ...)))))
          (def foo 42))
        foo)
      ;; (2)
      (let-syntax ((def (syntax-rules ()
            ((def stuff ...) (define stuff ...)))))
        (def foo 42))
      foo
      
      R5RSでは上記のコードは不正になる。let(rec)-syntaxはおそらくスコープを作るのだろう。(Gaucheで確認しただけで、R5RSを読んだわけではない)
      っが、R6RSではこれはOKで、let(rec)-syntaxはbeginと同じなのだそうだ。なので、(1)の場合は単なるinternal defineに展開されると考えればいいのだろう。
      explicit phasingでlet-syntaxが展開された後のことを考えればそうなのかもしれないが、(2)のケースを考えると納得がいかないなぁ。

      どちらにしろ、これは直さないと。でも、とりあえず先送りにしよう。こんな書き方することはないだろうし。

      2011-05-03

      Call/ccの実装が甘い

      R6RSテストを通そうと足掻いているのだが、いまいち上手くいかない。
      現在問題になってるのは、Cで実装されたassertion-violationがCのスタックを食いつぶすこと。
      単発なら問題になることはないのだが、テストケースでは&assertionが発生してもテストを継続するため、例外 -> ハンドラ -> 例外 -> ハンドラ 以下続くとなりCのスタックを食いつぶす。
      なぜこれが発生するかというと、dynamic-windがCで実装されていてかつ、call/ccがSchemeレベルの継続しか捕捉しないため、上記のようなC→Scheme→C→Schemeといった交互にレイヤーが変わるようなものに対して、継続が呼ばれた際にSchemeのスタックは戻すけど、Cのスタックはつぶされたままということになる。(のだと思う)
      (Ypsilon、moshはdynamic-windがSchemeレベルで実装されているのでcall/ccで上記の問題は起きないと予測。Gaucheはそれように継続を捕捉した際にCのスタックもjmpbufという形で捕捉している、と思う)

      解決方法はおそらく2つあって、dynamic-windをSchemeレベルにする、または継続を捕捉する際にCの継続も捕捉するのどちらかになるのかと。
      とりあえず、後者かな。現状回避だけ見れば前者が早いのだけど、もう少し先を見据えると、dynamic-windはCで実装されててほしい気がする。

      2011-05-01

      Queen's Day

      昨日はQueen's Dayでした。日本でいう天皇誕生日です。
      っで、フリーマーケットがあるので情報収集(たまには日本語を話したくなるともいう)も兼ねてアムステルダム南駅周辺に繰り出してみました。
      こんな感じで適当に場所取りして物を売ってます。
      まぁ、メインはアムス中央なのでここは割と閑散としてます。
      適当に物を買いつつ、近くに日本食品を扱ってたりするスーパーはないかとか聞いたり。
      まぁ、僕はあまり社交的ではないので適当ですが。後、この辺は日本人の駐在さんが多いみたいで、なんとなく空気が違った感じがしたというのもあるけど。
      (自身のコミュニティからあまり出てこない感じ?)

      適当に歩いた後、ついでにテレビでやっていた日本スーパーを探してみることに。
      意外と近くにあったけど、さすがに閉まっていた。
      来週行ってみることにする。