2012年5月30日水曜日

続・SVN使いだがGitに乗り換えようと思う

超基本的な変更〜コミットの操作を比較してみよう。
まずはSubversion。


単純に作業コピーを変更したら、svn commitで変更をレポジトリに反映させる。commitする前であれば、svn revertで元に戻せる。

次にGit。

Gitの場合、単に変更しただけのファイルはコミット対象にならない。基本的には、明示的にgit addで変更内容を登録してから、git commitで登録しておいた変更内容をレポジトリに反映させる。まあ、面倒ならgit commit -aでaddしながらcommitもできるけど。
さて、注目すべきはgit resetで過去のバージョンへ戻れる点。Gitではcommitそのものを取り消すことが出来る。どうせ自分専用レポジトリだし、最悪でも取り消せるので、気軽にcommitできることが大きな特長のひとつとも言える。恥ずかしいtypoがずっと残ってしまうSubversionとは実に対照的。

しかし、改めて図示してみると複雑だなぁ。まだブランチも出て来てないのに。

2012年5月29日火曜日

SVN使いだがGitに乗り換えようと思う

SubversionからGitに乗り換えようとしているのだが、その過程をつらつら書いてみる。
まず、軽く使ってみてSubversionと違うと感じたことと、似てると思ったこと。

Subversionと最も異なる点は、何と言ってもGitが分散型であること。
Subversionでは、ある時点でのレポジトリの内容を手元に複製して作業をするのに対して、Gitではレポジトリそのものを手元に複製して作業する。これによって嬉しい点は

  1. レポジトリの操作が速い
  2. ネットワークに繋がっていなくてもレポジトリを操作できる
  3. 気軽にレポジトリを変更できる

こんなところ。1と2は単純に手元にレポジトリがあるが故。分散型ならではの最大のメリットは、やはり3。Subversionは皆で1つの共通レポジトリ突っつくので、例え自分のブランチであっても、あまりいい加減なコミットはためらわれる。また、ブランチの粗製濫造にも抵抗がある。一方、手元に複製してきた自分専用のGitレポジトリなら、どんな駄コミットでも気軽にできるし、実験的なブランチも気兼ねなく作成できる。

バージョンの振られ方は似ているかな。どちらもコミットの単位は、レポジトリに対する何かしらの変更。レポジトリへの変更をコミットすると、全体のバージョンが進む感じ。まあ、細かいことを言えば異なる点は多々あるけれど、どちらもCVSのようにファイル毎に別々にバージョン管理しているわけではない。

実践的な話は明日以降に。

2012年5月28日月曜日

MacにHomebrewでGraphvizをインストール

Graphvizを使いたかったのでインストールしようと試みた。既にHomebrewのformulaがあるようなので、単純に
% brew install graphviz
…あれ? 駄目だ。よく見るとこんなエラーが出ている。
Error: No developer directory found at /Developer. Run /usr/bin/xcode-select to update the developer directory path.
/usr/bin/xcode-selectはbashスクリプト。Usageを見ると-switchオプションがそれっぽいので、こいつで/Applications/Xcode.appを指定してみた。rootでやれと怒られたのでsudo。
% sudo xcode-select -switch /Applications/Xcode.app
さて、改めて
% brew install graphviz
してみると、今度はインストールできた。

ググってみると、どうも/Developerというのは、古いバージョンのXcodeのインストール先だったらしい。まあ、過去の話なら深追いはいいや。

2012年5月27日日曜日

半導体の黄昏

チラシの裏。
おいルネサス。先週、6,000人クビ報道を一応は否定したと思ったら、否定してたのは6,000人じゃ済まないって意味かい。

国内の半導体は、山の端にわずかな回折光を残すのみ、って感じだなぁ。あんまり他人事でもないんだが。

Unicode表

JavaScriptが使えるようなので、おもむろに文字コード表を作ってみた。
一気に作るのは重たいので、クリックした部分を小分けに生成する。


U+0020 - U+007F
U+0080 - U+07FF
U+0800 - U+0FFF
U+1000 - U+17FF
U+1800 - U+1FFF
U+2000 - U+27FF
U+2800 - U+2FFF
U+3000 - U+37FF
U+3800 - U+3FFF
U+4000 - U+47FF
U+4800 - U+4FFF
U+5000 - U+57FF
U+5800 - U+5FFF
U+6000 - U+67FF
U+6800 - U+6FFF
U+7000 - U+77FF
U+7800 - U+7FFF
U+8000 - U+87FF
U+8800 - U+8FFF
U+9000 - U+97FF
U+9800 - U+9FFF
U+A000 - U+A7FF
U+A800 - U+AFFF
U+B000 - U+B7FF
U+B800 - U+BFFF
U+C000 - U+C7FF
U+C800 - U+CFFF
U+D000 - U+D7FF
U+E000 - U+E7FF
U+E800 - U+EFFF
U+F000 - U+F7FF
U+F800 - U+FFFF
U+10000 - U+107FF
U+10800 - U+10FFF
U+11000 - U+117FF
U+11800 - U+11FFF
U+12000 - U+127FF
U+12800 - U+12FFF
U+13000 - U+137FF
U+13800 - U+13FFF
U+14000 - U+147FF
U+14800 - U+14FFF
U+15000 - U+157FF
U+15800 - U+15FFF
U+16000 - U+167FF
U+16800 - U+16FFF
U+17000 - U+177FF
U+17800 - U+17FFF
U+18000 - U+187FF
U+18800 - U+18FFF
U+19000 - U+197FF
U+19800 - U+19FFF
U+1A000 - U+1A7FF
U+1A800 - U+1AFFF
U+1B000 - U+1B7FF
U+1B800 - U+1BFFF
U+1C000 - U+1C7FF
U+1C800 - U+1CFFF
U+1D000 - U+1D7FF
U+1D800 - U+1DFFF
U+1E000 - U+1E7FF
U+1E800 - U+1EFFF
U+1F000 - U+1F7FF
U+1F800 - U+1FFFF
U+20000 - U+207FF
U+20800 - U+20FFF
U+21000 - U+217FF
U+21800 - U+21FFF
U+22000 - U+227FF
U+22800 - U+22FFF
U+23000 - U+237FF
U+23800 - U+23FFF
U+24000 - U+247FF
U+24800 - U+24FFF
U+25000 - U+257FF
U+25800 - U+25FFF
U+26000 - U+267FF
U+26800 - U+26FFF
U+27000 - U+277FF
U+27800 - U+27FFF
U+28000 - U+287FF
U+28800 - U+28FFF
U+29000 - U+297FF
U+29800 - U+29FFF
U+2A000 - U+2A7FF
U+2A800 - U+2AFFF
U+2B000 - U+2B7FF
U+2B800 - U+2BFFF
U+2C000 - U+2C7FF
U+2C800 - U+2CFFF
U+2D000 - U+2D7FF
U+2D800 - U+2DFFF
U+2E000 - U+2E7FF
U+2E800 - U+2EFFF
U+2F000 - U+2F7FF
U+2F800 - U+2FFFF
U+30000 - U+307FF
U+30800 - U+30FFF
U+31000 - U+317FF
U+31800 - U+31FFF
U+32000 - U+327FF
U+32800 - U+32FFF
U+33000 - U+337FF
U+33800 - U+33FFF
U+34000 - U+347FF
U+34800 - U+34FFF
U+35000 - U+357FF
U+35800 - U+35FFF
U+36000 - U+367FF
U+36800 - U+36FFF
U+37000 - U+377FF
U+37800 - U+37FFF
U+38000 - U+387FF
U+38800 - U+38FFF
U+39000 - U+397FF
U+39800 - U+39FFF
U+3A000 - U+3A7FF
U+3A800 - U+3AFFF
U+3B000 - U+3B7FF
U+3B800 - U+3BFFF
U+3C000 - U+3C7FF
U+3C800 - U+3CFFF
U+3D000 - U+3D7FF
U+3D800 - U+3DFFF
U+3E000 - U+3E7FF
U+3E800 - U+3EFFF
U+3F000 - U+3F7FF
U+3F800 - U+3FFFF
U+40000 - U+407FF
U+40800 - U+40FFF
U+41000 - U+417FF
U+41800 - U+41FFF
U+42000 - U+427FF
U+42800 - U+42FFF
U+43000 - U+437FF
U+43800 - U+43FFF
U+44000 - U+447FF
U+44800 - U+44FFF
U+45000 - U+457FF
U+45800 - U+45FFF
U+46000 - U+467FF
U+46800 - U+46FFF
U+47000 - U+477FF
U+47800 - U+47FFF
U+48000 - U+487FF
U+48800 - U+48FFF
U+49000 - U+497FF
U+49800 - U+49FFF
U+4A000 - U+4A7FF
U+4A800 - U+4AFFF
U+4B000 - U+4B7FF
U+4B800 - U+4BFFF
U+4C000 - U+4C7FF
U+4C800 - U+4CFFF
U+4D000 - U+4D7FF
U+4D800 - U+4DFFF
U+4E000 - U+4E7FF
U+4E800 - U+4EFFF
U+4F000 - U+4F7FF
U+4F800 - U+4FFFF
U+50000 - U+507FF
U+50800 - U+50FFF
U+51000 - U+517FF
U+51800 - U+51FFF
U+52000 - U+527FF
U+52800 - U+52FFF
U+53000 - U+537FF
U+53800 - U+53FFF
U+54000 - U+547FF
U+54800 - U+54FFF
U+55000 - U+557FF
U+55800 - U+55FFF
U+56000 - U+567FF
U+56800 - U+56FFF
U+57000 - U+577FF
U+57800 - U+57FFF
U+58000 - U+587FF
U+58800 - U+58FFF
U+59000 - U+597FF
U+59800 - U+59FFF
U+5A000 - U+5A7FF
U+5A800 - U+5AFFF
U+5B000 - U+5B7FF
U+5B800 - U+5BFFF
U+5C000 - U+5C7FF
U+5C800 - U+5CFFF
U+5D000 - U+5D7FF
U+5D800 - U+5DFFF
U+5E000 - U+5E7FF
U+5E800 - U+5EFFF
U+5F000 - U+5F7FF
U+5F800 - U+5FFFF
U+60000 - U+607FF
U+60800 - U+60FFF
U+61000 - U+617FF
U+61800 - U+61FFF
U+62000 - U+627FF
U+62800 - U+62FFF
U+63000 - U+637FF
U+63800 - U+63FFF
U+64000 - U+647FF
U+64800 - U+64FFF
U+65000 - U+657FF
U+65800 - U+65FFF
U+66000 - U+667FF
U+66800 - U+66FFF
U+67000 - U+677FF
U+67800 - U+67FFF
U+68000 - U+687FF
U+68800 - U+68FFF
U+69000 - U+697FF
U+69800 - U+69FFF
U+6A000 - U+6A7FF
U+6A800 - U+6AFFF
U+6B000 - U+6B7FF
U+6B800 - U+6BFFF
U+6C000 - U+6C7FF
U+6C800 - U+6CFFF
U+6D000 - U+6D7FF
U+6D800 - U+6DFFF
U+6E000 - U+6E7FF
U+6E800 - U+6EFFF
U+6F000 - U+6F7FF
U+6F800 - U+6FFFF
U+70000 - U+707FF
U+70800 - U+70FFF
U+71000 - U+717FF
U+71800 - U+71FFF
U+72000 - U+727FF
U+72800 - U+72FFF
U+73000 - U+737FF
U+73800 - U+73FFF
U+74000 - U+747FF
U+74800 - U+74FFF
U+75000 - U+757FF
U+75800 - U+75FFF
U+76000 - U+767FF
U+76800 - U+76FFF
U+77000 - U+777FF
U+77800 - U+77FFF
U+78000 - U+787FF
U+78800 - U+78FFF
U+79000 - U+797FF
U+79800 - U+79FFF
U+7A000 - U+7A7FF
U+7A800 - U+7AFFF
U+7B000 - U+7B7FF
U+7B800 - U+7BFFF
U+7C000 - U+7C7FF
U+7C800 - U+7CFFF
U+7D000 - U+7D7FF
U+7D800 - U+7DFFF
U+7E000 - U+7E7FF
U+7E800 - U+7EFFF
U+7F000 - U+7F7FF
U+7F800 - U+7FFFF
U+80000 - U+807FF
U+80800 - U+80FFF
U+81000 - U+817FF
U+81800 - U+81FFF
U+82000 - U+827FF
U+82800 - U+82FFF
U+83000 - U+837FF
U+83800 - U+83FFF
U+84000 - U+847FF
U+84800 - U+84FFF
U+85000 - U+857FF
U+85800 - U+85FFF
U+86000 - U+867FF
U+86800 - U+86FFF
U+87000 - U+877FF
U+87800 - U+87FFF
U+88000 - U+887FF
U+88800 - U+88FFF
U+89000 - U+897FF
U+89800 - U+89FFF
U+8A000 - U+8A7FF
U+8A800 - U+8AFFF
U+8B000 - U+8B7FF
U+8B800 - U+8BFFF
U+8C000 - U+8C7FF
U+8C800 - U+8CFFF
U+8D000 - U+8D7FF
U+8D800 - U+8DFFF
U+8E000 - U+8E7FF
U+8E800 - U+8EFFF
U+8F000 - U+8F7FF
U+8F800 - U+8FFFF
U+90000 - U+907FF
U+90800 - U+90FFF
U+91000 - U+917FF
U+91800 - U+91FFF
U+92000 - U+927FF
U+92800 - U+92FFF
U+93000 - U+937FF
U+93800 - U+93FFF
U+94000 - U+947FF
U+94800 - U+94FFF
U+95000 - U+957FF
U+95800 - U+95FFF
U+96000 - U+967FF
U+96800 - U+96FFF
U+97000 - U+977FF
U+97800 - U+97FFF
U+98000 - U+987FF
U+98800 - U+98FFF
U+99000 - U+997FF
U+99800 - U+99FFF
U+9A000 - U+9A7FF
U+9A800 - U+9AFFF
U+9B000 - U+9B7FF
U+9B800 - U+9BFFF
U+9C000 - U+9C7FF
U+9C800 - U+9CFFF
U+9D000 - U+9D7FF
U+9D800 - U+9DFFF
U+9E000 - U+9E7FF
U+9E800 - U+9EFFF
U+9F000 - U+9F7FF
U+9F800 - U+9FFFF
U+A0000 - U+A07FF
U+A0800 - U+A0FFF
U+A1000 - U+A17FF
U+A1800 - U+A1FFF
U+A2000 - U+A27FF
U+A2800 - U+A2FFF
U+A3000 - U+A37FF
U+A3800 - U+A3FFF
U+A4000 - U+A47FF
U+A4800 - U+A4FFF
U+A5000 - U+A57FF
U+A5800 - U+A5FFF
U+A6000 - U+A67FF
U+A6800 - U+A6FFF
U+A7000 - U+A77FF
U+A7800 - U+A7FFF
U+A8000 - U+A87FF
U+A8800 - U+A8FFF
U+A9000 - U+A97FF
U+A9800 - U+A9FFF
U+AA000 - U+AA7FF
U+AA800 - U+AAFFF
U+AB000 - U+AB7FF
U+AB800 - U+ABFFF
U+AC000 - U+AC7FF
U+AC800 - U+ACFFF
U+AD000 - U+AD7FF
U+AD800 - U+ADFFF
U+AE000 - U+AE7FF
U+AE800 - U+AEFFF
U+AF000 - U+AF7FF
U+AF800 - U+AFFFF
U+B0000 - U+B07FF
U+B0800 - U+B0FFF
U+B1000 - U+B17FF
U+B1800 - U+B1FFF
U+B2000 - U+B27FF
U+B2800 - U+B2FFF
U+B3000 - U+B37FF
U+B3800 - U+B3FFF
U+B4000 - U+B47FF
U+B4800 - U+B4FFF
U+B5000 - U+B57FF
U+B5800 - U+B5FFF
U+B6000 - U+B67FF
U+B6800 - U+B6FFF
U+B7000 - U+B77FF
U+B7800 - U+B7FFF
U+B8000 - U+B87FF
U+B8800 - U+B8FFF
U+B9000 - U+B97FF
U+B9800 - U+B9FFF
U+BA000 - U+BA7FF
U+BA800 - U+BAFFF
U+BB000 - U+BB7FF
U+BB800 - U+BBFFF
U+BC000 - U+BC7FF
U+BC800 - U+BCFFF
U+BD000 - U+BD7FF
U+BD800 - U+BDFFF
U+BE000 - U+BE7FF
U+BE800 - U+BEFFF
U+BF000 - U+BF7FF
U+BF800 - U+BFFFF
U+C0000 - U+C07FF
U+C0800 - U+C0FFF
U+C1000 - U+C17FF
U+C1800 - U+C1FFF
U+C2000 - U+C27FF
U+C2800 - U+C2FFF
U+C3000 - U+C37FF
U+C3800 - U+C3FFF
U+C4000 - U+C47FF
U+C4800 - U+C4FFF
U+C5000 - U+C57FF
U+C5800 - U+C5FFF
U+C6000 - U+C67FF
U+C6800 - U+C6FFF
U+C7000 - U+C77FF
U+C7800 - U+C7FFF
U+C8000 - U+C87FF
U+C8800 - U+C8FFF
U+C9000 - U+C97FF
U+C9800 - U+C9FFF
U+CA000 - U+CA7FF
U+CA800 - U+CAFFF
U+CB000 - U+CB7FF
U+CB800 - U+CBFFF
U+CC000 - U+CC7FF
U+CC800 - U+CCFFF
U+CD000 - U+CD7FF
U+CD800 - U+CDFFF
U+CE000 - U+CE7FF
U+CE800 - U+CEFFF
U+CF000 - U+CF7FF
U+CF800 - U+CFFFF
U+D0000 - U+D07FF
U+D0800 - U+D0FFF
U+D1000 - U+D17FF
U+D1800 - U+D1FFF
U+D2000 - U+D27FF
U+D2800 - U+D2FFF
U+D3000 - U+D37FF
U+D3800 - U+D3FFF
U+D4000 - U+D47FF
U+D4800 - U+D4FFF
U+D5000 - U+D57FF
U+D5800 - U+D5FFF
U+D6000 - U+D67FF
U+D6800 - U+D6FFF
U+D7000 - U+D77FF
U+D7800 - U+D7FFF
U+D8000 - U+D87FF
U+D8800 - U+D8FFF
U+D9000 - U+D97FF
U+D9800 - U+D9FFF
U+DA000 - U+DA7FF
U+DA800 - U+DAFFF
U+DB000 - U+DB7FF
U+DB800 - U+DBFFF
U+DC000 - U+DC7FF
U+DC800 - U+DCFFF
U+DD000 - U+DD7FF
U+DD800 - U+DDFFF
U+DE000 - U+DE7FF
U+DE800 - U+DEFFF
U+DF000 - U+DF7FF
U+DF800 - U+DFFFF
U+E0000 - U+E07FF
U+E0800 - U+E0FFF
U+E1000 - U+E17FF
U+E1800 - U+E1FFF
U+E2000 - U+E27FF
U+E2800 - U+E2FFF
U+E3000 - U+E37FF
U+E3800 - U+E3FFF
U+E4000 - U+E47FF
U+E4800 - U+E4FFF
U+E5000 - U+E57FF
U+E5800 - U+E5FFF
U+E6000 - U+E67FF
U+E6800 - U+E6FFF
U+E7000 - U+E77FF
U+E7800 - U+E7FFF
U+E8000 - U+E87FF
U+E8800 - U+E8FFF
U+E9000 - U+E97FF
U+E9800 - U+E9FFF
U+EA000 - U+EA7FF
U+EA800 - U+EAFFF
U+EB000 - U+EB7FF
U+EB800 - U+EBFFF
U+EC000 - U+EC7FF
U+EC800 - U+ECFFF
U+ED000 - U+ED7FF
U+ED800 - U+EDFFF
U+EE000 - U+EE7FF
U+EE800 - U+EEFFF
U+EF000 - U+EF7FF
U+EF800 - U+EFFFF
U+F0000 - U+F07FF
U+F0800 - U+F0FFF
U+F1000 - U+F17FF
U+F1800 - U+F1FFF
U+F2000 - U+F27FF
U+F2800 - U+F2FFF
U+F3000 - U+F37FF
U+F3800 - U+F3FFF
U+F4000 - U+F47FF
U+F4800 - U+F4FFF
U+F5000 - U+F57FF
U+F5800 - U+F5FFF
U+F6000 - U+F67FF
U+F6800 - U+F6FFF
U+F7000 - U+F77FF
U+F7800 - U+F7FFF
U+F8000 - U+F87FF
U+F8800 - U+F8FFF
U+F9000 - U+F97FF
U+F9800 - U+F9FFF
U+FA000 - U+FA7FF
U+FA800 - U+FAFFF
U+FB000 - U+FB7FF
U+FB800 - U+FBFFF
U+FC000 - U+FC7FF
U+FC800 - U+FCFFF
U+FD000 - U+FD7FF
U+FD800 - U+FDFFF
U+FE000 - U+FE7FF
U+FE800 - U+FEFFF
U+FF000 - U+FF7FF
U+FF800 - U+FFFFF
U+100000 - U+1007FF
U+100800 - U+100FFF
U+101000 - U+1017FF
U+101800 - U+101FFF
U+102000 - U+1027FF
U+102800 - U+102FFF
U+103000 - U+1037FF
U+103800 - U+103FFF
U+104000 - U+1047FF
U+104800 - U+104FFF
U+105000 - U+1057FF
U+105800 - U+105FFF
U+106000 - U+1067FF
U+106800 - U+106FFF
U+107000 - U+1077FF
U+107800 - U+107FFF
U+108000 - U+1087FF
U+108800 - U+108FFF
U+109000 - U+1097FF
U+109800 - U+109FFF
U+10A000 - U+10A7FF
U+10A800 - U+10AFFF
U+10B000 - U+10B7FF
U+10B800 - U+10BFFF
U+10C000 - U+10C7FF
U+10C800 - U+10CFFF
U+10D000 - U+10D7FF
U+10D800 - U+10DFFF
U+10E000 - U+10E7FF
U+10E800 - U+10EFFF
U+10F000 - U+10F7FF
U+10F800 - U+10FFFF

2012年5月26日土曜日

続・BloggerでJavaScript

ここへのポストでJavaScriptは使えないことはないようだけれど、どうもイベント周りに一癖ありそうな感じ。



何も起きない…。

と思いつつポストしたら動いた。動かないのはプレビュー中だけか。

2012年5月24日木曜日

BloggerでJavaScript

ここへのポストでJavaScriptは使えるのか、ちょっと試してみる。

2012年5月23日水曜日

CMakeの情報源

CMakeのリファレンス的なものを探していたところ、実は--help系のオプションが充実しているみたい。とりあえず
% cmake --help-html [ファイル名]
とすると、指定した名前のファイルにマニュアルが保存される。ぱっと見、内容はmanと同じかな? 他の--help系オプションの説明もこのマニュアルに含まれているので、見やすいところにおいておくと便利かも。
とりあえずKindleに突っ込んでおこう。

2012年5月22日火曜日

CMakeを使おう

CMakeを使いたい。が、今一使い方が分かってないので、この辺のドキュメントをきちんと読もうと思う。


日本語の情報が少ないなぁ…。

ろくな大人になりませんでした

チラシの裏。

最近、月曜日のキン肉マンが楽しみで仕方ないんだが、今週はアトランティス格好良すぎ。既にろくでもない大人になってるから、悪魔なんぞを応援してしまうわ。
それにしても、結果は見たいけど怖くてページめくりをためらったなんて、いつ以来のことだろうか? 電子化されても、漫画はやっぱりページの概念があった方がいいなぁ。

2012年5月20日日曜日

いつもの逆転負け

チラシの裏。
また西口の勝利が消されてしまった。終盤に1点リードって、もはや負けフラグなんじゃないか?
大沼や小野寺がポコスカやられてた頃の方が、まだましだった気がする…。

2012年5月19日土曜日

Apple Magic Trackpadレビュー

Magic Trackpadを買ったので、軽くレビューしてみる。
その大きさは単3電池と比較してみれば一目瞭然。そしてその効果は絶大。よくあるノートPCの狭いトラックパッドだと、狭い机でマウスを使うように、何度も指を浮かせて撫で直さなければならないところが、こいつなら一撫ででかなり思い通りに操作できる。どんなに速度や加速度を調整するより、物理的に広くするという単純明快なアプローチは実に効果的だった。
購入前はApple Wireless Keyboardにサイズを合わせるなら最初から一体化してくれと思ってたけど、使ってみると別々の方が自由に配置できて快適。ちなみに、Magic Trackpadは上下逆に置いた方が手首の角度が楽だと思うのだが、残念ながら上下左右反転するオプションは見当たらない。

ジェスチャは非常に快適。今までそれほど良いとは感じていなかったMission ControlやLaunchpadが、サッと呼び出せるようになって俄然便利になった。一時物議をかもしたスクロール方向の設定はiOS風にしてみたが、これはすぐ慣れる。タップでクリックは過敏すぎると言う評判を聞いていたけれど、個人的にはそう感じなかった。
なお、4本指ジェスチャは5本指でやっても認識される。なので、4本指を広げたりすぼめたりするジェスチャは、わざわざ小指を立てなくても大丈夫。あと、爪は切った方がいい。

ドラッグの設定は分かりづらい。ダブルタップの2回目のタップを離さずに引きずる操作が、デフォルトでは効かない。そしてその設定は
システム環境設定>トラックパッド
にはなく、
システム環境設定>ユニバーサルアクセス>マウスとトラックパッド>トラックパッドオプション
でドラッグをチェックすることで有効になる。

なお、これらの設定が有効になるのはログイン後。ログインする前から使えるのは、表面を撫でてカーソルを動かす機能と左右の物理ボタンのみ。それまでは、シングルタップによる左クリックも使えないので注意。

スリープ中は左右の物理ボタンのみ有効。言い換えると、ただ表面に触れるだけではスリープは解除されない。せっかくスリープさせた直後に、うっかりマウスにぶつかって起こしてしまうというミスは激減しそう。

マイナスは右ドラッグができない点。Chromeでマウスジェスチャができなくなってしまった。まあ、進む/戻るはページ送りのジェスチャで対応できるようなので、タブの開閉はキーボードショートカットで行こう。

Gaucheで正規表現を使う

LLなら正規表現でパターンマッチや置換ができるのは当たり前だと思ってたら、Schemeの言語仕様に正規表現は含まれていないそうな。ただ、Schemeの一実装であるGaucheでは正規表現を使える。網羅は本家リファレンスに任せて、とりあえず使いそうな部分を適当に抜き出し。

正規表現オブジェクトは
#/〜/
と正規表現リテラルで記述するか、
(string->regexp "〜")
と文字列からの変換することで作成できる。

パターンマッチは
(rxmatch 正規表現 文字列)
または
(正規表現 文字列)
でテストできる。マッチしたら<regmatch>オブジェクト、しなかったら#f。
<regmatch>オブジェクトから一致した部分文字列等を取り出すにはrxmatch-*系の関数が使えるが、正規表現オブジェクトと同様に、<regmatch>オブジェクトも関数のように使用可能。
Rubyでいうところの以下のような処理は
if /(\w+)=(\w+)/ ~= string
  name = $1
  value = $2
end
Gaucheでは以下のように書ける。
(let
  ((match (#/(\w+)=(\w+)/ string)))
  (if match (begin
    (set! name (match 1))
    (set! value (match 2)))))

置換は
(regexp-replace 正規表現 元の文字列 置換文字列)
(regexp-replace-all 正規表現 元の文字列 置換文字列)

でできる。regexp-replaceは最初にマッチした部分のみを置換。regexp-replace-allは全てのマッチした部分を置換。置換文字列の代わりに1引数のプロシージャを与えると、マッチした部分を単純に文字列で置換する代わりに、<regmatch>オブジェクトを引数としてそのプロシージャを呼び出した結果で置換する。

2012年5月17日木曜日

MacでApache

MacでちょっとApacheを使いたいと思い、インストール事例を軽くググってみたところ、そもそも標準でインストールされているものなのね。手元のLionでは
システム環境設定>共有>Web共有
を有効にしてやるだけで、動き出した。
UserDirはデフォルトで~/Sitesという名前。パーソナルWebサイトを作成してやると、適当なサンプルを作ってくれる。

ああ、なんて楽チンなんだ。

2012年5月16日水曜日

C++でnullポインタをdelete

C++で0かもしれないポインタをdeleteしているコードを見かけたので、これはまずいだろうと思ったらそんなことはないそうだ。
そうとも知らず、今日までずっとゼロテストしてからdeleteしてた…。

2012年5月15日火曜日

続・Unicodeで天下統一

Unicodeの話その2。

UTF-32は、Unicodeの1文字を32ビット固定長の値で表すもの。これはまあ、そのまんま。
UTF-32もBOM(U+FEFF)でエンディアンの判別はできるが、そもそも、ファイルやネットワークでのやり取りはUTF-32に不向き。サイズは食うが扱いは容易なUTF-32の得意なポジションは、多言語対応プログラムの内部文字コード。

UTF-8は、Unicodeの1文字を下の表のように1~4バイトの可変長バイト列で表すもの。

UnicodeUTF-8(bin)
U+0000~U+007F0xxxxxxx
U+0080~U+07FF110xxxxx 10xxxxxx
U+0800~U+FFFF1110xxxx 10xxxxxx 10xxxxxx
U+10000~U+1FFFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

U+007F以下はそのまま1バイトで符号化されるので、この部分はASCIIコードと互換性がある。
U+0080以上はマルチバイトで符号化されるが、マルチバイト文字の各バイトは全てMSBが1。なので、Shift_JISのようにマルチバイト文字の一部がASCIIの他の文字に見えてしまうことはない。
マルチバイト文字の1バイト目は、その文字が何バイトなのかも表している。具体的には、最上位ビットから並んでいる1の数が、その文字のバイト数となる。なお、このルールだと5バイト以上のバイト列も表現できるが、Unicodeでそれは不正という扱い。

UTF-8はバイト列なので、エンディアンの問題はない。

2012年5月14日月曜日

Unicodeで天下統一


Unicodeの話その1。

昔は国や地域毎に文字コードがあるのが当たり前だった。基本となる英語圏のASCIIコードに対して、日本人は独自に日本語の文字付け足し、中国人は独自に中国語の文字を付け足し…。そんな互換性も何もない状況を打破しようと、全世界共通の文字コードを作ろうとして生み出されたのがUnicode。世界共通とは言ってもエスペラント語のような崇高かつ無謀な話ではなく、単純に世界中のあらゆる文字に文字コードを与えるだけの話。…だったのだが、初期の判断ミスでやや面倒なことになっている。

当初は単純に16ビットの文字コード(U+0000~U+FFFF)を割り振ろうと考えていたらしい。2バイトの固定長なら、容量は食うけれど扱いは簡単だ。しかし、65536文字ではとても世界中の文字を網羅できなかったので、後に21ビット(~U+10FFFF)に拡張するはめになった。この歴史的経緯が思いっきり現れているのがUTF-16。

UTF-16は、Unicodeの1文字を16ビット固定長の値で表そうとしたもの。が、前述の通り16ビットでは足りなかったので、結局Unicodeの1文字を16ビットまたは32ビットで表すものとなった。

UnicodeUTF-16
U+0000~U+D7FF0000h~D7FFh
U+D800~U+DFFFなし
U+E000~U+FFFFE000h~FFFFh
U+10000~U+10FFFFサロゲートペア

U+0000~U+D7FFとU+E000~U+FFFFはそのままなのでいいとして、問題は17ビット以上のU+10000~U+10FFFFの部分。この範囲の文字は、D800h~DFFFhの16ビットデータを2つ組み合わせて表現する。この2ワードの組み合わせは、サロゲートペアと呼ばれている。U+10000~U+10FFFFからサロゲートペアへの変換をRubyでクドく書くとこんな感じ。
def to_surrogate_pair(u)
  raise “out of range” if u < 0x10000 or u > 0x10ffff
  upper_5bits = u >> 16
  middle_6bits = (u >> 10) & 0x3f
  lower_10bits = u & 0x3ff
  upper_4bits = upper_5bits - 1
  first_word = (0b110110 << 10) | (upper_4bits << 6) | middle_6bits
  second_word = (0b110111 << 10) | lower_10bits
  [first_word, second_word]
end
上位5ビットから1を引いて4ビットにしているが、上位5ビットは最大でも10000bなので、1を引けば必ず4ビットに収まる。

ワード長が多バイトになれば無視できないのがエンディアンの問題。読み手と書き手であらかじめ意思統一できていれば問題ないが、文字列の先頭にBOM(Byte Order Mark)という特殊な文字を入れて、エンディアンを明示する方法もある。BOMの正体はU+FEFF。そしてU+FFFEは未定義なので、存在しないはず。にも関わらず先頭にU+FFFEがあるように見えたなら、それはエンディアンが逆だと分かるというわけ。
UTF-16と言っても、

  • あらかじめビッグエンディアンと約束しておく
  • あらかじめリトルッグエンディアンと約束しておく
  • エンディアンの約束はせず、BOMを付けてビッグエンディアン
  • エンディアンの約束はせず、BOMを付けてリトルエンディアン
  • エンディアンの約束はせず、BOMを付けずにビッグエンディアン

の5パターンがあるので注意。

UTF-16の文字列をバイト列として見ると、いたるところに00hが入りうる。したがって、既存のコードをUTF-16対応させる場合、C言語のstr系関数は使えなくなる。

今季初勝利

チラシの裏。
ようやく西口に勝ちが付いたっ! 200勝はまだちょっと先だけど、何とか届いてほしいなぁ。あと、1回くらいノーヒットノーランも。

涌井もまさか、勝ちより先にセーブが付くとは。

2012年5月12日土曜日

Scheme第20歩


遅延評価の話その3。

まずは前回の訂正。
遅延リストを使った方のバージョンなら実際に大きなリストが作られることはないと書いたけど、大きなリストのようなものはできてしまう。…と言うのも、遅延リストを変数に取っておいているので、使用済みプロミスがGCに回収してもらえない。評価済みプロミスは値をキャッシュしているので、結局読み終えた行でも最後まで残ってしまう。ああ失敗。

さて、Scheme学習シリーズの最後は、遅延リストの発明済み車輪である、Gaucheのutil.streamを軽く転がしてみよう。Let's
(use util.stream)

まず名前。これまで遅延リストと呼んでいたものは、util.streamライブラリではストリーム(stream)と呼ばれている。そして、このライブラリが提供する名前にはstream-というプリフィックスが付けられている。

ストリームを操作する関数は、リストを操作する関数のストリーム版が大体用意されているので、mapやfilterを使う感覚でstream-mapやstream-filterを使えばよい。

ストリームを作成する関数も色々用意されている。ファイル読み出しにはport->streamを使えばよい。前回のlazy-read-linesに相当することは
(port->stream i read-line)
これだけで実現できる。ちなみにread-lineを省略すると、read-charを用いて1文字ずつ読み出すストリームになる。
簡単な等差数列ならstream-iotaで作成可能。
(stream-iota 5) ; 0, 1, 2, 3, 4
(stream-iota -1) ; 0, 1, 2, 3, 4, ...
(stream-iota -1 10) ; 0, 10, 20, 30, 40, ...
(stream-iota -1 1 2) ; 1, 3, 5, 7, 9, ...
第1引数がストリームの長さで、負の値を指定すると無限長となる。第2引数は初期値で、省略すると0。第3引数がステップで、省略すると1。
stream-tabulateを使えば、もう少し複雑なものも作れる。
(stream-tabulate 5 sqrt)) ; 0, 1, 1.414, 1.732, 2
(stream-tabulate -1 (lambda (x) (* x x))) ; 0, 1, 4, 9, 16, ...
第1引数がストリームの長さで、負の値を指定すると無限長。第2引数がストリームの値を生成する関数で、引数は0, 1, 2, ...とストリームを読み進めるに連れてインクリメントされていく。

util.streamの実装は*load-path*のどこかにutil/stream.scmとして置いてあるので、眺めてみると参考になる。

2012年5月11日金曜日

いつの日かテレビ

チラシの裏。

いつの間にテレビが終了してしまうそうな。CMが豆しばばっかりだったし、単純にペイしなかったのだろうか? 結構楽しみにしてたんだけど、残念。
有料でもいいから、何かしらの形で3D猫動画を配信してくれないかな〜。

2012年5月10日木曜日

Scheme第19歩


遅延評価の話その2。

せっかく関数型言語を使うからには、カッコよく高階関数を使いたい。特にmapのようなリストを操作する関数を上手く使うと、実に簡潔に記述できることが多い。その一方で、問題の規模が大きくなると、大量のデータをリストで丸抱えするのは性能面で望ましくない。

そんな理想と現実の狭間に立たされたとき、仕方なく理想を捨てるしかないのだろうか? いやいや、遅延評価が理想と現実の架け橋となってくれるのだ。

例えば、入力ポートiから読み込んだ各行を()で括って出力ポートoへ書き出すことを考えてみよう。まずは遅延評価を使わないバージョン。
(define (read-lines i)
  (let ((lines '()) (tail '()))
    (let loop ((line (read-line i)))
      (cond
        ((eof-object? line)
          lines)
        ((null? lines)
          (set! lines (cons line '()))
          (set! tail lines)
          (loop (read-line i)))
        (else
          (set-cdr! tail (cons line '()))
          (set! tail (cdr tail))
          (loop (read-line i)))))))
; main
(define all-lines (read-lines i))
(for-each
  (lambda (line) (display line o) (newline o))
  (map
    (lambda (line) (string-append “(“ line “)”))
    all-lines))
read-linesは読み込んだ全ての行をリストの形で返す関数。それを使って取得したall-linesに対して、mapを使って()で括ってからfor-eachで出力しているのだが、このコードの問題は入力を読み尽してから文字列操作や出力を行う点。入力が100万行あれば100万行の入力と100万行のmap結果を蓄えるメモリが必要になるし、入力元がユーザーのキーボードであれば、CTRL-D等で入力を止めるまでレスポンスが見えない。

それではいよいよ、遅延評価を使ってみよう。
(define (lazy-read-lines i)
  (delay
    (let ((line (read-line i)))
      (if (eof-object? line)
        '()
        (cons line (lazy-read-lines i))))))
(define (lazy-null? p) (null? (force p)))
(define (lazy-car p) (car (force p)))
(define (lazy-cdr p) (cdr (force p)))
(define (lazy-map f ls)
  (delay
    (if (lazy-null? ls)
      '()
      (cons (f (lazy-car ls)) (lazy-map f (lazy-cdr ls))))))
(define (lazy-for-each f ls)
  (let loop ((p ls))
    (unless (lazy-null? p)
      (f (lazy-car p))
      (loop (lazy-cdr p)))))
; main
(define all-lines (lazy-read-lines i))
(lazy-for-each
  (lambda (line) (display line o) (newline o))
  (lazy-map
    (lambda (line) (string-append "(" line ")"))
    all-lines))
メインの処理は、記述上は遅延評価版の関数(lazy-xx)を使うよう変更しただけに見えるが、実際には大きく異なる。lazy-xxでは単純なリストの代わりに、forceするとリストのように見えるプロミスを扱うのだ。以下、このプロミスを便宜上、遅延リストと呼ぶ。

遅延リストは、forceすると空リストかドット対になるプロミスである。ドット対だった場合、そのcarはリストの要素、cdrは残りの要素を表す遅延リストである。この遅延リストを使えば、forceしてリストの要素を参照してみるまで、その要素の評価を後回しにすることができる。今回の例では

  1. displayするために、map結果の要素が1つ必要になる
  2. map結果の要素を1つ求めるために、all-linesの要素が1つ必要になる
  3. all-linesの要素を1つ求めるために、read-lineを1回実行する
  4. read-line結果を()で括る
  5. ()で括ったread-line結果をdisplayする

と、実際に大きなリストが作られることはなく、1行ずつ処理されるのだ。Marvelous!

当たり前の話ではあるが、本質的に後回しにできない処理は遅延評価でも改善できない。例えば全ての入力行をクイックソートして出力する場合、どう足掻いても、まずは全ての行を読むしかない。

2012年5月9日水曜日

Scheme第18歩


さて、Scheme学習シリーズもそろそろ延べ3週間なので、ぼちぼち締めに入ろう。最後は遅延評価の話。

多くのプログラミング言語は、基本的に前から順にプログラムを実行していく。Schemeも例外ではなく、例えば
(clip x (+ y 10) z))
という記述があったら(そしてclipがスペシャルフォームではない通常の関数であったら)、

  1. xを評価
  2. yを評価
  3. 10を評価
  4. y, 10を引数として+を呼び出し((+ y 10)を評価)
  5. zを評価
  6. x, (+ y 10), zを引数としてclipを呼び出し((clip x (+ y 10) z)を評価)

という順番で評価していく。

ここで、例えばclipが以下のように定義されているとする。
(define clip (lambda (min max v)
  (cond
    ((<= v min) min)
    ((>= v max) max)
    (else v)
  )
))
まあ、何の変哲もないクリッピングなのだが、ここで前出の
(clip x (+ y 10) z))
をx=0, y=100, z=-10として評価することを考えてみる。すると、condの最初の条件が早々に満たされるので、maxの値(+ y 10)は折角計算したにも関わらず、結果的には使われない。

こんなとき、値が必要になるまで式の評価を後回しにしようというのが遅延評価の考え方。必要にならなければ無期限後回しとなるので、結果として無駄な計算を行わずに済むというわけ。

Schemeで遅延評価を行うには、delayとforceを使う。
(define x 100)
(define p (delay (+ x 1))
(set! x 1000)
(print (force p)) ; 1001
上の例では(+ x 1)の評価をdelayで後回しにして、printする際にforceで評価させている。この例のpのようにdelayで遅らせた式を表現するオブジェクトのことを、Schemeではプロミスと呼ぶ。
注意すべきは、プロミスは評価値をキャッシュしている点。上記のコードに続いて
(set! x 2000)
(print (force p)) ; 1001
と実行しても、既に1度forceされているプロミスpは、自身の評価値が1001であると記憶しているので、改めて(+ x 1)を評価して2001となることはない。

なお、プロミスではない式をforceすると、単純にその場でその式を評価したのと同じ値となる。よって、
(let ((p xx)) (if (promiss? p) (force p) p))
のようにプロミスかどうかを判定せずとも
(force xx)
でよい。

…締め切れてないので、もうちょっと続く。

2012年5月8日火曜日

Scheme第17歩


モジュールの話。
今更言うまでもなくモジュール化はコードの再利用に有効だが、Gaucheにもその機能は備わっている。…と言うか、実は全ての定義は何かしらのモジュールに属していて、基本的には同じモジュールに属するもの同士が互いに見える。今までグローバルな定義として扱ってきた
(define var 10)
等は、厳密にはuserという名前のモジュールに属する。

モジュールの定義は
(define-module モジュール名 モジュール本体...)
とする。
定義したからには当然使いたいところだが、モジュール使い方は大きく分けて2種類ある。1つはカレントモジュールの変更。もう1つは外部モジュールの参照。

カレントモジュールを変更する。すなわち、使いたいモジュールの中に入ってしまえば、これまで居たモジュールの内部が見えなくなる代わりに、新たに入るモジュールの内部が見えるようになる。
(select-module モジュール名)
とすると、それ以降(からファイル終端まで)のフォームは指定したモジュール内で評価される。また、
(with-module モジュール名 フォーム...)
とすると、カレントモジュールを一時的に変更して、指定したフォームを評価する。

カレントモジュールを変更することなく参照できる外部モジュールを参照する方法の1つにimportがある。
(define-module foo
  (export visible1 visible2)
  (define visible1 100)
  (define visible2 200)
  (define invisible 300)
)
(import foo)
(print visible1 visible2)
(print invisible) ; エラー
このようにモジュールをimportすると、exportされているシンボルのみがカレントモジュールから見えるようになる。importにオプションを与えれば、細かい見え方の制御も可能。
なお、モジュールの定義を同名のファイルに記述しておけば
(require “foo”)
(import foo)
とする代わりに、useマクロを利用して
(use foo)
と簡潔に記述できる。

exportを使うと、外部モジュールをより密接にカレントモジュールに取り込む。
(define-module bar
  (export exported)
  (define exported 1000)
  (define not-exported 2000)
)
(define-module baz
  (extend bar)
  (export disp)
  (define disp (lambda () (print exported not-exported)))
)
(import baz)
(print exported)
(disp)
(print not-exported) ; エラー
例えば上記の例では、barをextendしたbazの中では、barのexportedとnot-exportedの両方が見える。またbazもexportedをexportしたことになる。しかし、not-exportedはexportされていないので、bazをimportしただけのカレントモジュールからは見えない。
exportするモジュールは複数指定可能。また、exportはuseと同様、指定したモジュールのファイルがまだロードされていなかったらロードする。

ChromeでCookie制御

ChromeのVanilla Cookie Managerが便利だ。
ホワイトリストに無いサイトのCookieを拒絶する拡張はFirefoxで色々使って来たが、このChrome拡張は拒絶するのではなく、受け入れた振りをしながら後で削除する。Cookieを有効にしろと言われて利便性を損なうようなことは無く、それでいて素性の知れないCookieをやんわりと断るこの方法は、なかなかよく出来てるなぁ。

2012年5月6日日曜日

Scheme第16歩

別ファイルにあるコードを利用する話。

誰かが書いたコードを利用して新しいプログラムを作りたいとき、
(load ファイル名)
が最も基本的な手段。これで、指定したファイルからSchemeのコードを読み出して評価する。サフィックス.scmは省略可能。

loadより高級なのがrequire。
(require フィーチャー名)
とすると、指定されたフィーチャーがまだロードされていなければロードする。フィーチャー名というのは、ファイル名からサフィックスを取り除いたもの。

Gaucheでロード時の探索先ディレクトリは

  • 環境変数GAUCHE_LOAD_PATH
  • goshの-Iオプション
  • (add-load-path ディレクトリ名)

で追加できる。また、現在の探索ディレクトリは変数*load-path*で確認できる。

Scheme第15歩

だらだらやってるうちに3週目に入ってしまったので、そろそろ実用的なところへの着地を考える。コマンドライン引数の話。

ちょいとしたスクリプトを書くにあたって、コマンドライン引数を使いたい場合は多い。そこで、Gaucheで引数を取得する例。
#/usr/bin/env gosh
(define main
  (lambda (args)
    (let loop
      ((as args))
      (unless (null? as)
        (print (car as))
        (loop (cdr as))
      )
    )
  )
)
mainという名前のプロシージャは特別で、存在すればコマンドライン引数のリストとともに呼び出される。上のスクリプトを適当なファイルに保存して、実行可能属性を付けて実行すると、goshのコマンドライン引数。すなわち、スクリプトファイル名と(存在すれば)その引数が1行ずつ表示される。
なお、mainの評価値が整数なら、それがgosh自身の終了ステータスとなる。

インデントの付け方に、未だに迷ってる…。

2012年5月5日土曜日

Google Driveがやって来た

そういえば、結構前にGoogle Driveが使えるようになった。
Mac用アプリの出来が駄目駄目らしいので、Dropbox的な自動同期は様子見中。Webアプリとしては…、まさかGoogle Docsが統合されてしまったの?

いや、Mac用アプリがきちんと動くようになれば、同期しているファイルを普通にローカルでもWebアプリでも編集できるってのは、機能的には大歓迎なんだけどね。問題はdrive.google.comがブロックされたらGoogle Docsまで制限されそうなところ。文書やスプレッドシートのWebアプリは引き続きdocs.google.comで提供されているようなので、Google Driveがブロックされても、とりあえずはドキュメント一覧が見えなくなるだけ、か?

2012年5月4日金曜日

Scheme第14歩

継続の話その2。

C言語等のスタックに慣れていると、ぎょっとするようなことができてしまう。
(define cont 0)
(let loop
  ((i 0) (sum 0))
  (if (> sum 1000)
    (begin (call/cc (lambda (c) (set! cont c))) (print "i=" i ", sum=" sum))
    (loop (+ i 1) (+ sum i))
  )
)
このコード。call/ccを除くと、0+1+2+3+...と1000を越えるまで足し込んでいるだけ。また、call/ccで実行されるプロシージャの中では、渡された継続cを変数contに取っておいてるだけで、継続自体の呼び出しはしていない。したがって、このコードを実行してもcall/cc部では特に何も起きずに、
i=47, sum=1035
と表示される。まあ、ここまではいいとして、問題はここから。
上のコードを実行すると、変数contにはprintする直前の継続が収められる。そこで
(cont)
と継続を呼び出すと、 何と再び
i=47, sum=1035
と表示されてしまう。

…物心ついたときからクロージャがあるような世代には当たり前かもしれないが、古い言語の場合、ある変数のスコープから抜けたとき、その変数のために割り当てていたメモリはさっさと明け渡される。例えばC言語でsetjmp/longjmpを使って同じようなことをやろうとしても、iやsumはループを抜けた時点で電子の藻屑になっている可能性がある。そのため、仮にlongjmpで戻っても同じ値が表示される保証が全くない。

1度スコープを抜けても変数が消滅しないからこそ、Schemeでは継続を使って縦横無尽に飛び回れるのだ。

2012年5月2日水曜日

Scheme第13歩

継続の話その1。
call-with-current-continuation、略してcall/ccの基本的な使い方。
(call/cc プロシージャ)
こうすると、引数として与えられたプロシージャが実行される。…だけなら話は簡単なのだが、それではwith-current-continuationが黙っちゃいない。プロシージャを実行するとき、その引数としてその時点での継続というものが渡される。

継続とは、ある瞬間の実行環境のスナップショットのようなもの。大雑把な言い方をすると、C言語のsetjmpで取得する情報をもうちょっとリッチにしたようなもの。Cの場合、setjmpで取得した情報を使うにはlongjmpという専用の関数を使うけれど、Schemeの場合は継続そのものが関数の形で得られる。そしてその継続を普通の関数のように呼び出すと、longjmpのように行ったきり戻らない。

一例として、リストlsの中から最初に見つけた3の倍数をprintするコードを、無理矢理call/ccを使って書いたもの。
(define ls '(11 17 5 6 21 31 33))
(print (call/cc (lambda (c)
  (let loop ((l ls))
    (cond
      ((eq? ls '()) #\x)
      ((= 0 (mod (car l) 3)) (c (car l)))
      (else (loop (cdr l)))
    )
  )
)))
実行時、call/ccの引数の無名プロシージャの仮引数cには継続が入っている。なのでこのcを実行すると、どんなにループやらなにやらがネストしていようともcall/ccの呼び出し箇所、今回の例では何かprintしようとしているところに飛んでいく。

2012年5月1日火曜日

Scheme第12歩

OOPの話。
まずはクラスの定義。
(define-class クラス名 (スーパークラス名 ...)  (スロット定義 ...))
クラス名とスーパークラス名はそのまんまの意味だからいいとして、スロットという見慣れない言葉の正体は、他の言語で言うところのクラスメンバのようなもの。オプション次第でインスタンス変数になったり、クラス変数になったりする。
クラスのインスタンスを作成するには
(make クラス名)
とする。また、インスタンスのスロットにアクセスするには
(slot-ref インスタンス スロット名)
(slot-set! インスタンス スロット名 値)
とする。

例えば、このように使う。
(define-class <animal> () (name (age :init-value 0)))
(define an-animal (make <animal>))
(slot-set! an-animal 'name "Nanashi")
(print (slot-ref an-animal 'age))
スロット定義は、名前だけ指定すれば単なるインスタンス変数となる。(名前 オプション ...)の形で定義すると、初期値やらgetterやらsetterやらを指定できる。が、他の言語にあるようなアクセス権はないので、カプセル化に関しては使う側の良識に依存している。
なお、クラス名を<>で括っているのは単なる慣例であって、文法上必要なわけではない。

ちょいと継承も使ってみよう。
(define-class <fish> (<animal>) ((in-saltwater? :init-value #f)))
(define-class <bird> (<animal>) ((fly? :init-value #t :init-keyword :fly? :getter is-able-to-fly?)))
(define carp (make <fish>))
(define a-penguin (make <bird> :fly #f))
(print (slot-ref carp 'age))
(print (is-able-to-fly? a-penguin))
スーパークラスのスロットも、サブクラスで追加したスロットも、同じように使える。

問題は多態性。Schemeの場合、他の多くの言語のようにメソッドがクラスに含まれるわけではなく、名前は同じだが引数のタイプや数が異なる関数の多重定義によって、多態性を実現する。ジェネリック関数と呼ばれるこのタイプの関数は
(define-method 関数名 (引数 ...) 処理内容 ...)
と定義する。例えば
(define-method bark ((a <dog>)) (print "bow"))
(define-method bark ((a <cat>)) (print "mew"))
とすると、引数が<dog>クラスのインスタンスなら"bow"と吠え、<cat>クラスのインスタンスなら"mew"と鳴く、bark関数が定義される。
大事なことなので2度書くが、ジェネリック関数はクラスの中にある特別な関数ではない。なので、特別に名前のみでスロットにアクセスできたりするようなことはない。

Scheme第11歩

ベクタと連想リストの話。

まずは、序盤にさらりと触れたっきりだったベクタについて。ベクタとは固定長の配列。作成するには#()リテラルか
(vector 要素 ...)
(make-vector 要素数)
(make-vector 要素数 各要素の初期値)
を使う。make-vector初期値を省略すると#<undef>になる模様。
こうして作成したベクタの長さは
(vector-length ベクタ)
で得られる、要素の取得と設定は
(vector-ref ベクタ インデックス)
(vector-set! ベクタ インデックス 設定したい値)
とする。ベクタのインデックスは0オリジン。例えば、長さが10なら有効なインデックスは0~9。vector-refもvector-set!も、無効なインデックスの値を指定するとエラーとなる。
ベクタの複製は
(vector-copy ベクタ)
(vector-copy ベクタ 先頭インデックス)
(vector-copy ベクタ 先頭インデックス 末尾インデックス)
で行う。先頭インデックスは省略すると0、末尾インデックスは省略すると(長さ-1)となる。インデックスが範囲外を指し示した場合、無効なインデックスに対応する要素は#<undef>。
ベクタからリストへ、リストからベクタへの変換は
(vector->list ベクタ)
(list->vector リスト)
とする。

ベクタとリストの使い分けは、まあ適材適所か。処理系の実装依存ではあるだろうけれど、大量のデータをランダムアクセスする場合はベクタが向いているだろう。一方、 伸ばしたり縮めたり、千切ったり繋げたりするのはリストの得意技。

連想リストはドット対のリスト。ベクタのように専用の型があるわけでなく、あくまでリストの一種。他のリストとの違いは、連想リスト専用の探索関数が用意されていること。例えば
(define al '((foo . 123) (bar . 456) (baz . 7890)))
という連想リストに対して
(assq 'bar al)
とすると、
(bar . 456)
という値が得られる。要するに、他の言語で言うところの連想配列と同じようなもの。ただし実装はあくまで連結リストなので、探索時間はO(n)。
連想リストの探索関数にはassq, assv, assocがある。これらの違いはキーの比較方法。前から順にeq?, eqv?, equal?で比較する。