Syntax highlighter

2012-03-16

クロージャー?

Twitterで呟いたが、Javaの無名クラスは厳密にはクロージャーになりえず、JDK8辺りで入る予定のlambdaも無名クラスの糖衣構文になるとかという話を昨日バーで同僚としていた。
糖衣構文でもあれば便利だろうなぁと思う場面が山ほどあるので、是非早いとこ実装してもらいたいが(JDK7では見送られた)。
話に参加していたもう一人の同僚が、違いが分からないと言っていたので、関数型言語のクロージャーとどう違うのかというのでも書いてみようかなぁと。ただ、僕自身あんまり理解してないので間違いは山ほどあるかと・・・

そもそも、クロージャーとは何ぞやと。詳しい説明はWikipediaでも見てもらうとして、上記で議論になっていたのは捕捉時の環境が閉じているかどうかということであった。JavaやC++ではここが完全に閉じることができず、渡された自由変数が参照渡しになる。以下はJavaでそれっぽく見せるようにしたコード。(C++では補足さえ出来ない)
package closure;

public interface Lambda<T> {

 T apply(Object... args);
}

// 別ファイル
package closure;

public class Closure {

 private Object obj;
 
 public void run() {
  // property
  Lambda<Object> lam = new Lambda<Object>() {
   @Override
   public Object apply(Object... args) {
    obj = args[0];
    System.out.println(obj);
    return obj;
   }
  };
  final Object obj2 = (Object)"string";
  Lambda<Object> lam2 = new Lambda<Object>() {
   @Override
   public Object apply(Object... args) {
    // obj2 = args[0]; // *1 compile error
    System.out.println(obj2);
    return null;
   }
  };
  System.out.println(lam.apply("here we go"));
  System.out.println(obj);
  lam2.apply();
 }
 
 public static void main(String[] args) {
  Closure cl = new Closure();
  cl.run();
  
 } 
}
*1の部分が重要で、Javaでは無名クラスの中から参照できる自由変数(とここでは呼ぶ)は変更不可能でなければならない。多分JVMの実装上も問題だろう。lam2がrunから戻った際にスタックからobj2が消えるのでその辺が問題になると思われる。
ではわれらがSchemeではどうだろうか?
(define obj #f)

(define (runner lam1 lam2)
  (print (lam1 "here we go"))
  (print (lam2 "we can do it")))

(define (main args)
  (let* ((obj2 "string")
  (lam1 (lambda (o) (set! obj o) o))
  (lam2 (lambda (o) (set! obj2 o) o)))
    (runner lam1 lam2)
    (print obj #\: obj2)))
#|
here we go
we can do it
here we go:we can do it
|#
あまりいい例ではない気もするが、lam2で捕捉されたobj2の値が変更されるとlambdaの外側のでも問題なく変更されている。
これが出来て何が嬉しいかと言われると正直微妙ではある。(結構使うけどこういうの)
ただ、Javaでもfinalつければ捕捉出来るんだし、ほぼ同じじゃないのだろうか?(いい加減)
(define obj #f)

(define (runner o run?)
  (let* ((obj2 o)
  (lam1 (lambda (o) (set! obj o) o))
  (lam2 (lambda (o) (set! obj2 o) o)))
    (cond (run?
    (print (lam1 "here we go"))
    (print (lam2 "we can do it"))
    (print obj #\: obj2))
   (else
    (values lam1 lam2 obj2)))))

(define (main args)
  (let ((o "string"))
    (runner o #t)
    (receive (lam1 lam2 obj2) (runner o #f)
      (print (lam1 "here we go"))
      (print (lam2 "we can do it"))
      (print obj #\: obj2))
    (print o)))
#|
here we go
we can do it
here we go:we can do it
here we go
we can do it
here we go:string
string
|#
こっちの方が捕捉時の環境というのが捕らえやすいだろうか?あんまり変わらないか?

2012-03-13

British or American English?

カナダにいたときに購読し始めたニュースレター(今はほとんど読んでなかった)から届いた面白げなタイトル。
その中にあった動画の一つがこれ
簡単な英語と米語の語彙の違いを紹介している。違いは以下のような感じ
BritishAmerican
TrousersPants
PantsUnderwear
視ると面白い。MeanがずっとBritishの方しか知らなかったり(Being Nastyなんて知らなんだ)。意外と米語だと思っていたのが実は英語だったとか(GardenとYardとか。スコットランドヤードって言うやん!)
自分の語彙を見ると主に日本で習う英語は米語だと思うが、たまに変なのもある(Trousersとか、学校ではこれがズボンと習ったなぁ。でも下着はUnderwearだった気がする)
謳い文句には文法も書いてあったのにサイトにそれらしいものは特に無かった。残念。

昨日の続き

意外とてこずっている。(まぁ、当たり前か・・・)
syntax-caseの実装が甘いのがここにきて痛い感じだ。
たとえばこんなコード。
(define-syntax print
  (syntax-rules ()
    ((_ o)
     (begin (display o) (newline)))))
(print
  (let ((x 'outer))
    (let-syntax ((m (syntax-rules () ((m) x))))
      (let ((x 'inner)) (m)))))
xという変数は合計で3回出現しているが、printマクロを噛ませることですべて同じ識別子へと見事な変貌を遂げている。これが問題なのだ・・・
syntax-rules内のxが3回目のxと同じため出力としてはinnerが帰ってくる。でも、printマクロを噛ませないとouterが帰る。これは最初のxが束縛された際の環境をマクロ展開器が知っているため、単なるシンボルであれば環境を閉じ込めて識別子にすることができるからだ。

あぁ、そうか。ならラップする際に何とかできればいいのか。いや、待てよ。同じ識別子なんだから、環境内でも同じか。結局簡単な方法はunrenameすることになるのか・・・

さて、もう一ひねりしてみるか・・・orz

2012-03-12

マクロとコンパイラと不具合と

ずいぶん昔から以下のようなマクロが動かない。
(define-syntax define-lambda
  (syntax-rules ()
    ((_ name formals body ...)
     (define name (lambda formals body ...)))))
(define-lambda test (t rest) `(t ,t))
(test 'a 'b) ;; => (t.xxxxxx a) xxxxxxは適当な16進数
なぜか?実は原因も全部分かっているのだが、直すに当たって余計な不具合を入れないためにもこれが必要だった経緯を書いておく。

そもそも、これは何か?
これはsyntax-caseのR6RSテストスイートをパスさせるために入れた苦肉の策である。
具体的には、syntax-caseを通った式はすべて識別子に変換される。その際に、識別子の同一性を保つため同一のsymbolであれば同一の識別子になるようにしている。

何故これは起きるのか?
上記のままでは拙い場合があった。問題としては一度識別子に変換されると2度目の変換ルーチン(以下renameと呼ぶ)ではそのままその識別子返してくる。その際に、ローカルに束縛された識別子が問題になった(と思う)。だが、マクロの展開器は賢くないのでコンパイラ側でローカルに束縛された識別子をシンボルに直してやる必要があった。また、その際に単に構文情報を剥ぎ取るだけではだめで、シンボルに直された識別子が一意である必要があった。
上記の例では(define-lambda ...)の中で「t」というシンボルはすべて同一の識別子に変換される。普通にquoteされるなら構文情報を剥ぎ取るだけなので問題ないが、quasiquoteまではunrename(と呼んでいる)ルーチンは見ない。そのため、quasiquote内の最初の「t」はunrenameされた状態で出力される。

どう解決するか?
解決方法は2つあると考えていて、unrenameがquasiquoteを見る。もしくは、unrenameをやめる。
前者は正直これ以上糞コードを増やしたくないので却下。後者はなぜunrenameなどと言うものが必要だったかという経緯を考えれば問題ない気がする。
つまり、renameルーチンが識別子を受け取った際に、その識別子をコピーしてやればいいはず。そうすれば、識別子が同一過ぎて困るという問題は無いはずだ。具体的にはwrap-syntaxという処理が式を識別子に変換しているのでそれをちょっといじって、コンパイラからunrename関係の処理をごっそり削ればいけるはず。

とりあえず試してみる。

2012-03-08

開発環境

ほぼ月1でリリースしてると、月1でフルビルドしないといけないということになっていて、Cygwin+非力なマシンでは厳しいなぁと思えてきた。
もっと非力なマシン+Linux GCCだと速いという事はやはりCygwinが遅いんだろうなぁと思い、何か別な環境はないかと探してみる。

とりあえず基準というか絶対条件として、
  • CMakeがサポートしてるコンパイラ
というのがどうしても入ってくる。そうするとどうしても問答無用で絞られてきて、現状の最新バージョン2.8.7ではGCC、MinGW、MSVC(含むVSプロジェクト)、Borland C++とWatcomであった。まぁ、GCCの部分はUnix Makefileなのでコンパイラ自体はなんでもいいのだが。結局Cygwinを避けるためには自前でmake相当のコマンドを持っているコンパイラになるわけだ。
っで、BCC32(フリー版の5.5)は僕が大学の時からバージョンアップしてないという記憶なのでさすがに10年以上前ではどうよ?と思いパス。(まぁ、こじゃれた最新機能はつかっていないのでOKな気もするが)となるとWatcomしかないじゃんと思い試してみた。

結論。BoehmGCすらコンパイルできなかった・・・orz

多分どこかCMakeLists.txtの記述がおかしいのだろうと思うのだが、Windows環境ではMSVC決め打ちの部分が多いので修正も大変。とりあえずwmakeが打てるところまできてこれが起きたので一気に気力が持っていかれた。

じゃあ素直にMSVCでいいじゃんと思うのだが、インストールに2G以上使うような富豪プログラムなのでHDDのスペースが足りない。(Cドライブが10Gしか切ってない上に、すでに8G使われている)
Windows SDKは問答無用で.NETがくっついてくるし、打つ手なし。

だれかいい案ないですか?

2012-03-07

DER, BER, CER?

ASN.1の代表的なエンコード(?)方式。
正直CERは見たことないのでよく知らないが、職業柄DERはよく見るしそれにくっついてBERもよく見る。

Sagittariusに(ようやく)ASN.1のバイナリを読むライブラリを入れたのでちょっといろいろ整理も兼ねて。
基本的な部分はBouncycastleの移植といった感じにしてあるので、クラスの継承関係もそんな感じ。
DERを読むならBERを読むのも簡単なので、DERを基本クラスにしてそのBERは派生。
Readerはタグみて読むだけなので、クラスの派生を知る必要はないが、両方読めるようにタグの種類でdispatchする感じ。
エンコードはおそらく本来はBERのオブジェクトも条件によってはDERを出力しないとまずいんだろうけど、面倒なのでBERだけ出力。つまり、BERを含む証明書(あるのか?)は読み込みしてDERだけに変換という荒業はできない。(これはそのうち何とかするかも。parameter使えば多分なんとかなる)
昔作ったASN.1のパーサーがあるけど、これどうしよう?一応バイナリと構文の整合性チェックに使う予定だったんだけど、やるのしんどい・・・

DERとBERの違いはなんぞやというGoogle検索の結果を勝手に予測して。
ここが詳しいけど、一応自分でも。
本質的には一緒。DERはタグ、長さ、データの順で書かれたバイナリ。タグは基本2種類あって、プリミティブと拡張(?訳語しらない)されたもの。最初の1バイトと0x1Fを論理積(and)した値が0x1Fなら続く数バイトはタグになる。
BERの方が機能的にはスーパーセットらしい(構築されたoctet stringとか?)。PKCS12形式の証明書群の中にあるのを見た。多分暗号化されたDERとか入れれるのだろう。(他のViewerで見るとどうもSagittariusのはうまく読めてないくさい。そのうち直そう)

2012-03-02

Sagittarius version 0.3.0リリース

リリースノートを書くのが億劫というか、何を直して何を追加したか思い出せん。もっとまめにChangeLog書かないと・・・

Sagittarius Scheme 0.3.0がリリースされました。このバージョンからCLOSが組み込みでサポートされます。ただし、すべての機能がサポートされているわけではありません。使用するには(clos user)もしくは(clos core)ライブラリをインポートしてください。

修正された不具合
  • internal defineされた値がマクロ内から参照できない問題が修正されました。
新たに追加されたライブラリ
  • (clos user) 及び (clos core)が追加されました。CLOS風のプログラムを書くにはこのライブラリをインポートする必要があります。
  • (text markdown)が追加されました。簡単なマークダウン形式のテキストのパース及びHTMLへの出力を行います。
  • (scribble)が(scribble parser)、(scribble convert)及び(scribble plugin)に分割されました。
  • (sagittarius document html)が追加されました。Sagittariusが作成するHTML形式のドキュメントを作成するのに使われています。
  • (getopt)が追加されました。SRFI-37の薄いラッパーです。
改善された機能
  • regex-replace-all 及び regex-replace-firstの第2引数がプロシージャを取れるようになりました。Javascriptの機能にinspireされたものです。詳しくはドキュメントを参照してください。
  • binary-port?及びtextual-port?がR7RS互換になり、ポート以外の引数を受け取ってもエラーを投げなくなりました。
  • get-bytevector-n及びget-bytevector-someのパフォーマンスが多少改善されました。内部的に2度メモリの割付を行っていたのが解消されています。
  • (sagittarius process)の機能が大幅に改善されました。JavaのProcessクラスの設計思想に基づいて書き換えられています。
  • (sagittarius threads)でしばしばデッドロックを起こす問題が解消されました。これによりドキュメントのサポートSRFIの欄に正式にSRFI-18が追加されています。ただしWindows上でthread-terminate!を使うと予期しない動作をします。現状確認されている不具合です。
新たに追加されたドキュメント
  • (sagittarius process)が正式にドキュメント化されました。

多分これ以上にいろいろいじったんだけど、思い出せない。

rebase問題+process

Sagittariusでプロセスの実装をしている際に、Cygwin上だとforkでころころ落ちてていらいらしていた。Cygwinのforkは不安定だという話は聞いていたので、こんなものだろうかと思っていたのだが、(実際、出てくるエラーメッセージもresource temporary not availableだったし)、いろいろ調べてみるとrebaseすると直るみたいな記事が山ほどあったので試してみた。

結果、あぁ、これだったのね、という感じで今はさくさく動いている。別段Cygwin上でプロセスを起動させることはないのだが、(これ自体Windowsのバッチでmavenのビルドスクリプトを書くのが苦痛で作ったものなので^^;)、動くとなるとなんらかに使えるかなぁとか考えてしまう。

と、これだけだと微妙なので、新生プロセスライブラリの概要でも。(誰も見てない?)
低レベルAPIのシグネチャ(これって日本語でなんていうの?)は特に変更なし。ラッパーで作っていたrunプロシージャが(name args)から(name . args)になって、書くのが多少楽になった。
今までは問答無用で標準入出力にリダイレクトしていたけど、パイプを通してSchemeからそれらの出力結果を取得できるようにした。(低レベルAPI)

実装上どうしようもなかったのだが、POSIX環境とWindows環境で微妙な差がある。POSIXだとプロセスの使い回しができるのに対し(PIDは一緒だが)、Windowsだとできない。これは、Scheme上では見えないけど、プロセスオブジェクトを作った際に実際にWindowsではプロセスを作ってしまうが、POSIXは後からforkとexecで実行するための情報を格納しているだけという違い。
設計思想的には両方ともWindows風にしてしまいたいが、僕の頭では無理。

理由:
プロセスオブジェクトを作成した段階でそれぞれのリダイレクト先にアクセスできるようにしたかった。でないと走らせる前に入出力のリダイレクト先を読み取るとか書き込むということができないから。また、作成と同時にプロセスが走るようにするとオブジェクトを作った意味が薄れるため。
誰かいい解決方法教えて。

2012-03-01

Win32プロセス

プロセスの実装を何とかしようとしているのだが、調べても答えが出てこない問題がでた。

現状、プロセスを作成した際に標準入力、出力、エラーを名前なしパイプに割り当てている。っで、プロセスは非同期もしくは同期(単に終了まで待つだけ)で起動できるのだが、MSDNを読むと作成されたパイプはバッファーの上限があるらしく、上限に達した場合読み取ってやらないと書き込みができなくなるという。
あんまりログを吐かないプロセスなら問題ないが、mavenでがりがりログを吐く上にコンパイルされるプロジェクトが20以上あるとなるとバッファーの上限はあっという間に超える。
じゃあ、読み取ってやればいいじゃんという話になるのだが、パイプから何かを読み取ろうとした際にまだ何も書き込まれていない状態だと現状EOFを返すのでループで回してもすぐ終了してしまう。っで多分デッドロックというかバッファーがいい感じに満杯になってうんともすんとも言わなくなる。
こういう場合って多くの処理系はどうやって対応しているのだろうか?
とりあえず、読み込みの方をいじった方がいいだろうか?

2012-02-29

うるう年

すっかり忘れていた。折角なので何か書いておこうというだけの話。

ここ数日(といっても今日で2日目だが)風邪ひいていて結構体がだるい。というか基本起き上がるとくらくらするのでベッドの中でごろごろしている。
早く治らないかなぁ。まぁ、でも明日は仕事にいくだろうけど。

2012-02-26

親の心子知らず

母親とは子供のことでいろいろ何か出来ないか模索せずにはいられない生き物なのだろうとちょっと実体験した話。

いろいろあって関係が壊滅的になっていた(今はほんの少し持ち直したと思う)のが事の始まり。昨日いきなり話し合いの場を設けられた。何故こうなったのかというのが主なもの。こういう話で原因がはっきりしていることってあるのかは知らないが、もし原因があるなら僕だろう。単にいろいろ覚悟が足りなかったという話だ。あんまり優秀な捕手ではないので、飛んでくる球を全部受けきれずという感じだ。(まぁ、直球すら落球していたという話ではあるのだが)
っで、一夜明けて今日、いきなりアントワープに行こうという提案が飛んできた。あぁ、いろいろ考えた結果「2人で何かをする」というのがそこだったんだろうなぁと瞬時に理解。でも、日曜のアントワープなんてただ歩くだけだし、疲れも溜まっていたのでお断りをした。んで、次がどうやらアムステルダムで買い物だったみたい。
正直どういった経緯でアントワープからアムステルダムになったのかは知らないけど、電話があって僕は行かないということを知った際の反応からこれもその一つだったんだなぁと理解。いろいろ申し訳ないなぁとは思いつつ、でも僕買い物に付き合わされても正直ストレスにしかならないんだよなぁとかも思ったり。
いろいろ好意を無にしてるなぁとは思うし、迷惑かけてるなぁとは思うけどう~ん。

甲斐性無しのダメ男ですいません。
(上記は既に謝ってるので、ネット上で言うなよというのはなしの方向で。ブログに書いてるのは単なるはけ口です。こういうとき日本語が分かる友人がオランダでほしいなぁと思う。)

2012-02-25

プロセスをちょっと本格的に実装

以前からとりあえず動く、というか単に他プロセスが呼べるレベル、のものはあったのだがさすがにこのままではなぁと思いちょっと改良(改悪?)してみた。

もともと設計の方針としてはJavaのProcessクラスみたいな感じで入力、出力、エラー出力のポートをプロセスが持っていてそれらをいじれるようにしようとしていた。っが、とりあえず全部標準入出力にリダイレクトして適当にしていた。
っで、ちょっと思い立ってまじめにそれらを割り当てることにした。まぁ上手くいってる。っが、ここでちょっとした問題が。
元々この機能は仕事でmavenを叩くのにWindowsのバッチでは厳しいなぁと思っていたから作ったのだが、今回の変更で標準出力にリダイレクトされないのでログが見辛い。また、走っているプロセスのパイプに対して出力があったのか知る方法がないので、出力ポートからログを上手く取得できないという問題がでた。
解決方法はいまいちいい案がないのだが、とりあえず出力のリダイレクト先を指定できるようにするとか、char-ready?(ポートはバイナリなのでu8-ready?か)的な何かをまじめに実装するか、プロセスが終了するまでじっと待ってからログ吐く(運用回避?)のどれかだろうか?

Rubyのspawnとかどうしてるんだろう?(使ったこと無いけど、いろいろ出来そうだなぁとドキュメント見て思った)

2012-02-23

Boehm GC "too many heap sections" error


Well, the reason why I am writing this article is it's not because I found the solution for that but I have figured out which version I could avoid this. So this is just a memo.

Boehm GC is one of the coolest library for C developer however it still has a lot of problems to use all platform (I guess). When I got this error, I was using Cygwin to develop my own project Sagittarius and trying to run tests. The GC version was 7.2 alpha 6. I actually used version 7.1 before and the reason why I tried to use the latest version was to solve multi thread problem. Well, as you can see it was a wrong decision. Each time I tried to run the tests it failed. So I decided not to use it and revised to stable (they say) version.

If you google this error message, you can probably see the solution which you need to build it with LARGE_CONFIG ld flag. Unfortunately, I haven't tried it because I also saw a lot of article which said it did not solve any problem.

Sorry if you google this error message to solve your problem, I have just written this article for nothing actually:-p

2012-02-20

宗教と家族

またこれ系のネタだ。まぁ、たぶんこれで最後だろうが。

あなたには彼女(もしくは彼氏)がいます。っでその兄妹がオウム真理教の信者であなたの彼女(彼氏)に「この宗教はすごくいいんだ」と勧めています。そんなときあなたはどうしますか?
  1. 止める
  2. 放置する
  3. 自分も改宗する
2もしくは3を選んだ方は続きを読んでもいらだつだけでしょう。

2012-02-17

常識で考えましょう...

Togetterにあったまとめ
@May_Roma流英語習得法:「常識で考えましょう…」
おおむね賛成なんだけど、微妙だなぁと思ったのが1個。(実は最初の15個くらいしか見てないので他にもあるかもしれないが)
https://twitter.com/#!/May_Roma/status/168427967175860225
 日本語力つけたい外人が、日本語でちゃんと喋ったり書いたりできないDQNに日本語習ってもうまくなるわけないでしょう。英語も同じでまともな先生に高い金を払って習わないとうまくなるわけないです。高い先生は自分の勉強に投資してんですよ。常識で考えましょう…
前半は非常に賛成なのだが、後半は実体験からそうでもないなぁというのがあったりする。

とりあえず「日本国内で勉強する」ということに絞るとすれば、当てはまるのかもしれないが、E○Cとかの英会話学校だと「高いけど先生はまともじゃない」なんてことがままある。実際僕も日本で英会話学校にいっていたのだが、あまりたいした成果はなかったような気がする。もちろん0ではないし、その後カナダに行った際に役に立ってはいたので無駄ではなかったとは思うが。この場合はおそらくここで言う「まともな先生」というのがキーワードなんだろう。それを探すのにスキルがいるということだ。

個人的に上記はとりあえず僕が思ったものでも小さいもので、大きくそれは違うだろうと思ったのは、この文章から見て取れた「金を払わないと英語は習得できない」というもの。(国語の点数は低い方だったのでそんなこと言ってないと言われると以下は破綻します)。
僕がある程度(ビジネスでも使える程度)英語が喋れるようになったのは高い金を払って先生から授業を受けたからではない。どちらかといえばそのような環境にいるからだと思っている。つまり英語で仕事をする環境にいるということ。同じことが日本で働いていた時にも言えて、大学を卒業してから身につけた日本語というものが非常にたくさんある。(御社とか弊社とか学生時代は知らんかった)
「習うより慣れろ」という言葉があるように実際に使われている言葉に触れるというのは何者にも代え難い勉強方法ではないだろうか。ただこれを言うと、

勉強なら他所でやれ
この学生の間違いは、会社に入って給料を貰いながらスキルアップしようと思っていること。小学生から大学まで16年学ぶ時間がありました。就職して仕事を するということは、学んだことを生かしてアウトプットするということ。まだスキルが足りないというのなら、その必要なスキルが学べる専門学校にでも行けば いい。会社に入ってスキルアップなんて図々しいことこの上なく、給料は貰うどころか、授業料を払うべきです。会社でのスキルアップというのは仕事の結果と してそうなるわけであり、それ自体を目的にするのは本末転倒。
上記の意見に当てはまる気がするのでそれはそれで微妙かもしれないが。ただ、現場で使われている言葉という点に焦点を絞るならそこにいなければわからないこともあるだろうということだ。
実際、僕もここで働き始めて英語の勉強をせざるを得なかった。具体的には電話の応対とかね。そうはいってもそれに対して金は払ってない(ネット代は含まない)。

効率という点を除けば手段はいくらでもあるはずだ。
「まともな先生」というのが人間以外も指すなら、高い金を除いて賛成ではあるが。

2012-02-12

BNFを書いてみた

Daring Fireball: MarkdownのDingusにあるチートシートからBNFを起こしてみた。無ければ作るのがナンとやら精神で。
BNF of Markdown,

Doc ::= Element*
Element ::= Paragraph | Header | List | Blockquote | Line
         |  CodeBlock | BlockHtml | Reference
Paragraph ::= Inline* Linefeed{2,}
Inline ::= (Text | Emphasis | Link | Image | CoseSpan )
Header ::= Setext-Style | Atx-Style
Setext-Style ::= Text Linefeed ('=' | '-')+
Atx-Style ::= '#'{1,6} Text '#'*
List ::= (Number '.' SP+ Inline+) (Linefeed List)*
      |  ('*' SP+ Inline+) (Linefeed List)*
Blockquote ::= '>' SP* Paragraph
            |  '>' SP* Linefeed (Blockquote)*
Line ::= ( (SP* '-') | (SP* '*') ){3,} Linefeed
CodeBlock ::= SP{4,} Inline+ Linefeed
BlockHtml ::= '<' (div|p|table|pre) '>' (Text | BlockHtml)+ 
              '<' '/' (div|p|table|pre) '>'
Text ::= ANY
Emphasis ::= '*' Text '*' | '**' Text '**'
          |  '_' Text '_' | '__' Text '__'
Link ::= '[' ID ']' '(' URL ('"' Text '"')? ')'
ID ::= Text
Image ::= '!' '[' ID ']' '(' URL ('"' Text '"')? ')'
Reference ::= SP{0,3} '[' ID ']' ':' URL ('"' Text '"')?
実装にある正規表現を追ったわけではないのでかなり微妙かつ、BNFの書き方をあんまり良く知らないので正しくはないかも。ルールに書いてないもので、SPとANYはそれぞれスペースと文字(二つ以上続く改行を除く)。Linefeedは改行で。

> Markdownに詳しい方
間違っている可能性が大いにあるので突っ込み大歓迎ですm(_ _)m

2012-02-11

Markdownがいるのだが

一つ前の投稿に関連するのではあるが、Markdown形式で書かれたテキストをHTMLに変換するスクリプトがいる。本家を使えばいいといえばそうなのだが、それでは面白くないのと、ちょっとした理由でSagittarius上のライブラリとしてほしいわけだ。
っでとりあえずいろいろな実装をみてみたが、どれもこれも正規表現でテキストからHTML一気に(まぁ多少ごにょごにょしているが)変換している。そんな中peg-markdownはCで書かれているがPEGでパーサーを作成している。とりあえず参考になるかもしれないと思い文法ファイルを見てみたが、信じられないぐらい複雑な文法をしていた。
正直あんな複雑な文法をガリガリPackratで記述するよりは手書きでパーサー書いた方が早い気がする。あとPackratは割りと機能が足りてない感じがするのでどうしても冗長に書いてやらないといけなくて結構大変というのもある。これはCSVのパーサ書いて感じた。あんな簡単な文法なのに結構面倒だった。

一番楽なのはshowdown辺りの実装を移植することかなぁと思っている。っがテキスト→HTMLと一括でやっちゃうのは正直面白くなくて、テキスト→AST→HTMLと一枚噛ませたいなぁと思っている。問題はこれだと正規表現とは非常に相性が悪く、自前パーサーが必要になる。とりあえず動くものなら前者の実装でもいいのだが、う~ん。
誰か簡単なBNF定義してくれないかなぁ・・・(他人任せ)

ChickenのPackrat

Markdownのパーサーがいるのでpackratを使ってパーサを書こうとしたのだが、いまいち使い方が分からなかった。なので、練習がてらCSVのパーサを書いてみることにしてみた。
とりあえずいい加減なパーサがこんな感じ。
(import (rnrs)
 (packrat)
 (srfi :14 char-set))

(define *text-set* 
  (ucs-range->char-set! #x2d #x7e #f
 (ucs-range->char-set! #x23 #x2b #f
       (ucs-range->char-set #x20 #x21))))
(define (any results)
  (let loop ((acc '()) (results results))
    (let ((ch (parse-results-token-value results)))
      ;; it's just testing
      (if (and ch (char-set-contains? *text-set* ch))
   (loop (cons ch acc) (parse-results-next results))
   (make-result (list->string (reverse! acc)) results)))))
(define (textdata results)
  (any results))

(define parser
  (packrat-parser 
   (begin 
     (define (crlf results)
       (let ((ch (parse-results-token-value results)))
  (case ch
    ((#\linefeed) (make-result "" results))
    ((#\return)
     (let ((ch (parse-results-token-value (parse-results-next results))))
       (if (char=? #\linefeed ch)
    (make-result "" results)
    (field-entry results))))
    (else (field-entry results)))))

     file)

   (file    ((h <- header '#\linefeed r <- records) (cons h r))
     ((r <- records) r))
   (header  ((n <- names) (cons :header n)))
   (records ((f <- fields '#\linefeed r <- records) (cons (cons :record f) r))
     ((f <- fields) (list (cons :record f))))
   (fields  ((f <- field-entry '#\, fs <- fields) (cons f fs)) ;; (1)
     ((f <- field-entry) (list f)))
   (names   ((n <- field-entry '#\, ns <- fields) (cons n ns))
     ((n <- field-entry) (list n)))
   (field-entry ((e <- textdata) e))))

(define (generator p)
  (let ((ateof #f)
 (pos (top-parse-position "")))
    (lambda ()
      (if ateof
   (values pos #f)
   (let ((x (read-char p)))
     (if (eof-object? x)
  (begin
    (set! ateof #t)
    (values pos #f))
  (let ((old-pos pos))
    (set! pos (update-parse-position pos x))
    (values old-pos (cons x x)))))))))

(define (parse-csv p)
  (let ((result (parser (base-generator->results (generator p)))))
    (if (parse-result-successful? result)
 (parse-result-semantic-value result)
 (apply assertion-violation
        (let ((e (parse-result-error result)))
   (list 'parse-csv
         (parse-error-messages e)
         (parse-position->string (parse-error-position e))
         (parse-error-expected e)))))))
(call-with-input-file "test.csv"
  (lambda (p)
    (parse-csv p))))
#|
入力ファイル
aaa,bbb
ccc,ddd,eee
fff,ggg,hhh
a,b,c
出力
((:header "aaa" "bbb")
 (:record "ccc" "ddd" "eee")
 (:record "fff" "ggg" "hhh")
 (:record "a" "b" "c"))
|#
generatorはドキュメントにあったものをちょっと改変しただけ。多分これが基本なんだろう。
かなり適当で本来はCRLFなのがLFだけだったり、エスケープされたものを認識しなかったりするがまぁ動く。気になったのは「/」の使い方で、こんな感じには使えないという不便さであった。
(entry (((/ (e <- escaped) (ne <- non-escaped))) (if e e ne)))
展開されたコードをみたらまぁ納得なのだが、これがやれないとなると結構面倒だよなぁ。あとこのライブラリは「*」とか「+」とか「?」がないので、それらを書こうと思ったら、(1)のように条件を2つ作る必要がある。

やっぱり正規表現をつかってやるべきだろうか。

2012-02-09

srfi-22

現状部分的に実装しているのだが、もう少しまじめに実装するべきだろうか。単にmain関数を特別視するかどうかというだけだが。
正直実装自体は非常に簡単なので(2~3行足すだけ)やるなら悩むことも無く終わるのだが、どうしよう。毎回(command-line)を呼び出すよりは(main args)と書いて参照できた方が楽だよなぁ。特に使い捨てのスクリプトならなおさら。

やるか。

2012-01-31

CiSEみたいなのがほしい

願望的に書いているが、現在使っているスタブジェネレータがちょっと使い辛くなってきた。いや、別に使い辛くはないのだが、拡張性が皆無だなぁということに気づいたのと、結構他のヘッダーファイルべったりな構成になっているのでSchemeのファイルなおしてCのヘッダー直してというのが面倒になってきた。
っで、GaucheのCiSEを眺めていたらすごく抽象化されていて美しいコードだなぁと。たぶんそのまま移植できるんだろうけど、それやったら面白くない上に結局理解してないから自分で拡張できない。ということでコードの分析。

基本的にほしい機能の部分としては、gauche.cgen.cise、gauche.cgen.stubの2つ。っでこれらが大きく依存しているのが、gauche.cgen.unit(以下ユニット)、gauche.cgen.literal(以下リテラル)。ツール的になのかフックなのかはまだ見てないけどgauche.cgen.typeがstubファイル内での<object>形式の記述をCに変換している。
ユニットではCのプリプロセッサと生のコードを扱う。生のコードがどう書かれるのかは知らない。それぞれを扱うクラスがあって、それぞれをどう出力するかの総称関数がある。
リテラルはSchemeのリテラルをCに変換するモジュール。リストとか文字列の変換。リテラルクラスが上記のユニットを継承して定義される。
CiSE内では基本的な変換部分しか定義されず、その他のモジュールでGauche固有の定義が入る。ただ、そうは言ってもCiSE内で定義されている構文にはScmObjとか入ってくる(let*とかfor-eachとか)
また、CiSEは環境を保持していて、トップレベル、ステートメント、式の3つがある。たとえばdefine-cfnはトップレベルでしか定義できないし、beginは全部いけるんだけどそれぞれ出力されるCのコードが違う。

とりあえずここまで踏まえて、まずどこまでやるかを考える。
  • 特に純粋なCファイルを出力する必要はない
  • 現状のStubファイルとVMインストラクションの手直しはがっつりやってもOK
  • SchemeファイルをCにする必要はない(gauche.cgen.precompモジュール相当はいらない)
として、方針。
  • ノード毎のクラスは取り入れたい
  • 環境+render部分を移植(だめじゃん)
  • 純粋なCiSE部分、Sagittarius固有定義というようにする
とした場合、ライブラリの構成を考えると、こんな感じだろうか?
                   +--------------+       +-------------+
                   |     CiSE     | ----- |     Unit    |
                   +--------------+       +-------------+
                          |                      |
                   +--------------+       +-------------+
                   |    Syntax    |       |   Literal   |
                   +--------------+       +-------------+
                          |                      |
                   +--------------+              |
                   |     Stub     | -------------+
                   +--------------+
CiSEではSagittariusに依存しない構文までサポート。letでは総称関数を作ってデフォルト型の定義を下位のライブラリで行うようにする。デフォルトの振る舞いはエラーでいい気がする。
SyntaxではSagittariusに依存する構文を入れる(for-eachとか)。
Stubは単なるエントリーポイントになるか、それとももう少し何か入れるかは考えてない。

2012-01-26

CLOSの動作チェック

0.3.0に備えてCLOSの動作チェック。組込みでCLOSをサポートするため。でもCL並みに高性能にするべきか悩み中。

とりあえず、Tiny CLOSをベースにしようと考えていたので、サポートしてるmoshで検証。ただ、moshのCLOSってうっかり変なことするとすぐにハングアップするので注意が必要。せめて構文エラーとかそんなメソッドないとか言ってくれればいいのに。
今回はaround、before、after、primaryについてちょっと調べてみた。結論を言うと、完全Aspect指向って感じ。検証コードは以下。
(import (rnrs) (clos core) (clos user))
(define (print . args)
  (for-each display args) (newline))

(define-class <person> () name age)
(define-method initialize ((p <person>) init-args)
  (initialize-direct-slots p <person> init-args))
(define-class <painter> (<person>) pen)
(define-method initialize ((p <painter>) init-args)
  (call-next-method)
  (initialize-direct-slots p <painter> init-args))

(define koch (make <painter> 'name "Koch" 'age 18 'pen "Brush"))
(print (slot-ref koch 'name))
(define-generic paint)

(define-method paint 'arround ((p <person>) colour)
  (print "starting messing up")
  (call-next-method)
  (print "ended messing up"))
(define-method paint 'arround ((p <painter>) colour)
  (print "starting")
  (call-next-method)
  (print "end"))

(define-method paint 'before ((p <person>) colour)
  (print "messing up " colour))
(define-method paint 'before ((p <painter>) colour)
  (print "preparing " colour))
(define-method paint ((p <painter>) colour)
  (print "painting " colour))
(define-method paint 'after ((p <painter>) colour)
  (print "painted " colour))
(define-method paint 'after ((p <person>) colour)
  (print "messed up " colour))
(paint koch 'red)
#|
Output:
Koch
starting
starting messing up
preparing red
messing up red
painting red
messed up red
painted red
ended messing up
end
|#
aroundがarroundになっているのは仕様です。aroundだと動かない・・・そしてmoshが死ぬ。
beforeはprimaryが呼ばれる前で、順番は子から親へ辿る。
afterはprimaryが呼ばれた後で親から子へ辿る。
aroundは見たとおりで、すべての処理の前と後。ただし、call-next-methodを呼ばないと本処理が走らない。また、同じメソッドがあれば、子から親の順で呼ばれる。
aroundはcall-next-methodをコメントアウトしたりすると理解が早い。たとえばのaround内にあるcall-next-methodを消すと、子->親->子で終了する。aroundを指定したら必ずcall-next-methodを呼ばないとえらいことになる。
Java等ですでにAspect指向をやったならあれとほぼ一緒だろう。表面的なことしか見てないけど。

問題は、どうこれを実装するかだな・・・

2012-01-19

Cygwinのdlsym

Google先生に聞いても今わからないなぁ。とりあえず何が起きたかメモ。

SagittariusにCLOSを入れようと組み込みクラスを実装して、拡張ライブラリの方も書き換えたら動的呼び出しが失敗するようになった。理由は分からない。
確認できていることとして、Windows(MSVC)とLinux(Ubuntu、GCC)ではOK。ただ、Windowsでもそうなんだけど、Cygwinではリンカーが死んでるのか腐ってるのか知らないが、普通にコンパイルするとinitializer is not constantとか変なメッセージがでて怒られたのでC++でコンパイルしてる。VCではOKでg++では駄目だということなんだろうか?
それとも、CでコンパイルされたDLLからC++でコンパイルされたDLLを呼び出せないとか?nmコマンドで調べるとシンボルはあるから、extern "C"とかそんなレベルではないはず。(正しくdllexportとか書けてるか自信ないと言えばないが・・・)
こんなリンカーの内側の動作を知らないと駄目というのは結構厳しい。

しかし、もう少しまともなエラーメッセージでないのかねdlsym()も。No errorって、見つからないからNo errorですか?
誰かヒントください。意味不明すぎて涙でそう・・・バイナリアン検定は落ちるなこりゃ。

追記
一晩寝たら原因がわかった。__declspec(dllexport)がCygwinにはついてなかった。ってか、CではOKでC++だとアウトかよ!!たぶん名前マングルのせいだな。

2012-01-17

Version 0.2.4リリース

もう少しいろいろテストしてからでもよかったかもと思いつつ。

Project page (in English)

Sagittariusバージョン0.2.4がリリースされました。このリリースからCommon Lisp風のリーダマクロが使えます。またR7RS(draft 5)をサポートしました。同ドラフトが要求している構文およびライブラリ(1部除く)をサポートしています。一部リーダが読み込むシンボルに違いがありますが、これはSagittariusがGauche風のキーワードをサポートしているためです。

修正された不具合
  • GC周りの不具合が修正されました。これはBoehm GCのライブラリを静的リンクしていたため起きた不具合です。現在ではGCライブラリを動的リンクしています。
  • define-with-keyがGauche風に動くよう改善されました。
  • 正規表現リーダが[[:char-set:]]を正しく読まない不具合が修正されました。
  • equal-hashが停止しない不具合が修正されました。
  • equal?で作成されたハッシュテーブルがバイトベクタを格納できない問題が修正されました
  • list->stringに文字列リストを渡してもエラーにならない不具合が修正されました。
  • マクロ展開周りの不具合が修正されました。詳しくはプロジェクトページのIssue 7を参照してください。
  • 3.1415|10といった仮数を指定している数字が読めるようになりました。ただし仮数は無視されます。
  • quotient、moduloおよびremainderの第二引数に0を渡すとSIGSEGVが発生する問題が修正されました。
  • 正規表現リーダが\0mnn、\xhh、\uhhhhおよび\Uhhhhhhhhが正しく読み込めなかった不具合が修正されました。
新たに追加されたライブラリ
  • (shorten)ライブラリが追加されました。lambdaを^と書けます。
  • (scheme repl)以外のR7RSのライブラリが追加されました。
  • JSONパーサライブラリ(json)が追加されました。Chicken Schemeからの移植です。
  • Packratパーサライブラリ(packrat)が追加されました。Chicken Schemeからの移植です。
新たに追加されたプロシージャ
  • string-splitが(sagittarius regex)ライブラリに追加されました。
  • secure-randomが(crypto)ライブラリに追加されました。
  • dolist、cond-list及びslicesが(util list)に追加されました。Gaucheからの移植です。
  • cond-expandが組込構文になりました。
  • define-libraryが組込構文として追加されました。R7RSのライブラリ構文です。
  • include及びinclude-ciが組込構文として追加されました。R7RSライブラリ構文外でも使用可能です。
  • #!fold-case及び#!no-fold-caseが追加されました。
新たに追加されたドキュメント
  • (crypto)ライブラリがドキュメント化されました。
  • (math)ライブラリがドキュメント化されました。
  • R7RSのサポートに関するドキュメントが追加されました。

2012-01-13

続R7RSモジュール

今回は舞い上がらずに自分の中だけで。
ChatonのGauche部屋に以下の書き込みがあった。(Twitterで見てるので分かった。便利な世の中だ)
okuoku
http://compassoftime.blogspot.com/2012/01/r7rs.html R7RSライブラリ構文が来るのか
nmoshはcond-expandのfeatureとキャッシュの関連が微妙なので保留中。。
実はキャッシュの問題はまだ残っている、というか諦めていて、Sagittariusではそもそもマクロ展開後にマクロが変更されてもキャッシュは更新されない。これぐらいなら何とかなるかもしれないが面倒でやってない。
しかし、include系はまず無理で、includeされたファイルの変更を検知する方法がない。なのでinclude先が変更されてもinclude元が更新されない限りキャッシュは作り変えられず切ない思いをする。この辺キャッシュの考え方が多分moshとは違って、ライブラリとして変更がない、もしくは少ないファイルがキャッシュされるべきという風に考えている。つまり、開発中ならキャッシュをクリアすればいいだけの話で、実際に使用するならライブラリ自体に手を入れないよね?っていう考え。
ただ気になるのはcond-expandのlibrary句で、あれば本家を使ってなければ自前みたいな風に分けられるだろうと考えているのだが、それやって後から本家が追加されたらどうするの?みたいな問題はある。(まぁ、その場合もキャッシュクリアしてくださいと言うだろうが)

どうでもいい話だが、ライブラリ構文に関してはR7RSの方が優れている気がする。cond-expandが標準で入ったのもそうだが、以下のように書けるのがでかいと思う。
(define-library (a library)
   (import (scheme base))
   (export a-procedure)
   ;; importが複数回でてもOK
   (cond-expand
     ((library (srfi :1))
      (import (srfi :1)))
     (else
       (define (acons a b c) (cons (cons a b) c))))
   (begin
     (define (a-procedure) ...) ;; something nice
   ))
R6RSではimport句はライブラリ中ただの1回しか出現してはいけないし、トップレベルでも厳格にやるとcond-expandで切り分けることもできない。(おかげでテスト用のプログラム書くのに苦労する。毎回コメントアウトしないといけない)
define-libraryを使った方がポータルに書きやすいかもしれない。未だにChibi Scheme以外でサポートしている処理系を知らないが・・・
ポータビリティを大分捨てているのに何を今更という感はあるが、テスト用書き捨てプログラムを書くのに便利になるのはいいことだ。

2012-01-12

R7RSモジュール

次のリリースではドラフト5(たぶん確定だと信じる)のR7RSモジュールシステムが入る。まだ、include-ciがダミーな実装だがそれ以外はだいぶよさげ。
以前exportやincludeが(scheme base)からエクスポートされていないと書いたが、まじめに実装して「されていなくてもいいのか」とちょっと納得した。(includeに関しては他で使えてもいいかなぁと思ったのでexportされていてもいいんじゃとは思うが。これってR5RSで書かれたものの再利用が簡単にできるための措置なんだろうか?)

R7RSで定義されているライブラリの内、(scheme repl)は次のリリースでははずすことにした。というかinteractive-environmentが必要になる場面が想像できないのと、(scheme base)等のライブラリがimportされた環境をREPLに用意するのが大変なため。
R7RSのモジュールはコアなライブラリではなく、現状sitelib扱いにしている。libとsitelibで何が違うかといわれると大分混ざってしまった感があるので答えづらい。

構文も(たぶん)網羅されてるはず。(#u8とか)。ベクターはもともとself evaluateだったので問題なし。微妙な問題だが、#!r7rs(#!compatibleと現状は一緒、つまりデフォルト)をつけると`#`がnon-termな文字になる。つまりabc#vu8(1 2 3)というのはabc#vu8というシンボルと(1 2 3)というリストに分かれる。#!r6rsをつけるとabcと#vu8(1 2 3)になる。0.2.4から入るリーダマクロのおかげでかなり簡単に実装できている。

chibi-schemeに次いで2番目と言えるかはわからないが、割と早めにR7RSのサポートをしていると思う。(ライブラリはmoshのr7rs-bridgeから大分もらったが)

2012-01-08

人間失格

太宰治ではない。(読んだことないなぁ、そういえば)

個人的な人格の欠陥の話である。よくも悪くも僕は淡白だと思う。というよりは人に対して一定以上の興味がわかないというべきか。正直いろいろなことを生活習慣にしていかないと傍から見た際にまったく興味がないと思われるらしい。というか既に思われた。
よくよく考えてみると自分のネコ好きというのも、その昔に自分で作った習慣のような気がする。昔から家にはネコがいるのが当たり前だったのでそれが簡単だったのだろう。
別段自分が無味乾燥な人間ではないと思うのだが、どちらかと言えば引きこもり系な性格ではあると思う。嫌なこと、楽しいこと、すべて自分の中だけで完結して表に出さない。内側で驚いていても、表面には出さないので、僕は滅多なことでは驚かない人間、と思われている。(まぁ、お化け屋敷程度では驚かないが)

3つ子の魂100までなんて言われるように、これを今から変えるのは難しいだろう。変えようとも思っていない。元々自分の理解者は自分だけでいいと思っている上に、人からの理解を期待していないというのもあるだろう。経験的に期待しても裏切られるというのが刷り込まれているからかもしれない。

何が言いたいというわけではないが、振られてそんなことを思った次第。
オランダに居る理由がなくなった日の夕方。

2012-01-07

結局

Gaucheがあの方式を取れるのはMakefileを自前で書いてる(automakeではない)からであって、CMake使ってると不可能な気がしてきた。

しょうがないのでREADMEに注意書き書いてdllでリンクするようにビルドした方がいい気がする。VCでコンパイルした際にどうしようかなぁというのと(gcmt-dllとリンクできなかった。何でだろう?)、オートダウンロード機能をつかってCygwinでコンパイルした際に問題になるけど。(GCのオートダウンロードはVCだけにすればいいだろうか?でも微妙だよなぁ・・・)
そもそもGCを静的リンクにしようとした理由ってなんだっけ?

2012-01-06

足元固め(バグつぶし)

ようやく不可解なメモリ関連の原因が特定できそうな気がしてきた。
現状、同じCygwinで起きる場合と起きない場合があった。今までどちらも同じ環境でビルドしていると思っていたが実は違った。片方はGCのDLLを使っていて、もう片方は静的リンクしてた。DLLの方はメモリを壊さないけど、静的リンクは壊していた。
今までメインのDLLにGCが入っていればモジュールとして呼ばれるDLLはリンクしなくてもいいと思っていたがそうでもないのだろうか?

ちょっと調査しよう。

sxpathメモ

SXPathで名前空間付のSXMLにクエリーを発行するのがいまいち分からなかったのでメモ。
正直これが正しいのか良く分かっていない。
(import (text sxml ssax) (text sxml sxpath) (pp))
(define *xml* "<?xml version=\"1.0\" ?><root xmlns:h=\"http://localhost/\"><h:p>hello</h:p></root>")
(define *namespace* '((h . "http://localhost/")))
(let ((sxml (ssax:xml->sxml (open-string-input-port *xml*) *namespace*))
      (sxml2 (ssax:xml->sxml (open-string-input-port *xml*) '())))
  (pp sxml)
  (pp sxml2)
  (let ((doc ((sxpath '(root h:p)) sxml))
       (doc2 ((sxpath '(root h:p) *namespace*) sxml2)))
    (pp doc)
    (pp doc2)))
#|
(*TOP* (@ (*NAMESPACES* (h "http://localhost/")))
       (*PI* xml "version=\"1.0\" ")
       (root (h:p "hello")))
(*TOP* (*PI* xml "version=\"1.0\" ")
       (root (http://localhost/:p "hello")))
((h:p "hello"))
()
|#
パース時に名前空間を与えてやるとタグをプレフィックス付でパースする。与えないとそのまま長い名前で出力する。
プレフィックス付のSXMLをsxpathに渡すなら、名前空間は要らない(あってもよい)。
プレフィックスなしをsxpathに渡すなら長い名前で指定する必要がある。名前空間渡しても識別しない。
ここに使い方があった。
Re: SXPath namespaces - msg#00006
どうやらsxpathの第二引数は微妙な動作をするくさい。上記の例だと
(pp ((sxpath "//root/h:p" *namespace*) sxml2))
#|
((http://localhost/:p "hello"))
|#
これで通る。S式なクエリでは認識しないが文字列ならOK。いまいちssaxとの相性が悪いというか、完全別モジュールという感じだ。

2012-01-04

マクロ戦争再び

分かってはいたことだがsyntax-caseがバギーである。
笑ってしまったのは、明らかに意図していない動作をするコードであることでR6RSテストスイートがパスできること。バグの上に成り立つコードなんてイヤだ。

ということで何が問題かを洗い出し、対策を考えることにする。
問題とするコードはとりあえず以下のものだけに絞る。
(define-syntax loop
  (lambda (x)
    (syntax-case x ()
      ((k e ...)
       (with-syntax ((break (datum->syntax #'k 'break))
         #'(call-with-current-continuation
             (lambda (break)
               (let f () e ... (f)))))))))
(let ((n 3) (ls '()))
  (loop
   (if (= n 0) (break ls))
   (set! ls (cons 'a ls))
   (set! n (- n 1))))
これはR6RSの仕様書にもあるキーワードを追加しないloopマクロの実装。現状ではbreakは存在しないといわれて怒られる。(でもテストケースでは通る)
loopは以下のように展開されることがコードから読み取れる。
(let ((n 3) (ls '()))
  (call-with-current-continuation
   (lambda (break)  ;; *1
     (let f ()
       (if (= n 0) (break ls)) ;; *2
       (set! ls (cons 'a ls))
       (set! n (- n 1))
       (f)))))
ここで問題になるのは、*1のbreakと*2のbreakが同じ識別子にならないこと。現状の実装ではマクロ内にあるパターン変数と実際に展開されるコード内にあるシンボル(識別子)が同じと考えられそうなら同じものを使うということをやっている。
正直それが正しいかどうかは分からないが、マクロ展開器は(特にこのパターンでは)どの識別子がローカルに束縛されているのか知ることが出来ない。これがネックになって上記のようなことを行っている。
まぁ、それはおおむね上手くいっている感じなのでそれを突き詰めるようにしていこうと思う。
上記の例だけ見れば、パターン変数としてのbreakはマクロ作成時に取得せざるを得ない。このとき取得したパターン変数含む環境をマクロ環境とする。マクロ展開時にはこのマクロ環境からbreakパターン変数を探すことになる。
現状で何が問題か?なぜか見つかったパターン変数が識別子ではなくシンボルになっていた。実際テンプレートないでbreakが見つかると、with-syntaxで作ったbreakがパターン変数として認識されるため置き換えが発生する。その際になぜか置き換えられたパターン変数が識別子ではなくシンボルであった。
考えられそうなこととして、with-syntaxは以下のように展開される。
(syntax-case x ()
  ((k e ...)
   (syntax-case (list (datum->syntax #'k 'break)) ()
     ((break)
      (let ()
        #'(call-with-current-continuation
            (lambda (break)
              (let f () e ... (f)))))))))
この際にパターンに与えられるS式からシンタックス情報が剥ぎ取られている可能性がある。

この問題の原因が分かった。datum->syntaxがシンボルを識別子にしていないんだ。なんらかの理由があって識別子のライブラリとマクロ環境(もしくは現在の環境)が保持しているライブラリが一緒だった場合ラップしないようにしてたんだ。なんでだったかな?

(続)R7RSドラフト5

ここから大きく変更はないだろうと仮定してdefine-libraryの実装に踏み切ろうかと思う。
定義を見ると、export、import、begin、include、include-ci、cond-expandがlibrary declarationとして定義されていて、それらが中に何度出てきてもよさそうな感じである(一回だけとか書いてないからそう読む)。
問題になりそうなのはexport、import、とcond-expandあたりだろう。
exportが2回以上出てきた際の扱いが書いてない。 両方をexportするのか、エラーではじくのか、処理系依存になるのだろうか?
importが複数回でた場合も特に記載がない。例えば以下のケース。
(define-library (import test)
  (import (scheme))
  (begin
    (define test 'a)
    ;; lazyは(scheme lazy)で提供されるが、
    ;; この場合はどう解決する?
    (define llist (lazy (cons 'a 'b))))
  (import (scheme lazy))
)
importの順番は関係ないのか、それとも上から順なのか。最終的な呼び出しは、Sagittariusの構造上importの順番は関係なのだが、マクロ展開についてはimportされた後しかマクロはつかえない。この場合上から順に解決すると、lazyはマクロではなく単なるプロシージャとして扱われる。どっちがR7RS的には正しいのだろうか?
cond-expandは現状では解決するのに外部ファイルを読み込む必要がでてくるので、ビルトインにする必要がある。ただ、libraryフィーチャが追加されているのでこれをどうするか。意図としては以下のようなものだろう。
(define-library (cond-expand test)
   (cond-expand
     ((library (srfi s1)) (import (srfi s1)))
     (else ;; 必要そうなリスト操作の自前実装
      )
)
あれば便利なんだろうけど、キャッシュとの兼ね合いが非常に悪くなりそう。R6RSで不便だった部分が解消されるイメージではあるが。
単純にR6RSのlibrary構文のエイリアスでは無理そうなので別途ビルトインシンタックスを作る必要がある。
素人目に見て結構微妙な構文に見えるがどうなんだろう?

2012-01-02

R7RSドラフト5

ちょっとまじめに眺めている。
その際に見つけた気になる部分のメモ。

2.1 Identifiersより
All implementations of Scheme must support the following
extended identifier characters:
! $ % & * + - . / : < = > ? @ ^ _ ~
「:」は識別しじゃないとだめらしい。キーワードはだめってこと? (拡張という扱いにしよう、そうしよう)

4.2.1 Conditionalsより
caseはcond同様=>をサポートしないといけないらしい。

4.2.5 Delayed evaluationより
eagerって何だ?
The eager procedure returns a promise which when forced
will return obj . It is similar to delay, but does not delay
its argument: it is a procedure rather than syntax.
procedureが望ましいってあるだけで、syntaxでもいいのね。あいまいじゃね?

4.3.3. Signalling errors in macro transformersより
(syntax-error <message> <args> ... ) syntax
procedureじゃねぇ!!

5.2.3. Multiple-value de finitionsより
define-valuesが追加されている。

5.5.1. Library Syntaxより
cond-expandが<library declaration>に入ってる。どうしたものかね。

6.11. Exceptionsより
error-object系のプロシージャをどうしようか。R6RSのコンディションでいけるか?

6.13.4. System interfaceより
loadがenvironmentを引数に取るんだけど、えっ?って感じなんだが。う~ん。
evalのファイル版みたいなもの?

リテラルなリストの扱いが面倒だなぁ。

謹賀新年

Happy new year!!
Beste wensen, gelukkige nieuwjaar!!
本年もよろしくお願いいたします。

元旦はわりと忙しくてかけなかった。

今年の抱負(というか目標)
  • 週2くらいでジムに行く
  • オランダ語を話せるようになる
  • ギターを毎日少しでもいいので練習する
書いておいて年末にできたかどうか確認しよう。
去年は個人プロジェクトを発足するということを書いたみたいだ、目標達成ではあるか?

個人的な近況報告できるほど変化があったわけではないのが寂しい。

以下はSagittariusな話題。


2011-12-29

括弧ゴルフ

折角リーダーマクロを作ったし括弧ゴルフでもしてみるかとやってみた。以下のサイトのルールで。
本当にLispはカッコが多い?
最後にあるLispのリーダーマクロを参考にGaucheのfoldを使ったケースを実装してみた。
#! /usr/local/bin/sash
(set-macro-character #\! (lambda (p c) (read-delimited-list #\$ p)))
! import ! srfi :1 $ $
! fold ! lambda ! x y $ ! print ! - x 1 $ "!= " y $ ! * x y $ $ 1
  ! iota ! string->number ! cadr ! command-line $ $ $ 2 $ $
まぁ、ある意味当たり前だが、括弧8個でいける。
set-macro-characterは本来は(sagittarius reader)をインポートして使うべきだが気にしない。
しかし、これ見てSchemeと思う人はおらんだろうなぁ。

リーダーマクロが動いた

まだ、あまりテストしていないが、簡単なリーダーマクロが動いた。
とりあえず当初の目的の通り正規表現を#/regex/と書ける様にしてみた。こんな感じ。
#<(sagittarius regex)
(import (sagittarius regex))
(define rx #/\w+/)
(print (regex-replace-all rx "abcdef@abcdef" "**$0**"))
もちろん#/regex/ixumsのようにも書ける。
汚いなぁと思うのは#<(ライブラリ)のように書かないとリーダーマクロがインポートされない部分。上記のようなプログラムなら(import)がやっても一緒なのだが、ライブラリが問題になってくるので、こんな風にした。
また、loadしたファイルの影響を受けると意味が分からなくなるので影響範囲はファイル単位になっている。

括弧ゴルフに勝つるツールが手に入った(違

2011-12-23

Linuxでコンパイルさせるためのメモ

tarボールでコンパイルさせるテストの一環。
そのうちソースに反映させるが、忘れないようにメモしておこう。

xubuntu 10.xx(細かいバージョン忘れた)ではapt-getでインストールできるcmakeのバージョンが2.8.3だったので、CMakeLists.txtの先頭行にあるバージョンを2.8.3にしてテスト。

手直しが必要だったファイル:
  • library.c
  • core.c 
  • transcoder.c
  • system.c
  • load.c
  • test-lib.c
  • src/CMakeLists.txt
  • ext/ffi/CMakeLists.txt
library.cとsystem.cはミューテックスの初期化問題。まじめにInit関数でやりましょう。特にlibrary.cはInit関数がないので作成して、core.cで呼ぶ必要がある。
system.cは<io.h>がないって言われたのと、environがないって言われた。<io.h>はcmakeを走らせる際にチェックを追加する。environはどうしよう?見つからない理由が分からない。とりあえず通した際にはextern char **environをつけた。
transcoder.cはcodec内で使ってる名前で怒られた。その名もputcとgetc。こいつらマクロなんだ。知らなかった。あればundefするように変更。どうせ中では使ってないし。ってか、こんな極悪なマクロ作るなよ・・・mix、max並みにひでぇ・・・
test-lib.cはuintptr_tがないと怒られた。&t;stdint.h>をインクルードして解決。
src/CMakeLists.txtはpthreadとdlをターゲットリンクに追加。
ext/ffi/CMakeLists.txtはapt-getでlibffiをインストールしたので、ターゲット名が違った。この辺Cygwin版と比較する必要があるなぁ。

以上をなおしたらコンパイルが通った。意外と自動ダウンロード機能も働いていて、BoehmGCは勝手にダウンロードしてコンパイルしていた。(zlibは既にいたのでそのまま使用していたが)
ユニットテストはなんと肝心なrun-test.scmがtarボールに入っていなかった。とりあえずリポジトリからファイルをダウンロードして走らせる(汗。自宅のxubuntuは非常に非力なマシンで動いているので初回テストはガリガリと音を立てていたが(メモリが少ないのよ)、オールクリア。キャッシュテスト用に再度走らせても問題なかった。大枠の出来は割といい感じみたいだ。
ドキュメントの生成も(テストが通ったので当たり前だが)動いている。
まっさらな状態で一応全部いけるということが分かったのは大きい。

それにしても、非力なマシンにもかかわらずコンパイル時間がCygwinでやるより早かった。VCでnmakeを使ったときにも感じていたが、GCCが遅いのだとばかり思っていた。

バージョン0.2.3リリース

今までextディレクトリをtarボールに入れ忘れていたということに気づいて、今回からはそれらが入ってきます。(ということは今までのtarボールってコンパイルすらできなかったということだろうか・・・試してないからなぁ。今後は試すようにしよう。)

今回のリリースは後の拡張に備えたメンテナンスリリースです。

修正された不具合:
  • ライブラリインラインでマーキングミスがあったのが修正されました
  • bitwise-first-set-bitにbignumを与えた際、不正な値を返すことがあった不具合が修正されました
  • renameエクスポートが正しく動作していない不具合が修正されました
改善された動作
  • 文字セットが組み込みになりました
  • gcdにbignumを与えた際のパフォーマンスが改善されました。メモリの使用量が大幅に減っているはずです
  • 正規表現ライブラリが新たに書き直されました。単純な正規表現でかつ巨大なテキストに対してのマッチングもしくは文字列置換であれば以前のものより高速に動作することが期待できます
新たに追加されたライブラリ
  • パフォーマンス測定用ライブラリ(time)が追加されました。現在のところtimeマクロのみエクスポートされています
Windowsバイナリでテストケースを走らせると失敗するテストが2つあります。base64ライブラリのテストとmimeライブラリのテストです。どちらのライブラリもドキュメント化されていないのですが、使用する際は注意してください。

次はリーダーマクロだ!ほぼ正規表現のためだけに入れるといっても過言ではないが、まぁ気にしない。

根本的な解決ではないが

RSA鍵の作成時に素数チェックにgcdを使用していて、Bignumのgcdが非常にナイーブな実装だったため大量のメモリを使用していた。そこで、binary GCDを実装して、メモリの割り当てが最大で4回になるように実装しなおした。そしたら、前述の不具合はとりあえずなりを潜めたのであった。
(あんまり高速化にはなっていないっぽい。512ビットの鍵ペアを作るのに2秒かかる[Core2Duo 3GHz]。Javaでは109ミリセコンドという、20分の1の時間で生成された。う~む)

Windows版のSagittariusでもそれなりにいけるようになってきたような気はするが、テストを走らせると失敗するケースが1つ増えた。正規表現ライブラリを置き換えたからかもしれない。かなりテストを書いたがまだカバーしきれてないらしい。でもテスト外で同じ正規表現、同じ文字列でマッチさせるときっちりマッチするんだよなぁ。メモリ系だろうか、また・・・。Windows版だけというのがまた痛い。メイン環境じゃないからデバッグがしづらい・・・

とりあえず、「よきに計らえ」と勝手に言い聞かせて0.2.3をリリースしてしまおう。

2011-12-22

バグの原因究明

Sagittariusにはドキュメント化されていないライブラリが結構ある。理由はさまざまで、APIが固定されていないとか、近い将来変更になるのでその後とか、バグが取れてないとか、まぁいろいろだ。
その中の「バグが取れていない」の代表格暗号化ライブラリでどこに不具合があるのかがようやく分かった。
結論を言えば、メモリ割り当ての際にオーバーラップして割り当てているせいで、メモリ破壊を起こしているというものだ。
原因はおそらく2つに分けられるだろうが、1つはBoehmGCがマークミスを起こしている。これは回避しようがないので、あきらめるしかない。もう一つは何らかの理由により、割り当てたメモリが使われていないという状態になり、GCによって回収されている(一緒くさいな・・・)。とりあえず前者っぽい挙動をしている。なぜだ?

今回はどうやって直すかというのではなく、どうやって探したかをメモしておく。場所を特定するのに非常に面倒なバグだったので。

起きた環境:
  • VCでコンパイルしたバイナリ
  • 自宅ノートPCのCygwinでコンパイルしたバイナリ
職場のPCでは起きないという隠密バグ(職場もCygwin)。BoehmGCのコンパイル時フラグかな?
使用したツール:
  • WinDbg
  • GDB
NMakeを使っているので、VC++ Expressではデバッグできなかった(評価版なのでプロセスアタッチできない)。
デバッグ方法:
WinDbgでウォッチ式を指定。地道に起きるまでステップ実行(涙)。WinDbgではウォッチ式をしていしても、人の目で監視しないとだめだった。面倒すぎ。
ある程度特定したのちに、自宅PCのGDBでデバッグ。何が壊れているのか分かっているので、ブレークポイントでその値が設定される場所で停めて、アドレスを確認。以下のコマンドを打つ。

watch *0x{上記で調べたアドレス}
最初、丁寧にキャストして実行したらGDBが終わらなかった。キャストせずに上記のように打つとハードウェアウォッチポイントになるらしく、実行がすごく速かった。っで、たどり着いた先はBoehmGCのmalloc。。。どうしろと?
さて、解決方法でも考えるか・・・

2011-12-20

ベンチマークとって見た

新正規表現ライブラリのテストを書いているのだが、どの程度速く(もしくは遅く)なったのか知りたかったので、比較してみた。
コードは以下
(add-dynamic-load-path "./build")
(add-load-path "./sitelib")
(load-dynamic-library "sagittarius--regex2")
(load-dynamic-library "sagittarius--regex")
(import (rnrs)
 (time)
 (srfi :13) (srfi :1))

(define bench
  '(begin
     (define-syntax bench-regex
       (syntax-rules ()
  ((_)
   (begin
     (define rx
       (compile-regex "[-_.0-9A-Za-z]+@[-_0-9A-Za-z]+[-_.0-9A-Za-z]+" 0))
     (define (repeat s c) (string-concatenate (make-list c s)))
     (define s (repeat "abcdefgh" 500))
     (define m (regex-matcher rx s))
     (define times 20)
     (time (do ((i 0 (+ i 1))
         (r (regex-replace-all m " ")
     (regex-replace-all m " ")))
        ((= i times) r)))))))
     (bench-regex)))

(let ((args (command-line)))
  (if (= (length args) 2)
      (cond ((string=? (cadr args) "old")
      (eval bench
     (environment '(sagittarius regex impl) 'user)))
     ((string=? (cadr args) "new")
      (eval bench
     (environment '(sagittarius regex2 impl) 'user)))
     (else 
      (error 'command-line "unknown option")))
      (print "usage: test2.scm old|new")))
で、結果。
$ time ./build/sash -D./build test2.scm old

;;  9.964000 real    9.890000 user    0.000000 sys
./build/sash -D./build test2.scm old  10.06s user 0.03s system 99% cpu 10.176 total
$ time ./build/sash -D./build test2.scm new

;;  0.013000 real    0.015000 user    0.000000 sys
./build/sash -D./build test2.scm new  0.19s user 0.00s system 96% cpu 0.193 total
Pikeさんパネェっす。
別にこのパターンに特化しているわけではないのだが、Perl拡張正規表現が入ってくると別の話になる。それは一つ前のエントリで書いた通り、VMが遅いけど多機能モードに移行するので以前のものより遅くなる可能性が高い。(というか、多分遅くなる)
ただ、個人的に(後方参照はかなり便利だからちょっと考え物だが)先読み、後読み系やpossessiveマッチはあまり使わないので気にしていない。便利なんだろうけど、そんな込み入った正規表現書かないという理由で。

テストケース整備して、APIを以前のものと同じにしたら0.2.3をリリースしよう。

2011-12-19

ハイブリッドVM(正規表現)

正規表現を書き書き直している最中、RE1とRE2を参考にしたPikeVMでは肯定先読み系はおろかpossessiveマッチさえも実装が困難だということに気づいた。
そこでとりあえず普通に再帰を使って書いてみたら恐ろしく遅かった。
どうしたらいいかなぁと電車の中で考えていたら、「ハイブリッドにしちまえよ」という悪魔のささやきが聞こえたのでやってみた。

どうしたか?
拡張正規表現(ここでは上記の2つ+バックリファレンスを指します)を除いた単純な正規表現のみで記述されたパターンはPikeVMで動かし、えらいごてごてした正規表現は再帰を使ったVMで動かしてみた。
これやるとおそらくpossessiveマッチで書いた方が遅いという不思議現象が起きるがそれは置いておいて、後読み(と後方参照もかな?)を除いた210個の単体テストが通った。遅いVMだと異常に実装が楽だったが、涙が出るほど遅く、PikeVMでは速いけどバックトラック系の処理が書けないというジレンマをまぁ使えそうかなぁと思える程度には解消した気がする。
(それでもJavaから移植したコードの方がまだ多少速い・・・まぁ最適化ほとんどしてないからある意味当たり前なんだけど)

後は後読みと細かいフラグの部分を実装して、APIを実装したら一応完成か。クリスマスまでに間に合うだろうか?

2011-12-16

ふと、テレビを見ていて思ったこと

標題に似合わず英語関連です。

文自体は忘れたが、その中にあった単語「labour」を見てふと思ったこと。
そういえば、上記の単語は自然な感じだが、「labor」と書かれると不自然な感じがする。いや、単に英語と米語の違い名だけで意味は一緒なのだが。っでなんとなく自然に感じるつづりと不自然感じがするつづりを並べてみた。
ちょっとしたつづりの比較
英語米語自然だと感じる方
colourcolorcolor
labourlaborlabour
behaviourbehavior両方OK
favourfavor両方OK
favouritefavoritefavourite
centrecentercenter
theatretheater両方OK
とりあえず思いついたのがこんな感じだった。不思議と「ou」になるのが自然と感じるみたいだが、さすがに「color」はこっちのが自然だ。「center」もまぁどちらかと言えばという感じだが、職業柄どうしてもこっちの綴りになる感じ。そのため、「theatre」はこっちの方が自然。(というか街中で見るのはこっちか、bioscoopeだし)
labourは多分カナダにいたときに見た「labour day」が印象に残っているのだろう。
あと何があるだろう?「z」と「s」かな「analyse」とか。うっかり「s」で書いて、Google先生に尋ねたりしてる。
ヨーロッパなのでCMとか英語よりになるんだろう。実際そうだと感じるし。

どうでも話だった。
そういえば、中学、高校の英語のテストで「centre」って書いたら○もらえるのかね?「favourite」はいけると思うけど、どうなんだろう?

2011-12-15

正規表現実装中

ぶっちゃけ行き詰ったのでちょっと問題点の洗い出し。

出来てそうなこと
  • greedyマッチ
  • non-greedyマッチ
  • スタートとエンドアンカー
  • 単語境界
  • グループ(キャプチャリングとそうじゃないの含む)
  • 文字クラス
出来がやばそうなの
  • 肯定、否定先読み及び後読み
  • possessiveマッチ
やばそうなのもある程度は動いてるんだけど、パターンによっては簡単に無限ループに入る。
例えばこんなパターン
\D(?!123)
っで、"ABC123"を与えた場合、文字「C」にマッチしなければいけないが、マッチしない。また、パターンが
\D*(?!123)
になると無限ループする。(ほとんど出来て無いじゃん!)
原因は幅0を実装しているところにあって、2つ目のパターンだと、文字「A」は「\D」と否定先読みの両方を見る。っで、否定先読み(肯定でも)の条件にマッチすると、次の文字に行かずその場にとどまる(幅0の実装)。そうすると、結局文字列"ABCD123"の先頭から同じパターンを繰り返すので無限ループ突入となる。
っと、ここまで書いてちょっと解決案っぽいのが思いついた。要するに先にマッチしたものがあった場合にアサーションに突入しなければいいのではないだろうか?ちょっと試してみよう。

2011-12-12

ジョークプログラム(sleep sort)

sleep sortなるものを実装してみた。
元ネタはここGenius sorting algorithm: Sleep sort
消えてると寂しいので一応引用。
Genius sorting algorithm: Sleep sort
1 Name: Anonymous : 2011-01-20 12:22
    Man, am I a genius. Check out this sorting algorithm I just invented

    #!/bin/bash
    function f() {
        sleep "$1"
        echo "$1"
    }
    while [ -n "$1" ]
    do
        f "$1" &
        shift
    done
    wait

    example usage:
    ./sleepsort.bash 5 3 6 3 6 3 1 4 7
2 Name: Anonymous : 2011-01-20 12:27
    >>1
    Oh god, it works.

    But I don't like to wait 218382 seconds to sort '(0 218382)
3 Name: Anonymous : 2011-01-20 12:31
    >>2
    yes the worst case is very big
まぁ、要素ごとにスレッドを止めて終わった順に値を表示するだけ。
Sagittariusで書いてみた。append!が破壊的かつSRFI-18をサポートしてる処理系なら何でもいけるはず・・・
(library (sleep-sort)
    (export sleep-sort)
    (import (rnrs) 
            (srfi :1)
            (srfi :18))
  (define (sleep-sort lst)
    (let* ((new (list '()))
           (threads (map (lambda (e)
                           (unless (integer? e)
                             (assertion-violation 'sleep-sort
                                                  "integer required but got" e))
                           (make-thread (lambda ()
                                          (thread-sleep! e)
                                          (append! new (list e))
                                          e)))
                         lst))
           (r (map thread-start! threads)))
      (map thread-join! r)
      (cdr new))))
こんな感じで使う
(import (sleep-sort))
(print (sleep-sort '(5 4 6 2 8 9)))
;; => (2 4 5 6 8 9)
上記のコメントにもある通り、最悪時間(というか計算時間?)は要素内の最大値になるので、馬鹿でかい数値だと10分くらい返ってこないとか普通にありえるので要注意。(ソートが終わらないから仕事ができないという言い訳にはなるけどw)

追記:
びっくりするくらい2番煎じだった件・・・ scheme(gauche)でもsleep-sort

英語に対する感覚

前に英語で書いた記事にコメントが付いていた(スパム扱いされていたので気づくのに遅れた)。
これはそのコメントを読んでちょっと気になったことについて。

前置きとして、批判的な意味合いはまったく無く、コメントやご指摘がいただけるのは非常にうれしいことだと考えています。もしここから以下を読んで不快に思われたらごめんなさい。ということで一応分けてみる。やたら長くなったし。


2011-12-11

Inside of Sagittarius: Library system

I am not sure whether somebody is interested in this topic or not. However if there is someone who is hesitating to use Sagittarius because he/she does not know about inside. (Well, I just wanted to write something about Sagittarius in English, sorry)

The reason why I am writing this article is Sagittarius has a little bit different library system comparing with Ypsilon and mosh. (Sorry, I don't know about other implementation) Those two implementations does not have real library system, as far as I understood it has more like just alpha-conversion. So you can not operate or explicitly call any library with it. On Sagittarius, however, libraries are also a first class object. So you can actually do something with it. (I have no intention to make its APIs public, though). Because of this, Sagittarius has sort of namespace. (The same symbol can be bind to different values in different libraries).

What is the good things of it? Unfortunately, I can only say one good thing with it which you can re-define existing value somewhere in the library somebody wrote. So you can even patch the libraries which is written in C. Well, this is actually not so secure, however it is very convenient for debugging. (I used this behaviour to debug macro expansion.)

How is it implemented? It is actually really simple. The VM has library table which is created in Scheme or C and hold it to make sure no duplicated library exists. And library itself has a hashtable and some meta information. The hashtable's key is symbol and value is gloc. Gloc is sort of handle for bindings.

What is the *bad* thing of it? Well, I don't like to write this answer but every implementation have good/bad things and user must know it. Because of this library behaviour, Sagittarius can not have explicit macro expansion phase, so all for import keyword will be ignored. And because of this, (could be because of my insufficient skill), Sagittarius' syntax-case can not compromise a few R6RS requirements.

Sagittarius is still under developing however it has already a lot of useful libraries (maybe not documented yet...). If you like the idea and want to try R6RS implementation, why don't you try it?

2011-12-08

コードをいじっていない

一応、1週間の休暇中でコードを触らないと決めたので触っていないのだが、アイデアは出そうなので取り留めないことをメモしておこう。

正規表現書き直しについて
マッチが既存のものより3倍程度遅いが、これはひょっとしたらマッチさせるたびにメモリの割り当てが3回発生してるからかもしれない。なので、現状マッチさせる際に作成しているコンテキストをマッチャー作成時に初期化して再利用したら高速化されるかもしれない。これでなお遅かったらなんだろう?
Possesiveマッチとか後方参照とかどうしよう?RE2はサポートしてないんだよね。パーサーはCL-PPCREを参考にしたので、その辺対応してるのに、VMは対応(まだ、だといいな)してない。考えないと。

リーダーマクロについて
これは実装手順になっていくのかな?現状ではまぁ普通にスイッチ文で一文字ずつ見ている。次のリリースもしくはその次位には細かく分けられたリーダーを用意して都度プロシージャーを呼び出すようにする。ただし、Cで実装されたものを単純にapplyすると遅そうなので、それは直接呼び出すようにする(引数さえ調節可能、ってかVMはそうしてるし)。その後、リーダーマクロ用のテーブルにマクロのセットを足してディスパッチするようにしてやる。ユーザー定義のリーダーマクロはそこができてから実装するようにする。多分、文字の登録もしくはディスパッチャ(2文字)の登録になるはず。CLの実装も確認しておきたい。xyzzyのソースを読む必要があるか?
(まずはCLのリーダーマクロをいじってみろという話もあるが・・・)

CLOSについて
これは0.3.0くらいに入れたい機能だが、実装方針をとりあえず立てておきたい。ソースが結構大きくなっているので、少しずつ置き換えるというのが難しいかもしれないが、最初は即値クラスから実装(fixnum、文字、boolean、etc)。一気に置き換えるなら現状のタグを消してコンパイルかけて一つずつつぶしていくという地味な作業になるだろうか?気になるのはタグの11ビット以降を利用しているもの(文字列とか)。別にフラグを構造体に持たせる必要がでるのか、現状のようにヘッダー内でなんとかしてしまうか悩むところ。
クラス階層として、文字列、ベクタ、バイトベクタをシーケンスクラスのサブクラスにしようかはちょっと悩むところだが、ハッシュテーブルとツリーマップはディクショナリクラスのサブクラスにしたいところ。
総称関数は多分後回しにできるので、先にクラス構造を入れてしまいたい。
まずはTinyCLOSのソースを熟読するところから始める必要はあるだろう。

もし何方かSagittariusを使っていて、こんな機能がほしいってのがあったら是非教えてほしいですm(_ _)m

2011-12-05

Reader macro preparation

Since I want to add CL like reader macro to Sagittarius, I have been thinking how. I think I had a solution for it so I'm going to memorise it.
Since R7RS draft has been released 4 times and it has different reader macro for bytevector (blob in R7RS or also bytevector?), I need to switch readers between R6RS and R7RS. By default, it can be both, but if user (for now only me...) explicitly specified the mode with shabang #!r6rs or #!r7rs, it should enable or disable some R6RS or R7RS specific reader macro. Well, it is actually easy to just switch with current solution (currently I just see the flag of it and check). But it's not so smart when R8RS or R9RS appears.
So, I'm thinking to add reader macro sets to VM and for future even libraries. The concept is really simple, I just need predefine reader macros for R6RS and R7RS it can be shared its implementations and when #!r6rs or #!r7rs appears switch the set. For this purpose, this behaviour must be per file like current shabang and by default it should have everything (just my preference)
How to do it? The point will be 2 or more letters reader macro such as #vu8(...) or #u8(...). I am thinking I will add 2 sets to VM. One is one letter reader macro and other one is 2 letters reader macro and 2 letters are high priority (or else bytevectors will be invalid vector syntax ...)
Well, I wrote about reader macro however first of all I need to finish replacing regular expression library...

LA行ってきた

今回はバカンスではなく、仕事の面接ということでダウンタウンがメインだった。面接の結果はまだ出ていなく、ボールは向こうが持っている状態。ただ、ボールがもしこっちに来た際には決断をしなければならない。いく前は割りとLAに行こうかなと思っていたのだが、今は揺れている感じ。
いくつか理由がある。良いところと悪いところ両方。少し頭の中をまとめていこう。

良い点
  • まず間違いなく給料があがる。それも下手したら3倍くらい。
  • うちの彼女がアメリカに行きたがっている(NYCなので東海岸だが・・・)
  • ビザを出してくれるアメリカの企業はほとんどないので、次はないかも
悪い点
  • アメリカにいるのに日系企業で日本のコミュニティにいたら、なんか違う。
  • 生活面ががらりと変わる。特に気候とか。夏場40度もあるって聞いたから多分融ける。
  • RX-8は諦める必要がある。(持っていくのが不可能に近い)
  • IT系でも今やってる分野からかなり離れるので次を考えたときに不安。
  • あのLA訛は慣れるのに時間掛かりそう。
大きな部分を占めている順に書いてみた。悪い点が多いのだが、よい点の一番上はかなりでかい。悪い点の下2つは割りとどうでもいいしw
良い悪い関係なく揺れる点ももちろんいくつかあって、それらも含めて考えないといけない。正直人生の岐路としてはかなり大きいので、ボールがこっちの来ないまま「あの時は残念だったね」なんて言っていた方が楽かなぁとも思ったりする。優柔不断な性格には結構きつい・・・

2011-12-01

LAに到着

着陸アプローチが2回あったり、空港が停電してたりとあんまりろくなことがなかったが(機内食のチキンカレーは旨かったか)、無事LAに到着。
思っていた100倍でかい街でちょっとうろたえていたり。あまりにでかすぎて既にオランダが恋しいというチキン野郎。

何故ここにいるのか、(しかも独りで)、実は面接を受けに来ているのです。そう、ひょっとしたら(また)移住するかもしれない。オランダ気に入ってるので、給料の額面とか次第ではあるけど。(まだ決まってない)
自分自身こんなに国を転々とするとは夢にも思っていなかった。本当どこでこんなイベントフラグ立てるんだろう?
どうでもいいが、LAの人の英語が聞き取りずらい・・・オランダ訛に慣れすぎたか・・・

2011-11-29

ちょっとひどすぎだろこれ?

橋下知事が高校生をフルボッコwwwwwww
上記のサイトにあった動画を見た。ひどすぎというのは知事ではなくて、高校生の方。
討論になってない。というか嘆願にすらなってない。

というだけでは自分も同類なので、まずは自分がどちら側か。もちろん知事側。
理由、僕は大学受験の際に不景気のあおりで傾きかけていた親の事業のため、母親から「お前だけは国公立に行ってくれ」と言われたので。
この高校生たちに言いたいこと。中学のときに勉強しなかったの?それとも別の理由で私学に行ったの?学力が優秀なら奨学金制度もあると思うけど、それは活用しないの?etc.

と、これだけだと面白くないので、高校生側からなんとか議論に持っていけないかとも考えてみた。
大阪の受験事情を知らないので、僕が高校受験をした際の愛知の事情で。
愛知県は当時A日程、B日程と分かれていて、もちろん両方受けることができる。ただ、進学校を狙って両方とも自分のレベルより少し高い高校を受けた際に、両方とも落ちるということはままある。この辺僕は割りと問題だなぁと思っていて、どうしても滑り止めとして私学を受ける必要があった。(もちろん中学浪人という選択肢もあるはあるが)
僕ならそこを切り口にして、
  • 将来的に○○を専攻したいと思い、その分野に力を入れている公立の進学校を狙ったが、不甲斐なくも両方落ちた
  • 母子家庭で母親の収入だけでは厳しいのを知っていたが、進学校の私立に自分もバイトをして学費を稼ぐからと無理をいって入学
  • そうは言ってもそれだけでは厳しいし、バイトばかりして学業がおろそかになるのも本末転倒なので、補助金も学費の足しにしていた
  • 奨学金は大学の入学費等に当てるため貯金している
という路線で補助金の必要な環境にいることをアピールして、例えば学業の優秀な人には補助金を出して教育に力を入れるよう促すかね。
でも、単に補助金の減額だった上記のようなのは考慮に入ってるかもしれない。全額カットだと議論に余地ありかもしれない。問題は、そこまで考えている高校生には見えなかったのと、話の内容から箸にも棒にも引っかからない成績だったので私学に行ったように見えたので、話に信憑性がでないことか。

2011-11-28

頓珍漢なコメントをしてしまった

えぇ、正直浮かれてました。

事の発端はこちらのブログ
R7RS実装のポイント(改) - .mjtの日記復帰計画
なぜ浮かれたか、Sagittariusの名前が載ってたから。
っで、キーワードのことについて触れてたので、頑張ればポータブルに書ける旨をコメントしたのだが、3回くらい記事を読み返しておかしいことに気づいた。
(コメントを消す機能はないみたいなので、頓珍漢なコメントはインターネットに残り続ける・・・orz)
多分、R6RS処理系全体でポータブルなR7RSのライブラリを書くということなんだと思う。(国語苦手だったんです)
もしそうなら、こんな超マイナー処理系まで考慮に入れていただいて大変ありがたいです。moshを散々disってすいません。でも、moshには負けません(何の勝負?)

こんなとこ読んでないとは思うけど、一応言い訳・・・

2011-11-27

トヨタ「ハチロク」

こんな記事を見つけた
トヨタ「ハチロク」復活へ…来春に200万円台
200万円台といわれると、僕のRX-8(日本にある。売られる前に引き取らねば!)と一緒だわね。まぁ、前半か後半かで大分違うが。(僕のは240万台だったかな?そこから20万値引きがあったけど、諸費用が結構かかった記憶。ブログのどこかに書いたか?)
正直、トヨタがそう好きではないというのを差し引いても、あまり魅力を感じないなぁと思ってしまった。水平対抗なエンジンがいいっていうなら、スバルのインプレッサを買えばいいわけだし、イニシャルDを読んでない20代の若者はハチロクって言われても「何それ?」だろう。
スポーツカー好きな僕としてはメーカーがスポーツカーを作るというのは非常に好ましい状況なのだが、これはどの層をターゲットにしてるのかわからないなぁ。(RX-8のときは4ドアスポーツカーで、多分30代の家族持ちでスポーツカーにもう一度乗りたいという人だったのだろうと思う)
正直今のご時世で2ドアスポーツに200万以上払う人がいるだろうか?マツダのロードスターみたいな「車好きに!」みたいなコンセプトならまだしも、そうは見えないしなぁ。

この辺の意見を見ると、賛否両論だなぁ。まぁ、元が2chなので微妙ではあるが。
下の方みたら、ベースで250万になるとかのレスがある。本当なら正直RX-8より魅力が低いのに価格が上という糞仕様になるなぁ。(多少RX-8贔屓過ぎるか?ロータリーエンジン可愛いよロータリーエンジン)

日本のIT企業とオランダのIT企業の比較

slashdot.jpにこんなのがあった。
日本人プログラマーについての記事が Hacker News で話題になった
元ネタはこっち(英語)
Force Multipliers and Japanese Programmers

要約すると、日本企業は新しいフレームワークを使うことを極端に嫌がるため、生産性を落としているというものだ。
僕が日本のIT企業で働いた経験は(多く見積もっても)4年半なのであまり本質を捉え切れてないかもしれないが、大まかにそうだなぁと思うところが多々あった。スラッシュドットのコメント蘭は特に的を射ていた感がある。
それにプラスしてではないが、自分が感じたオランダ企業との比較をちょっと書いてみようと思う。一つしか知らないのであまり参考にはならないが。

納期を極端に重要視する
日本で働いていた際にどう考えても無理だろうという納期があったが、いわゆるデスマでなんとかした経験がある。その際にプロジェクトマネージャーに「どう考えても間に合わない再スケジュールしてくれ」と頼んだのだが、答えは当然「無理!」だった。
逆にオランダではその辺は割りと柔軟でこちらからあらかじめアラートを挙げると、優先順位の付け直しをしてクライアントとスケジュール調整をしてくれる。

自社製のフレームワークに極端にこだわる
まぁ、これは上記のブログでも言及されていたが、日本では割と元請けのフレームワークの使用を強制される。そのフレームワークが「実際に生産性を向上させるもの」ならいいのだが、4年半(開発者としては2年半)の間にそのような優れたものを見たことがない。おおよそ、「素直にhibernate使えよ」とか(当時なら)「素直にstruts使えよ」というようなものが主だった。個人的にこれらのフレームワークが好きではないのだが(特にspring)、確かに車輪の再開発をする必要がないのでビジネスロジックに集中できる。また、一度これらのフレームワークを習得してしまえば別のプロジェクトでも使えるので習得期間という初期コストを抑えられる。でも、○○社謹製△△フレームワークなんての物の習得がプロジェクト毎に入るんだよね。大抵上記のフレームワークの劣化版かつAPI互換性なしで。まぁ、その期間なにもしなくても金が入るから偽装派遣屋さんには美味しいかもしれないけど。

ユニットテストの概念がない、その為リファクタリングが非常に困難
この辺は今はよく知らないけど、僕が働いていたときはテストといえばテストケースの仕様書があって、人間が手でデータを揃えて都度エビデンスの作成。仕様変更もしくは不具合修正があればやり直し。これが結合テストですらなく単体テストレベルだったから涙が出そうだった。
(じゃあ今テスト駆動でやってるかといえばそうでもないのだが・・・)

PG->SE->PM以外のキャリアパスがない
個人的にはこれは結構問題だと思ってて、それぞれ全然違うものなのにあたかも順当なキャリアパスのように扱っている。例えば日本の求人を見ると、「PG->SEへと着実なスキルアップ」なんて殺し文句をそれこそほぼすべてのSIerの求人でみる。SEというポジションが最早なんなのか分からなくなったけど、SEの先にPMがあるなら、PG->PMになるはずだ。PMってプロジェクトマネージャの略なのでスケジュール管理とクライアントとのミーティングがメインタスクになる。
そうなりたいならいいけど、プログラマからそこに行く道って、大工が保険の営業になるくらい違うと思うのだが、どうしてそれがデフォルトなんだろう?(理由は次の問題で)

PMが最も単価の高い高級職
上も問題の答えだと思う。企業は単価の高い人間を売りたい(IT企業の多くが2次請け=人売りだと仮定)ので、単価の安いPGよりも単価の高いSE、PMを元請けに売りたいわけだ。
でも、これってすごく間違っていて、技術の高いプログラマが育たない、もしくは育っても開発に携わらないということになる。
これはモチベーションの問題だが、PGの単価が安いってことは、がんばっても給料安いのでやる気もでんわね。

思い出せただけでこれだけか。そもそも、文学部出身がPGになるってので間違ってる気がするけど。如何にIT業界(SIerか?)の求める人材がコミュニケーションスキルに偏ってるか分かるところか。

2011-11-24

疎な配列(Sparse Array)

あんまりこれについて書かれてる記事がなかったので書いてみる。
(MathematicaにマニュアルがGoogleでトップにヒットするんだもん)

事の発端
RE2のソースを読んでて、SparseArrayなる入れ物があるのを発見。Sparse Arrayとは何ぞやと思い調べてみた。

分かったこと
タイトル通り、訳としては「疎な配列」。意味は配列自体は巨大なんだけど中身はすっからかんな配列。(実装が腐ってるとそうなる)
ハッシュマップと何が違うの?もともとは行列を扱うのに適したものだったらしい。なのですべての要素に初期値がある。(ハッシュマップにはない)
後、検索、挿入、削除などのアクセスが定数時間(O(1)ってこと?)。イテレーションにかかる時間がO(n)でnは要素数、配列のサイズではない。
普通の配列を使うより高速かつハッシュマップと違ってリサイズの心配がない。(動的な配列ではないた。、実装によると思うが)


何がハッシュマップに比べてよさげか
あくまで配列なので、ハッシュの計算をする必要がない。(RE2のSparseArrayはそうなってる)
RE2の実装ではイテレータはインデックスと値のpairを返す。(多分そうやってO(n)のイテレーションを実現してるのだろう、for(int i = 0; i < size; i++)とは書けないということだね)
リハッシュの心配がないので、メモリの見積もりが聞く。


何が単なる配列よりよさげか
総アクセス時間が要素数に対しての線形になる。(配列のサイズじゃない)

自前で実装するほどのメリットが今のところ見当たらない。結局メモリの節約になるわけでもないし。
あ、まてよ、RE1もRE2もコンパイルされた正規表現のインストラクションの数だけ配列つくってるなぁ。10000以上になってきたらさすがに総アクセス時間がパフォーマンスに影響を与えそうだ。
とりあえず、配列アクセス用のマクロ書いて、パフォーマンスチューニングの段階になったら考えるか。(こういうときC++だとtemplateが使えると便利だよなぁ・・・)

2011-11-23

Inlinable marking

On Sagittarius Scheme, as I wrote before (in Japanese) it has mechanism to inline non exported small procedure and seems constant variable implicitly. Well, it sounds really good, it does optimise like GCC's static functions or static const variables.
However, if it marks wrong way, there is problems. I have introduced define-constant syntax and implicit inliner for non exported constant value since version 0.2.2. The latter one had the problem. The compiler marks some variable which should not be inlinable as inlinable. Let me show my embarrassing staff:
(library (test)
   (export test)
   (import (rnrs))

  (define *inner-value* #f)
  (define *inner-value2* #f)

  (define (test)
    (and (begin (set! *inner-value* #t))
         (begin (set! *inner-value2* '())))
    (display *inner-value*)(display *inner-value2*)(newline))
)
In this case, (I actually did not check if it does mark wrongly or not) *inner-value2* will be marked as inlinable, so the compiler inline it as #f. This is the problem. As you can see, it has to be '().
Inlinable marking is written as conservative as possible. Well, actually it wasn't ...

I was too lazy to open an issue for this on Google code, so I just fixed it.

2011-11-22

俺もアメリカに行くよ

mixiのマイミクが書いた日記の標題をそのままパクリ。
そのままです、アメリカに行きます。3泊4日で。今回はLAです。

期間は11月30日から12月3日まで。何が偶然かと言えば、マイミクは誕生日をアメリカで過ごしたが、どうやら僕もそうなりそうだ。節目の年(と多くの人が考える年齢になる日)をアメリカで過ごすのは何かの暗示だろうか。
まぁ、いろいろ事がうまく(なのか拙くなのか正直自分でも分からん・・・)進むといろんな意味で節目になるだろうなぁとは思っているのだが。

ところで、誰かLAでお勧めがあったら教えて。正直右も左も分からん。
といっても、あんまり身動きが取れる時間がないので手早くいけるお勧めの場所、レストランなどがあるといいなぁ。

2011-11-20

ロードマップを考えてみた

今後のリリースでSagittarius Schemeがどのようになるのかというのを考えてみた。
たいした物ではないし、0.3.0までしか考えてない。
version 0.2.3
  • 組み込み文字セット(完)
  • 正規表現ライブラリの書き直し(in Progress)
    • cl-ppcreの移植
version 0.2.4
  • リーダーマクロの導入
    • 【動機】正規表現を#/regex/と書きたい!
version 0.3.0
  • Tiny CLOSをベースにした組み込みCLOSの導入
    • 【動機】総称関数がほしくなる場面が多々ある
    • 【動機】現状、レコードと公開していない構造体が混在しているのですっきりさせたい
  • 上記をベースにしたレコードの実装
  • (可能なら)VCでの文字列をC++11仕様のU"string"に変更
    • 文字列リテラルをutf-32(ucs4)に統一
    • VCでのコンパイルはCではなくC++になるかもしれない
思ったとおりたいしたものではなかった。もちろんバグフィックスは随時だし、簡単にできそうかつ効果的だなぁと思ったものは実装していくと思う。
なので0.3.0が0.2.4の直ぐ後に来るとは限らないわけだが。(ただCLOSを組み込みにするとなると、結構な修正が入るので、無暗に修正範囲を広げることはしたくないので、多分0.2.4の後は0.3.0になると思うが。もしくはリーダーマクロを0.3.1に回すかもしれないが)
リーダーマクロは実装の目安がある程度あって、試してみたいなぁというのがある。これも内部的に結構いじくることにはなるので、(主にリーダー周りだが)どこで入れようかは実際迷い中。

独自実装を入れすぎだろか?
まぁ、Mostly R6RSと謳っているので問題ないか。ユーザー数1(僕)だし。

2011-11-16

正規表現ライブラリを書き直そう

組み込みの文字セットが完了したので、いよいよ本丸に取り掛かろう。
現在SagittariusではJavaから移植した正規表現を使っている。別にこれで問題はないと言えばないのだが、気になるのはその中で独自のASTを持っていること。
せっかくSchemeなんだしS式でASTを構築したほうがいいんじゃね?とちょっと思えてきた。
なぜそう思ったか?
世の中にはPCREとSREという正規表現のタイプがあるらしい。まぁ、もっとあるだろうが。っで、PCREは超有名なPerlの正規表現。SREはS式で書かれた正規表現らしい。Perl形式の正規表現をSREに置き換えて、ってやれば、PCREもSREも使えて一粒で2度おいしい感じがする。
と、こういうことをやっているのが、irregexというAlex Shinn氏が書いてるScheme用の正規表現ライブラリなわけだ。とりあえず、それを足がかりに文字列->SREなものを作って、ごにょごにょするか、もう少し資料をあさってみるか。(鬼車のソースを眺めてみるのもありか)

2011-11-15

Scheme使いのためのClojure入門

なんてタイトルをつけてみた。別にたいしたことをするわけでもなく、単に文法上の比較というかなんというか。
とりあえず、Clojureを触ってみた感想ではある。
;; 定義
(define hello
 (lambda (name) (string-append "hello, " name)))
;; or
(define (hello2 name)
 (string-append "hello, " name))
;; 定義
(def hello (fn [name] (str "hello, " name)))
;; or
(defn hello2 [name] (str "hello, " name))
特に何も書く必要はないだろうというくらいよく似ている。[]は引数だけど、ベクターのリードマクロでもある。
うわさによるとdefnは構文ではなくマクロだそうだ。 あと、defnはデフォルトでSchemeでいうcase-lambda仕様になっている。
;; 名前付let
(define (factorial n)
  (let loop ((cnt n)
            (acc 1))
    (if (zero? cnt)
        acc
        (loop (- cnt 1) (* acc cnt)))))
;; 名前付let
;; なんてものはないのでloopとrecurを使う
(defn factorial [n]
  (loop [cnt n acc 1]
    (if (zero? cnt)
      acc
      (recur (dec cnt) (* acc cnt)))))
loop構文は多分letのバインド方式だと思う。Clojureではletは括弧が一つでよくて、順に変数名、初期値、以下続くという感じになる。Schemeに慣れているとちょっと混乱しそうになる。
とりあえずこれくらいにしてしまう。本家サイトにドキュメントあるので、興味があればどうぞ。

それはおかしいだろ

マジで言ってるなら人間性を疑うレベルだが。
https://twitter.com/#!/YANA1945/status/136027550081220608
今日の面接。「もし給料が支払われなかったらどうする?」私が必ず訊く質問だ。目をキラキラさせて「構いません!」と叫ぶ人は合格。少しでも戸惑う奴。そんな奴とはプライベートでも口を聞きたくない!さっさと立ち去れ!
「論外ですね」が僕の答えになるだろうなぁ。もしくは何も言わずに立ち去るか。
叫んだ人は奴隷根性丸出しと批判してもいいかもしれない。
業績不振で遅延支払いになるとかだったらまぁ、「理由しだいでは理解します」になるかもしれない。実際、今の会社は今年のベースアップはなしと言われたし、理由もまぁしょうがないかというものだった。
支払われないということは、雇用契約の反故を通り越して「死ね」と言われているのとほぼ同義だと思うので、奴隷なら雇用者の都合で死ねという理念が無ければこの質問は出ないはずだ。

もともとは、
Island Life - 釣りくさいけど
で見つけたものなのだが(べ、別にShiroさんの隠れファンなんかじゃないんだからね!)、豪快に1本釣りされたのかもしれない。
でも、このエントリの追記にツイートした人は面接代行業者とあったので、割とマジなんだろう。整合性も確かにある。
っが、お前同じこと言われたら目をキラキラさせて「構いません!」と叫ぶんだろうな?と問い詰めてやりたい。他のツイートもなんだかキ○ガイじみてるものだったし、まぁ、根性論で走る人なんだろう。
可能な限りお近づきになりたくないタイプの人種だ。

2011-11-14

赤黒木(続)

結局LLRBではなく純正赤黒木を作ることにした。というかJavaのTreeMapの実装をそのまま移植したともいう。
テストができてないが、とりあえずいいだろう。

一つ前の記事で、メモリ効率と書いたが、よく考えればそんなものどうでもよくて、重要な点は順序があることだった。実装してて気づいた。馬鹿か俺は(カカシ風)
この赤黒木はSchemeのAPIにもするつもりではあるが、元々は組み込みの文字セットを実装するためのものである。なので、ハッシュテーブルのようにオーダーがなくなるようなものではなく、データの挿入時にオーダーが保たれるものじゃないと困るわけだ。(範囲等の関係で)

正直赤黒木の中身がどうなっているのかいまいち理解してないが、(2-3-4木を二分木で表現したものということくらいしか・・・)、単に使うだけなら問題あるまい。問題が出たら直せばいいわけだし。
木の回転とバランスが命だということは実装(コピー)してて気づいたが・・・

2011-11-12

赤黒木

(後々の正規表現ライブラリ改善のため)組み込みの文字セットを入れようと考えているのだが、ハッシュテーブルをセットとしてもつより、平衡二分木を持った方がメモリ効率的にいいかなぁと思い、赤黒木を調べ中。(長い)

調べてみると、実装が難儀とあって、LLRBなるものを発見。Javaで200行程度で実装できるらしい。とりあえずこいつを実装して、後から実装を変更するのもありか。
となると、他の二分木も実装(AVL木とか)も試してみたくなるよなぁと思うのが人間で、そうするとインターフェースだけ定義して実装は別にしたい。何を持つべきだろう?
正直、どこまでやるかということになるが、一応Schemeからも実装できるようにしたいなぁと思うと以下のようになるだろうか?
typedef struct tree_map_rec
{
  int type; /* CかSchemeか*/
  union {
    struct {
      int (*insert)(struct tree_map_rec *tree, node_t *node);
      /* so on */
    } c_procs;
    struct {
      /* scheme procs on C object*/
    } scheme_procs;
  } impl;
} tree_map;
問題になりそうなのはnode_tの定義か?まぁ、とりあえずintptr_tの別名にして、実装毎に適当に定義してキャストすればOKだろうか?
あとは、実装ごとにイテレータをどう用意するかとかその辺を煮詰めないとなぁ。この辺C++だともう少し簡単に書けそうだよなぁ。ABIの問題さえなければ・・・

2011-11-11

1年前のネタを拾った

ボーっとどう書く的なネタを探していたら、こんなの発見。

Twitter / ばーるのようなもの: comp.lang.scheme で簡単なリスト操作 ...
comp.lang.scheme で簡単なリスト操作のお題が出ておるな。
っでやってみた。
(import (rnrs) (rnrs mutable-pairs))
(define (acons k v r) (cons (cons k v) r))
(define (process-labeled-list lst)
  (let loop ((lst lst)
      (ans '()))
    (if (null? lst)
 (map cdr (list-sort (lambda (p1 p2) (< (car p1) (car p2)))
       ans))
 (cond ((assv (caar lst) ans)
        => (lambda (slot)
      (set-cdr! slot (append (cdr slot) (cdar lst)))
      (loop (cdr lst) ans)))
       (else
        (loop (cdr lst) (acons (caar lst)
          (cdar lst)
          ans)))))))

(process-labeled-list '((0 a b) (1 c d) (2 e f) (3 g h) (1 i j)
   (2 k l) (4 m n) (2 o p) (4 q r) (5 s t)))
set-cdr!を使うのは卑怯だろうか?
副作用ありだとあんまり関数型っぽくはない気もするが。

他の処理系でも動かすために、aconsを再定義。Sagittariusならなくても動く。というか、import文すら無くてもこれくらいなら動く。

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の改善だったのに遅くなったという面を除けば悪くない。