
秋の夜長にぴったりな本がたくさんそろいました。全部読み終わるのはいつになるだろう。特にKnuth先生のThe Art of Computer Programming Volume 1-3のボックスセットは邦訳のほうではなく、原書なので余計に時間がかかりそうです。2010年ぐらいまでには読み終わりたいですね。
具体的なプロトタイプ設計に入る前に、別のアイデアを忘れてしまわないようにメモ書きしておきます。今回、当初の目標はHTMLのtableと同程度の表現能力を考えています。さて、表現能力の獲得はがんばればよいとして、その3で紹介したようにセルが結合されているような表をAPIで指定していくのは面倒くさそうです。たとえば、こんな感じになるんでしょうか。
Hogan h = new Hogan(); // (3, 2)の位置のセルでセル幅を2にして結合 h.setColumnSpan(3, 2, 2); // (4, 6)の位置のセルでセル高を2にして結合 h.setRowSpan(4, 6, 2);
これはこれで必要なAPIですが、これだけで表を作り上げていくのはかなり面倒くさそうです。ですので、下記のようなイメージで、流行の流れるインタフェースというのを使ってはどうかと考えています。
// Builderを通してほぼHTMLの感覚でかける。
Hogan h = HoganBuilder.table()
.thead().tr()
.th("銘柄", Style.rowSpan(2))
.th("始値")
.th("高値")
.th("前日比")
.th("出来高", Style.rowSpan(2))
.th("売気配")
.tr()
.th("終値")
.th("安値")
.th("前日比%")
.th("買気配)
.tbody().tr()
.td("ABC", Style.rowSpan(2))
.td(6460, Style.decimalFormat("#,##0"))
.td(7360, Style.decimalFormat("#,##0"))
.td(2000, Style.decimalFormat("+#,##0;-#,##0"))
.td(1275, Style.decimalFormat("#,##0").rowSpan(2))
.td("---", Style.decimalFormat("#,##0"))
.tr()
.td(7360, Style.decimalFormat("#,##0"))
.td(6020, Style.decimalFormat("#,##0"))
.td(0.373, Style.decimalFormat("+##0.0%;-##0.0%"))
.td(7360, Style.decimalFormat("#,##0"));
// 行の追加
h.addRow(HoganBuilder.tr()
.td("DEF", Style.rowSpan(2))
.td(55000, Style.decimalFormat("#,##0"))
.td(56000, Style.decimalFormat("#,##0"))
.td(-1800, Style.decimalFormat("+#,##0;-#,##0"))
.td(61899, Style.decimalFormat("#,##0").rowSpan(2))
.td(52800, Style.decimalFormat("#,##0"))
.tr()
.td(52700, Style.decimalFormat("#,##0"))
.td(52200, Style.decimalFormat("#,##0"))
.td(-0.033, Style.decimalFormat("+##0.0%;-##0.0%"))
.td(52700, Style.decimalFormat("#,##0")));
// 行の追加
h.addRow(HoganBuilder.tr()
.td("GEF", Style.rowSpan(2))
.td(32350, Style.decimalFormat("#,##0"))
.td(32400, Style.decimalFormat("#,##0"))
.td(300, Style.decimalFormat("+#,##0;-#,##0"))
.td(144616, Style.decimalFormat("#,##0").rowSpan(2))
.td("---", Style.decimalFormat("#,##0"))
.tr()
.td(30650, Style.decimalFormat("#,##0"))
.td(30100, Style.decimalFormat("#,##0"))
.td(0.009, Style.decimalFormat("+##0.0%;-##0.0%"))
.td("---", Style.decimalFormat("#,##0")));

このようにかければ、わりと拡張の融通も利きそうですし、HTMLで表を作ったことがある人ならば、違和感なく使えると思います。スタイルの指定をどうするかはもう少しアイデアを練り込む必要がありそうです。なお、上記の例では毎回、それぞれの行で書式を指定していますが、このあたりは冗長に見えるので、テンプレート機能のようなものを用意しても良いだろうと思っています。
// 株価クラス
public class StockPrice {
private String symbol;
private int startPrice;
private int endPrice;
private int highPrice;
private int lowPrice;
private long volume;
private int diff;
private float diffPercent;
private int ask;
private int bid;
public void setSymbol(String symbol) { this.symbol = symbol; }
public String getSymbol() { return this.symbol; }
... // 以下 setter/getterやコンストラクタなど
}
// 株価情報
StockPrice s = new StockPrice("DEF", 55000, 52700
, 56000, 52200
, -1800, -0.033f
, 61899
, 52800, 52700);
Hogan h = ... /// ... ベースとなる表の作成
RowTemplate tmpl = RowTemplateBuilder.tr()
.td("symbol", Style.rowSpan(2))
.td("startPrice", Style.decimalFormat("#,##0"))
.td("highPrice", Style.decimalFormat("#,##0"))
.td("diff", Style.decimalFormat("+#,##0;-#,##0"))
.td("volume", Style.decimalFormat("#,##0").rowSpan(2))
.td("bid", Style.decimalFormat("#,##0"))
.tr()
.td("endPrice", Style.decimalFormat("#,##0"))
.td("lowPrice", Style.decimalFormat("#,##0"))
.td("diffPercent", Style.decimalFormat("+##0.0%;-##0.0%"))
.td("ask", Style.decimalFormat("#,##0")));
// 株価情報JavaBeanからテンプレートに沿って行に展開
h.addRow(tmpl.newRow(s));
このようにたとえばRowTemplate・RowTemplateBuilderのようなクラスを作っておき、実際に付け加えるデータはStockPriceインスタンスのように管理しやすいままの形で複雑な形式の行を追加します。この例でのRowTemplateBuilderのtd(”symbol”, … のような第一引数はプロパティ名です(拡張してELに対応するか、BeansBindingに対応してもおもしろいですね)。ここではJavaBeanに対するアクセスを考えていますが当然、HashMap等にも応用できます。
この例は表示スタイルについてのみ定義していますが、ほかの発展イメージとしてはバリデーションルールの追加や、エディタやレンダラの指定なども考えられます。
だいぶデザインコンセプトが固まってきました。最初のプロトタイプはいきなり実装を始めてプログラミングの感覚を取り戻すことにしようかと思っていましたが、それではもったいないのできちんとした設計からやる方針に変えました。きちんと設計を進めてみたところ、今回作ろうとしているのは単に表としてデータを羅列するための役割を持った部品とするよりも、ある情報アーキテクチャに基づいてデータをユーザに公開したり、ユーザからのアクションに対応するためのフレームワークと考えた方がより汎用的です。それに、それぐらいの設計上の敷居をあげておかないとリハビリとしては甘いような気もします。

最初、どれぐらい汎用的なデータモデルを用意すれば、複雑な表にも対応できるか考えるために複雑な表の例を考えることにしました。

まずJTableでも実現できるもっともシンプルなテーブルを考えます。列がいくつかあって、ヘッダがあります。データは一行ごとに表示される形式です。これならばデータの形式はJTableのTableModelのように行ごとに管理するのが良さそうです。セルの結合が多少あっても、それはレンダリングのためのヒント情報を与えてやれば解決できそうです。もう少し複雑な例を考えてみます。

セルやヘッダが結合された上に、ひとつのデータの固まりが複数行を使う場合です。ここでは例として株価情報にしていますが、一件のデータについて表示項目がかなり多くなる場合には、横にデータを広げるよりも折りたたんで、複数行で表現するほうが可読性が向上する場合があります。このような表を扱うような場合、表示上の行と、データ上の行は必ずしも一致しなくなります。JTableのTableModelでこのような表を扱うのはかなり難しくなります。

次の例は、たとえばテーブルの表示幅にあわせて列数が可変になるような表の構造です。この例ではデータは左上の端から右方向に詰め込まれ、表示幅が足りる範囲で右方向に成長します。表示幅が足りなくなれば、次の行に改行します。このような表はショッピングサイトなどのカタログでよく見受けられます。この表のデータモデルは、一次元的ではありますが、表示上の位置関係とは独立したデータモデルとなり、表示位置は動的に決定されます。

さて、表の例としては上記以外にも様々なパターンが考えられますが、様々な形式の表に対応するためには、モデルが多様なデータ形式を受け入れるようにするか、複数パターンのデータモデルを受け入れるようにするなど、いかにも使い勝手が悪くなりそうなにおいのする方向で設計が進みそうです。ここでは、当初のネーミング通り、方眼紙というコンセプトに戻ることにしました。

まずは、もっともシンプルな方眼紙のようなコンポーネントを設計し、それをベースとして多様なデータ表現を実現できるような拡張の形で広げていく方法です。これならば扱うデータモデルの複雑さとAPIとしての使い勝手のバランスがうまくとれそうです。この設計が良いかどうかまだ分かりませんが、あまり設計ばっかり考えていてもリハビリにならないのでだいたいこの方針で、随時軌道修正しながら実装プロトタイプを作っていってみることにします。
これから作るSwingコンポーネント、JHogan(仮)の設計方針についてまとめておきます。
- UIクラスも自作する。レイテンシ上、シビアになった場合のチューニング余地を広げるため。また、デザイン上も独自の工夫をするのが楽しそうだから。
- JScrollPane相当も自作する。フォーカスの制御や、複雑なスクロールの制御をするなら自作した方が簡単そう。
- プラグイン可能なアーキテクチャ。いろいろ追加的機能はどんどん欲しくなると思うので、機能を動的につけたり外したりしやすい感じのアーキテクチャをねらいます。
- アノテーションやジェネリクスの活用。使えるところでは積極的に使います。
- ツールの作成。ある程度複雑な表を作ろうとすると、JHoganのAPIをたたいて表を設計するのは面倒くさくなりそうなので、表のテンプレートを作るためのツールも作ることにします。ただし、ツールばっかりに注力しないように、優先順位は低めに設定します。
新たに設計を始める前に、もっとも類似で参考とすべきクラスJTableを使ってみての感想まとめておきます。今回はリハビリ目的と言うこともあって、JTableを拡張するよりは、JTableではなく全く新たにコンポーネントを設計しようと思っています。これは、JTableだけでなく、画面スクロールのためのJScollPaneも同様に新たにこの方眼紙コンポーネントのために設計することにします。では、既存のSwingコンポーネントJTableとJScrollPaneを使った場合の問題点を。
- TableModelが抽象的すぎる。ある程度複雑な表を扱おうとすると、TableModelが定義しているインタフェースでは不足ぎみです。リッチな表現を実現するためには、JTableかTableModelのどちらかでレンダリングのためのコンテキスト情報(たとえばセル結合とか、背景色とか)を持つ必要がありますが、現状のJTable・TableModelのコンビネーションではこれらのデータを扱う方法が十分考慮されているようには思えません。TableModelを実装したクラスでそれらの情報を管理しても良いのですが、TableModel相当の実装が渡されたときの互換的な振る舞いを定義するのが面倒なことと、より適切にテーブルモデルを設計することもリハビリの一環と考え車輪の再発明をすることとします。
- ヘッダとなるべきセルが上部以外にある場合、スクロールとフォーカスの扱いがJTableとJScrollPaneの組み合わせではややこしい。たとえば、表の左側を固定してヘッダとしたい場合、典型的な実装例ではJScrollPaneのRow Headerに別のJTableインスタンスや、JListインスタンスを設定して、データ内容や表示を同期したりします。単純な例ではそれでも良いのですが、フォーカスの移動や、スクロール、データ内容更新に伴う列サイズの更新などが生じた場合のハンドリングが複雑で、バグが生じやすく、また、再現が難しいケースもあるので実装としては避けたい方式です。
- JTableを拡張する場合、Javaバージョン追随に対して十分ケアしなければならなくなる。Java SE 6ではJTableに対してsortやDropModeなどの拡張が追加されていますが、Java SEのバージョンアップに伴って一般的なメソッド名として追加されてしまうと、のちのち重複した名前をつけてしまった場合に面倒になります。特に、一般的な名称は標準的な仕様でりようされてしまい、回避的な名称にすると、理解しづらくなってしまうので新たにクラスを起こす方が適切だろうと考えています。
- 複雑なヘッダ構造に対応できない。ヘッダとなる行(あるいは列)が複数行で構成されるような表に対応することは現状のJTableHeaderでは難しい。
- プラグイン的に振る舞いを追加することがあまり簡単ではない。表のようなコンポーネントには様々な要件が日常的に要求されますが、JTableは処理の多くを隠蔽しているので、多様な処理をあとから付け足すためにはややこしい処理を余儀なくされたり、せっかく作ってもJavaのバージョンアップとともに挙動が変わってしまう可能性もあります。
JTable継承しないデメリットもあります。JTableを含め標準的なSwingコンポーネントはLook & Feelに応じたUIレンダリングクラスが標準的に用意されています。その一方で、新たにコンポーネントを起こす場合には、各Look & Feel向けにそれらのUIレンダリングクラスを実装する必要が生じます。このことがSwingでカスタムコンポーネントが初期に想定されたほど開発されなかった原因の一つだと思います。ただ、これは今回、マルチLook & Feel対応を初期段階ではあきらめることで許容範囲内とします。余裕があれば、複数のLook & Feelに対応しても良いのですが面倒なのできっとしないと思います ;-P
最近、サンプルプログラムを動かしたり、ちょっとしたシェルスクリプトを書く以外ではめっきりプログラミングをしなくなったので、リハビリをしようと思います。リハビリ目的なので、ある程度複雑な題材にチャレンジすることにします。ちょうど、前から作ろうと思っていた題材があるのでそれを作ることにします。今回作ろうと思うのは、JavaのSwing向けに方眼紙みたいなコンポーネントです。

SwingにはJTableというコンポーネントがありますが、ある程度複雑なことをしようとすると、とたんに破綻します。たとえば、それぞれの行にヘッダ(左端の列をヘッダとする)を付与したり、セルを結合したり、同一列内で複数のデータ型を扱うような場合です。これらの問題を解決するような新たなコンポーネントを作ることは、リハビリの題材として丁度規模的にも、難易度的にも丁度良さそうです。このような表のようなコンポーネントは、特に日本市場では成熟されたExcelによる業務システムが暗黙的な比較対象となることもあって機能的にも際限なくテーマが与えられますし、ユーザインタフェースとしては十分機敏に動かなければならないのでパフォーマンスチューニングや、ある種トリッキーなアルゴリズムを思い出すきっかけになりそうです。ひとまず、2〜3回ほど使い捨てのプロトタイプを作って実証実験をした後に、そこそこ形となるモノを仕上げてみたいと思います。
前のエントリで買う、と言っていたコンピュータの数学は、大きめの本やをいくつかまわっても見つからなかったので、とりあえずそれはAmazonで注文して(明日ぐらいに届くみたい)、今日は渋谷に行ったついでに、別の本をブックオフで買ってきました。大好きなHead FirstシリーズのObject-Oriented Analysis & Designです。

ラッキーなことに1,000円で手に入りました。いまさら、OOAやOODを熱心に勉強しなければいけない訳ではないですが、特に、AnalysisとかDesignなんていうカテゴリの本は読めば読むほどいろんな観点が手に入ってとても有益だと思っています。問題は読み終われるかどうかだけですね。ほかにも読んでない本、いっぱい残ってるし・・・。
懐かしむシリーズ(?)第二弾です。いろいろ昔のファイルを整理していると、懐かしいプログラムが出てきました。高校の頃はやたらアセンブラとかを使ってグラフィックライブラリなんかを再実装するのにやたら時間を費やしていました。
/* * PC-9821 256color graphics library * $Id: dos256.c 1.1 96/02/03 22:14:27 Okazaki Exp Okazaki $ */
RCSのログも残っていました。12年半前のプログラムですね。でもこれ以外にも何回か作り直した記憶があります。これはPC-9821のMS-DOS専用グラフィックライブラリで、PC-9801では4096色中16色しか使えなかったのが、PC-9821では16777216色(24ビット)中256色を使うことができる拡張がありました。このプログラムはその拡張を利用するためのプログラムです。
#include "dos256.h"
void dos256begin(void)
{
_asm {
mov al,07h
out 6ah,al
mov al,21h
out 6ah,al
mov al,0dh
out 0a2h,al
}
}
void dos256end(void)
{
_asm {
mov al,07h
out 6ah,al
mov al,20h
out 6ah,al
}
}
もうほとんど中身はアセンブラです。この辺は文献を参考にしながら淡々と打ち込めばいいだけです。
void dos256cls(void)
{
_asm {
mov ax,0e000h
mov es,ax
mov bh,0
mov bl,1
mov es:[0004h],bh
mov es:[0006h],bl
mov cx,16384
xor di,di
mov ax,0a800h
mov es,ax
xor eax,eax
rep stosd
mov ax,0e000h
mov es,ax
add word ptr es:[0004h],2
add word ptr es:[0006h],2
mov cx,16384
xor di,di
mov ax,0a800h
mov es,ax
xor eax,eax
rep stosd
mov ax,0e000h
mov es,ax
add word ptr es:[0004h],2
add word ptr es:[0006h],2
mov cx,16384
xor di,di
mov ax,0a800h
mov es,ax
xor eax,eax
rep stosd
mov ax,0e000h
mov es,ax
add word ptr es:[0004h],2
add word ptr es:[0006h],2
mov cx,14848
xor di,di
mov ax,0a800h
mov es,ax
xor eax,eax
rep stosd
}
}
画面をクリアするための関数ですが、一応、32ビットレジスタを使って4バイトずつ画面をクリアしているので少しは速いです。そういえば、当時はBorland C++ 4.0とTurbo Assemblerを使っていたと思います。
void dos256pixel(int x, int y, int c)
{
unsigned long adr;
unsigned short vadr;
unsigned short ioadr;
unsigned char col = c & 0xff;
if (x >= 0 && x < 640 && y >= 0 && y < 640) {
adr = (long)y * 640L + x;
vadr = (unsigned short)(adr & 0x7fff);
ioadr = (unsigned short)(adr >> 15);
_asm {
mov ax,0e000h
mov es,ax
mov ax,04h
mov si,ax
mov ax,ioadr
mov es:[si],ax
mov ax,0a800h
mov es,ax
mov ax,vadr
mov si,ax
mov al,col
mov es:[si],al
}
}
}
アドレスを計算してドットをうちます。これも淡々と打ち込めばいいだけ。
void dos256palette(int c, int r, int g, int b)
{
unsigned char col = c & 0xff;
unsigned char cr = r & 0xff;
unsigned char cg = g & 0xff;
unsigned char cb = b & 0xff;
_asm {
mov al,col
out 0a8h,al
mov al,cg
out 0aah,al
mov al,cr
out 0ach,al
mov al,cb
out 0aeh,al
}
}
パレットの変更。フルカラーが当然の今だとあんまり使わないかもしれませんが、GIF画像なんかをPhotoshop等で扱ったことのある方ならインデックスカラーとしておなじみかもしれません。
void dos256line(int x1, int y1, int x2, int y2, int c)
{
int dx, dy;
int s;
int step;
dx = x2 - x1; if (dx < 0) dx = -dx;
dy = y2 - y1; if (dy < 0) dy = -dy;
if (dx > dy) {
if (x1 > x2) {
step = (y1 > y2) ? 1 : -1;
s = x1; x1 = x2; x2 = s; y1 = y2;
} else {
step = (y1 < y2) ? 1 : -1;
}
dos256pixel(x1, y1, c);
s = dx >> 1;
while (++x1 <= x2) {
if ((s -= dy) < 0) {
s += dx;
y1 += step;
}
dos256pixel(x1, y1, c);
}
} else {
if (y1 > y2) {
step = (x1 > x2) ? 1 : -1;
s = y1; y1 = y2; y2 = s; x1 = x2;
} else {
step = (x1 < x2) ? 1 : -1;
}
dos256pixel(x1, y1, c);
s = dy >> 1;
while (++y1 <= y2) {
if ((s -= dx) < 0) {
s += dy;
x1 += step;
}
dos256pixel(x1, y1, c);
}
}
}
線を引くところはブレゼンハムのアルゴリズムです。これもどこかの本に載ってたのを淡々と写経しているような感じです。ブレゼンハムのアルゴリズムが載っている本を参照しているなら、円とかも実装してもよさそうな気がしますが、使わない物は実装しない主義だったようですね。そういえば、画像系でいうと、いかに画面を素早く塗りつぶすかでいろいろ試行錯誤した後が見つかりました。フルアセンブラ実装とか。
code segment assume cs:code,ds:code,ss:code org 100h start: ; extended graphics start .186 mov al,07h out 6ah,al mov al,21h out 6ah,al ; mov al,06h ; out 6ah,al mov al,0dh out 0a2h,al ; mov ax,0e000h ; mov es,ax ; xor al,al ; mov es:[0100h],al ; xor ax,ax ; mov es,ax ; mov al,80h ; or es:[054dh],al ; display hsync 31kHz mov ah,31h int 18h and al,04h jnz hsyncend ; display hsync 24kHz -> 31kHz mov ah,30h mov al,00001100b ; hsync 31kHz mov bh,00010001b ; 640x200(upper), text 25line mode int 18h hsyncend: ; clear screen ; erase bank 0 to 7 mov bh,0 mov bl,1 call erase ; erase bank 8 to 15 mov bh,8 mov bl,9 call erase ; extended graphics end mov al,07h out 6ah,al mov al,68h out 6ah,al mov al,20h out 6ah,al ; mov al,06h ; out 6ah,al mov al,0ch out 0a2h,al ; mov al,0dh ; out 6bh,al xor ax,ax ; mov es,ax ; mov al,7fh ; and es:[054dh],al ; quit mov ax,4c00h int 21h erase: .386 mov ax,0a800h mov es,ax mov ax,0e000h mov fs,ax xor eax,eax mov fs:[0004h],bh mov fs:[0006h],bl mov cx,16384 xor di,di rep stosd add word ptr fs:[0004h],2 add word ptr fs:[0006h],2 mov cx,16384 xor di,di rep stosd add word ptr fs:[0004h],2 add word ptr fs:[0006h],2 mov cx,16384 xor di,di rep stosd add word ptr fs:[0004h],2 add word ptr fs:[0006h],2 mov cx,14848 xor di,di rep stosd ret code ends end start
mov ax,4c00h と int 21hなんかは呪文のように覚えてた記憶がよみがえりました。こんな感じでアセンブラにはまってた時期もありましたが、図書館に行ってもなかなかTurbo Assemblerの本はなくて、MASMの本をみて、Turbo Assembler的に書き換えるということをしてたような気がします。
久しぶりに発掘したプログラマブル関数電卓 SHARP EL-5120。これにはBASICライクなプログラムが組める機能が搭載されており、当時プログラミングしたプログラムがそのまま残っていました。たぶん、94年〜95年頃にかけて作ったプログラムですね。何のこと無い、教科書そのままのプログラムです。
GCM : REAL INPUT A INPUT B T=A*B LABEL G M=A-INT (A/B)*B IF M=0GOTO E A=B B=M GOTO G LABEL E PRINT "GCM PRINT B WAIT PRINT "LCM B=T/B PRINT B
GCM(最大公約数)とLCM(最小公倍数)を求めるためのプログラムです。ユークリッドの互除法ですね。懐かしい。続いては、割ったあまりを求めるだけのプログラム。
MOD : REAL INPUT A INPUT B M=A-INT (A/B) * B PRINT M
シンプルですね。とはいえ、以外と役に立った記憶があります。次は
CRAMEL : REAL PRINT"AX+BY=P PRINT"CX+DY=Q WAIT 1 INPUT A INPUT B INPUT P INPUT C INPUT D INPUT Q Z=AD-BC X=(PD-BQ)/Z Y=(AQ-CP)/Z PRINT X WAIT PRINT Y
これもクラメルの公式通りですね。2元1次方程式の解の公式。次が残っていた最後のプログラムです。素因数分解をするやつですね。
SOINSU : REAL INPUT A C=2 M=√A LABEL K D=A/C IF D=INT DGOTO W C=C+1 IF C>MGOTO F GOTO K LABEL W PRINT C A=A/C IF A=1GOTO E PRINT "HIT KEY WAIT GOTO K LABEL E END LABEL F PRINT A
ちなみに、IF A=1GOTOのようにA=1とGOTOの間にスペースが空かないことに違和感があるかもしれませんが、どうもEL-5120の仕様です。変数は1文字の物しか使えなかったはずです。この電卓を買った当時まだパソコンは所持しておらず、プログラムを組む、ということはとても高貴なことで、制約の多かったEL-5120 BASICでのプログラミングもとても楽しい時間でした。同時期に持っていた、CASIOの関数電卓はぜんぜんグラフィカルではないプログラマブル関数電卓で、しかもメモリも30ステップ分しか無く、途中でうち間違えたらやり直し、というシビアなプログラミング環境でしたが、それでも夢中でプログラムしていたのを思い出します。高校一年の頃です。z80のマイコンボードでハンドアセンブルしたプログラムを16進キーボードで入れてたのは高校三年の頃。今は、その当時と比べれば雲泥の差の環境を持ちながら、うまく使いこなせていないのが少し残念です。がんばらないと!とりあえずコンピュータの数学でも買ってきて久しぶりに勉強しようかな。