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な話題。