2012年4月29日日曜日

Scheme第10歩

マクロの話ファイナル。
これまでのマクロの話は、low-levelとかtraditionalとか言われているタイプのマクロに関するもので、今では新しいマクロ定義の方法がある。今回は新しい方のマクロについて。

もう1つの新しいマクロはhygienic macroと呼ばれているもの。日本語だと衛生的マクロとか健全なマクロと訳されているみたい。新しいとは言ったものの、1998年の規格(R5RS)で定まったものらしいので、実はそんなに新しくもない。

前置きはこのくらいにして、早速使ってみる。
(define-syntax for   (syntax-rules (in to)
    ((_ x in xs body ...)
      (for-each (lambda (x) body ...) xs))
    ((_ i a to b body ...)
      (if (<= a b)
        (let loop ((i a)) (when (<= i b) body ... (loop (+ i 1))))
        (let loop ((i a)) (when (>= i b) body ... (loop (- i 1))))
      )
    )
  )
)
ここで定義したはforというマクロは、以下のように使用する。
(for v in '(foo bar baz) (print v))
(for c 1 to 10 print c)
(for n 100 to 10 print n)
上から順にfoo, bar, bazと表示。1, 2, ..., 10と表示。100, 99, ..., 10と表示する。

さて、いよいよマクロ定義の中身を見てみよう。
最初のdefine-syntaxは、define-macroの新型みたいなもの。次のforが定義したいマクロの名前。ここまではいいとして、問題はその次のsyntax-rules。
syntax-rulesの第1引数は、シンボルのリスト。ここで列挙したシンボルは、第2引数以降のパターンマッチの際、予約語として扱われる。
syntax-rulesの第2引数は、((パターン) 置換テンプレート)の形。指定したパターンにマッチしたら置換テンプレートを適用して得られたフォームに置換することを意味する。
パターンの中では、予約語は予約語そのものにマッチする。また、'...'は 0個以上の可変個数のフォームにマッチする。そしてそれ以外は、任意の1つのフォームにマッチする。
上記の例ではパターンの先頭に'_'があるが、これも単なる変数の1つでしかない。しかも、結局はマクロ名forにしかマッチしないので、 '_'から得られる情報は実質ゼロ。そんなどうでもいい変数の名前として '_'を使用する習慣があるようなので、今回はそれに倣ってみた。
置換テンプレートの中身は、ほとんど見た目通り。パターンの中で使用した変数は対応するマクロの実引数に置き換えられ、それ以外はそのまんま。これまでのlistやquoteを駆使してきたマクロと比べると、格段に見やすい。
syntax-rulesの第3引数以降は、 第2引数と同じ書式。要するに、複数の置換ルールを定義できるのだ。上記のforの例では、inを使う形式とtoを使う形式をまとめて定義している。

ちなみに、hygienicと呼ばれるからには単なる便利なラッパーというわけではない。新しい方法で定義したマクロでは、明示的にgensymを使わなくても自動的に名前の衝突を避けてくれる。

2012年4月28日土曜日

Scheme第9歩

マクロの話その3。
マクロの中でローカル変数を使いたい場合。例えば2つの変数の値を入れ替えるマクロを、単純にletを使って書くとこうなる。
(define-macro
  swap (lambda (a b)
    `(let ((tmp ,a)) (set! ,a ,b) (set! ,b tmp))
  )
)
これを
(define x 10)
(define y 20)
(swap x y)
(print "x=" x ", y=" y)
このように使用すれば、期待通りxとyが入れ替わってくれるのだが、
(define foo 100)
(define tmp 200)
(swap foo tmp)
(print "foo=" foo ", tmp=" tmp)
このように使用すると、fooとtmpが入れ替わってくれない。何が起こったかは、マクロを手動で展開してみると一目瞭然。
(let ((tmp foo)) (set! foo tmp) (set! tmp foo))
fooはローカル変数tmpに保存した値を書き戻しただけだし、letの外のtmpは参照すらされていない。

要するに、マクロの引数とローカル変数の名前がかぶると、引数が内側から見えなくなる問題が起こる。ならば、引数とは絶対にかぶらない名前をローカル変数に付けてやればよい。そこで出てくるのがgensym。gensymは評価するたびにユニークなシンボルを返すので、これをローカル変数名として使ってやればよい。
(define-macro
  swap2 (lambda (a b)
    (let ((tmp (gensym)))
      `(let ((,tmp ,a)) (set! ,a ,b) (set! ,b ,tmp))
    )
  )
)
ぱっと見、最初のswapと似てはいるが、ローカル変数tmpの役割がまるで違うことに注意。gensym使用版swap2のtmpは、マクロ展開時に使用するローカル変数。そしてtmpが保持するのはaの値ではなく、aの値を保持する変数の名前。tmpはunquoteされているので、マクロの展開結果にtmpは現れない。

Scheme第8歩


マクロの話その2。
マクロはそのプロシージャの実行結果で置換されるのだが、実行結果が所望のS式になるようにあちこちquoteして周るのは、なかなか面倒かつややこしい。それを多少は改善してくれるのがquasiquote。

quasiquoteは、ほとんど全部quoteしたいのだけれど、部分的にquoteしたくない要素もあるリストを構築するとき、便利なもの。quasiquoteの中で例外的にquoteしたくない部分をunquoteする。リストの展開まで行うunquote-splicingも使える。

手っ取り早く使用例。第2引数のリストの合計に第1匹数を掛けるマクロを、まずはquasiquoteを使わずに定義してみる。
(define-macro
  gained-sum
  (lambda (a xs) (list '* a (apply list '+ xs)))
)
このマクロを
(gained-sum 10 (1 2 3 4 5))
のように使用すると、
(* 10 (+ 1 2 3 4 5))
と展開されるのだが、まあ、はっきり言ってマクロ定義は分かりにくい。そこでquasiquoteを使うと、
(define-macro
  gained-sum-qq
  (lambda (a xs) `(* ,a (+ ,@xs)))
)
マクロ定義が、マクロ展開結果にだいぶ近付いて見える。
ちなみに`xは(quasiquote x)、,xは(unquote x)、,@xは(unquote-splicing x)の略記法。専ら略記法しか使わなさそうだが。

マクロ内で使用するローカル変数の話は、次回に持ち越し。

2012年4月26日木曜日

Scheme第7歩


マクロの話その1。
Schemeのマクロはややこしい。しかも、ややこしい点が複数あるから、余計にややこしい。しかしまあ、ややこしいと繰り返してもややこしいだけなので、1つずつややこしい点を解消していこう。

まずはマクロの定義。
(define-macro マクロ名 プロシージャ)
これだけ見れば、別にグローバルな名前付きプロシージャの定義とほとんど同じように見えるが、問題はその展開。
C言語のマクロは、マクロの定義に従って参照個所を機械的に置換するだけだが、Schemeのマクロはdefine-macroで定義したプロシージャの中身で置換する、…わけではない。プロシージャの実行結果で置換するのだ!

例えば、以下のコードについて考えてみよう。
(define foo ‘bar)
(define bar “bar is bar”)
(define-macro foo-macro1 (lambda() ‘foo))
(define-macro foo-macro2 (lambda() foo))
(print “foo-macro1 = ” (foo-macro1))
(print “foo-macro2 = ” (foo-macro2))
foo-macro1のプロシージャの実行結果はfooというシンボル。foo-macro2のプロシージャの実行結果はfooの評価値、すなわちbarというシンボルである。これをふまえて手動でマクロ展開すると
(print “foo-macro1 = “ foo)
(print “foo-macro2 = “ bar)
となる。
…と、ここまではあくまで前処理で、この展開結果を実行するのが本番。したがって、最終的には
foo-macro1 = bar
foo-macro2 = bar is bar
と、fooの評価値とbarの評価値が表示される。

次に引数付きマクロの例。
(define-macro p (lambda (x) (list ‘print x)))
(let loop ((i 1))
  (when (<= i 10)
    (p i)
    (loop (+ i 1))
  )
)
マクロが展開されるとき、マクロの仮引数の値は(quote 実引数)である模様。なので、上記の例を手動で展開すると
(let loop ((i 1))
  (when (<= i 10)
    (print i)
    (loop (+ i 1))
  )
)
となり、めでたく1〜10が表示される。ここでもし、マクロ定義のクォート等を怠るとどうなるか。
(define-macro p2 (lambda (x) (print x)))
(let loop ((i 1))
  (when (<= i 10)
    (p2 i)
    (loop (+ i 1))
  )
)
このマクロp2は(print ‘i)の実行結果、すなわち#<undef>に展開されるので、実行しても10回#<undef>が評価されるだけ。こうならないためにも、マクロは基本的に実行結果がS式になるように定義するのだ。

…とはいえ、listやquoteを使って巧い具合にS式になるようにマクロを定義するのは、結構面倒。そこで出てくるのがquasiquoteなのだが、長くなってきたので次回に続く。

Google Driveがやって来る?

噂のGoogle Driveが始まった、らしい。
早速使ってレビューしてみようかと思ったのだが、混雑してるのか何なのか、どうもすぐには使い始められないみたい。右上の通知ボタンを押して、準備ができたらメールを送ってもらうようにした。

まあどうせ、あの串にブロックされるんだろうけど…。

2012年4月24日火曜日

Scheme第6歩


I/Oの話。
まずはポート(port)について。他の言語ではファイルハンドルとかストリームとか呼ばれているものを、Schemeではポートと呼ぶ。ポートの扱いは他の言語のファイルと同様、オープン→読み/書き→クローズという流れ。

入力ファイルのオープンは
(open-input-file "filename")
出力ファイルのオープンは
(open-output-file "filename")
で行う。これらは第2引数以降に細かいオプションを指定可能。

文字列をポートとして扱うことも可能。
(open-input-string "string")
で取得した入力ポートからは、第1引数に指定した文字列が読みだされる。また
(open-output-string)
で取得した出力ポートに書き込んだデータは、
(get-output-string)
で文字列として取得することができる。なお、get-output-stringは副作用がない。

読み書きの前にクローズ。入力ポートiのクローズは
(close-input-port i)
出力ポートoのクローズは
(close-output-port o)
とする。

さて、いよいよ読み書き。まずは主だったプロシージャを列挙する。
read-char i入力ポートiから1文字読み込み
read-line i入力ポートiから1行読み込み
read-byte i入力ポートiから1バイト読み込み
read-block n i入力ポートiからnバイト読み込み
read i入力ポートiからフォームを1つ読み込み
write-char c o出力ポートoへ文字cを書き出し
write-byte b o出力ポートoへバイトデータbを書き出し
write f o出力ポートoへフォームfを書き出し
display f o出力ポートoへフォームfを人間が読みやすい形で書き出し
newline o出力ポートoへ改行文字を書き出し
いずれも、入力ポートの指定を省略すれば標準入力(standard-input-port)が、出力ポートの指定を省略すれば標準出力(standard-output-port)が使用される。ついでに、標準エラー出力は(standard-error-port)。

write, readについて補足。writeはフォームをSchemeの処理系が解釈できる形の文字列にして書き込む。readはその逆で、処理系が解釈できるようなフォームの文字列表現を読み出す。

ポートのクローズの面倒を見てくれる、Rubyでいうブロック付きIO.openのようなものがSchemeにもある。
(call-with-input-file "filename" (lambda (i) ...))
(call-with-input-string "string" (lambda (i) ...))
(call-with-output-file "filename" (lambda (o) ...))
(call-with-output-string "string" (lambda (o) ...))
いずれも第1引数に従ってポートをオープンし、そのポートを引数とする第2引数のプロシージャを呼び出した後、ポートをクローズする。

2012年4月23日月曜日

Scheme第5歩


ループと再帰の話。
まず、他の言語によくあるforやwhileのようなものはSchemeにはない。なので、ループに相当するコードは再帰で書く。それだと10000回ループしたらどんだけスタックを食い潰すんだ? …と、手続き型言語に慣れたプログラマなら心配するところけれど、Schemeは末尾再帰をループにすることを言語仕様で定めているそうな。

再帰であろうとなかろうと、プロシージャの呼び出し方には違いはないが、問題はプロシージャの定義の仕方。
(define sum (lambda (x) (if (= x 0) 0 (+ x (sum (- x 1))))))
このようにグローバルなら別に問題ないのだが、問題はローカルな再帰プロシージャを定義したい場合。letにしてもlet*にしても、ローカル変数の初期値を評価するところからは、そのローカル変数自信が見えない。そこでletrecを使う。
(letrec
  ((sum (lambda (x) (if (= x 0) 0 (+ x (sum (- x 1)))))))
  (display (sum 10))
  (newline)
)
letrecの第1引数はletと同じフォーマットに見えるが、そこで導入されたローカル変数を評価するときは、letと違ってローカル変数を参照できる。
(letrec ((c (+ b 1)) (b (+a 1)) (a 1)) (display c))
この例ではc, b, aという定義の順番にとらわれることなく
a = 1
b = 2
c = 3
となる。

ややこしいのが名前付きlet。
(let loop ((i 10) ...)
は
(letrec ((loop (lambda (i) ...))) (loop 10))
と同じ。例えば1から10まで表示する場合、名前付きletを使うと
(let disp ((i 1)) (display i) (newline) (unless (= i 10) (disp (+ i 1))))
と書ける。

改めてletを見てみると、letの中でのみ有効な名前付きのプロシージャを定義して呼び出すことこそ、letの本質のように思えてきた。そう考えると、ローカル変数はあくまでプロシージャの仮引数機能で作っているだけのように見える。

Scheme第4歩

変数のスコープの話。

まず、これまで使って来た
(define global-variable 100)
はグローバル変数の定義。そして、プロシージャの話のところでさらりと書いたけれど、lambdaの仮引数はその中でのみ有効なローカル変数。

さて、(今のところ)ブロックスコープの無いJavaScriptだと、ブロックの代わりに
js> (function() {...})();
みたいなことをやるけれど、Schemeもlambdaを使わないと新たなスコープを導入できないのか? というと、そんなことはない。
(let
  ((a 1) (b 2) (c 3))
  (display (+ a b)) (newline)
  (display (+ b c)) (newline)
  (display (+ c a)) (newline)
)

このようにletを使うと、最初の引数に従って
a = 1
b = 2
c = 3
という3つのローカル変数が作られた上で、2番目以降の引数のフォームが評価される。
ここで、このローカル変数の初期値を評価するときのスコープはletの外側のものであることに注意。
(let
  ((a 1))
  (let
    ((a 2) (b a))
    (display b) (newline)
  )
)
この内側のletのローカル変数bを初期化するときのaは、あくまで外側のa。従って、これを評価すると2ではなく1と表示される。もし内側のbを初期化するとき、直前に初期化された内側のaの値を使いたければ、let*を使う。
(let*  ((a 1) (b (+ a 1)) (c (+ b 1))) ...)
は
(let ((a 1))
  (let ((b (+ a 1)))
    (let ((c (+ b 1))) ...)
  )
)
と等価。

さて、ここまではグローバル変数やローカル変数を定義してばかりだったが、変数なので値を変更することも当然できる。その方法は、グローバルかローカルかには依存せず
(set! 変数名 新しい値)
とする。set!はあくまで値の再設定なので、新しい変数の導入にset!は使えない。

ここで、なぜ変数の定義には初期値が必要なのか? setではなくset!なのか? おそらく、あまり変数の再設定はするなという思想を反映しているんじゃないかな。
…とは言え、今でこそ変数の再利用は保守性を下げる悪癖でしかなくなったけれど、コンパイラが賢くない時代においてはメモリ節約のテクニックだった。Schemeはかなり歴史のある言語のようだけど、昔からあえて再設定を醜くしていたのなら、慧眼だなぁ。
後発のHaskellですら、変数を再設定させない仕様は冒険だったと思う。嫌いじゃないけど。

2012年4月22日日曜日

Scheme第3歩

制御構文について。まずはif。
(if 条件フォーム 真のとき評価されるフォーム 偽のとき評価されるフォーム)
これは、C言語で言うところの3項演算子。いちいちフォームと書いたのは、いずれもS式である必要がないから。偽のとき評価されるフォームは省略可能。なお
(if #f "never evaluated")
をGaucheで評価すると、#<undef>という値が返ってくる。

whenとunlessはifの亜種。
(when 条件フォーム フォーム1 フォーム2 フォーム3 ...)
は
(if  条件フォーム (begin フォーム1 フォーム2 フォーム3 ...))
と等価。unlessはwhenの真偽反転版で、
(unless 条件フォーム フォーム1 フォーム2 フォーム3 ...)
は
(when (not 条件フォーム) フォーム1 フォーム2 フォーム3 ...)
と等価。

次はcond。これはifのネストの略記版で、
(cond
  (条件フォーム1 フォーム1-1 フォーム1-2 ...)
  (条件フォーム2 フォーム2-1 フォーム2-2 ...)
  (else フォーム3-1 フォーム3-2 ...)
)
は
(if 条件フォーム1 (begin フォーム1-1 フォーム1-2 ...)
  (if 条件フォーム2 (begin フォーム2-1 フォーム2-2 ...)
    (begin フォーム3-1 フォーム3-2 ...)
  )
)

と等価。

最後にcase。これはC言語で言うところのswitchのようなもの(Cの場合、switchは式ではないので値を持たないが)。
(case 対象フォーム
  ((一致条件フォーム1-1 一致条件フォーム1-2 ...)
     フォーム1-1 フォーム1-2 ...
  )
  ((一致条件フォーム2-1 一致条件フォーム2-2 ...)
     フォーム2-1 フォーム2-2 ...
  )
  (else フォーム3-1 フォーム3-2 ...)
)
無理矢理condで書くと
(cond
  ((or
    (equal? 対象フォーム 一致条件フォーム1-1)
    (equal? 対象フォーム 一致条件フォーム1-1) ...)
      フォーム1-1 フォーム1-2 ...
  )
  ((or
    (equal? 対象フォーム 一致条件フォーム2-1)
    (equal? 対象フォーム 一致条件フォーム2-1) ...)
      フォーム2-1 フォーム2-2 ...
  )
  (else フォーム3-1 フォーム3-2 ...)
)
となる。ただし、手元のGaucheで試してみた限り、caseだと対象フォームは一度しか評価されない。なので、対象フォームの評価に副作用がある場合は、condで書いた結果と一致する保証は無い模様。

2012年4月21日土曜日

Scheme第2歩

自分の言葉でちびちびまとめていくとは書いたものの、さすがに用語はScheme界隈のものにあわせておこう。
S式(s-expression)
前回の説明は不正確で、とにかく0個以上の要素を()で括ればS式。
フォーム(form)
Schemeのプログラムの断片。S式もフォームだが、S式の中の1つ1つの要素もフォーム。
リスナー(listener)
フォームを評価するもの。
手続き、プロシージャ(procedure)
実行可能なフォーム。他の言語で言うところの関数のようなもの。
さて、ここからはこのプロシージャの話。
まずは作成。
(lambda (x) (display (string-append "Hello " x)))
これはJavaScriptに例えると
js> function (x) { alert("Hello " + x); }
のように無名の関数オブジェクトを作成しているだけ。JavaScriptでいうところの
js> hello = function (x) { alert("Hello " + x); };
のように、何らかの名前を付けてやりたければこうする。
(define hello (lambda (x) (display (string-append "Hello " x))))
これで
(hello "World")
のように呼び出せる。引数のスコープは、普通にプロシージャの中のみ。

プロシージャの引数に付いて補足。
引数を1個取りたければ
(lambda (a) (display a))
2個取りたければ
(lambda (a b) (display (+ a b)))
3個取りたければ

(lambda (a b c) (display (+ a b c)))
4個取りたければ…(以下略)。
問題は可変引数。2個以上取りたければ
(lambda (a b . c) (display (apply + a b c)))
1個以上取りたければ
(lambda (a . b) (display (apply + a b)))
0個以上取りたければ
(lambda a (display (apply + a)))
とする。
あるかどうか分からない残りの引数が可変長のリストの形で渡されるというのは、まあよくある話だからいい。が、その表記は分かりづらいので、実例を挙げて頭の中を整理する。
(define foo (lambda (a b . c)) (略)))
この引数リストをドット対で表現すると
(a . (b . c))
となる。一方、このプロシージャを
(foo 1 2 3 4)
と呼ぶときの引数リストをドット対で表現すると
(1 . (2 . (3 . (4 . ()))))
となる。よって、その対応付けは
a = 1
b = 2
c = (3 . (4 . ())) = (3 4)
となるわけか。
0個以上の可変引数に()が無いのは決して付け忘れでない。例えば
(define bar (lambda a (略)))
これを
(bar 1 2 3)
と呼ぶときは
a = (1 2 3)
となり、
(bar)
と引数無しで呼ぶときは
a = ()
となる。
なぜ0個以上の可変引数を()で括ると駄目なのかは、やはり引数リストをドット対表記で考えてみれば分かる。
(define baz (lambda (a) (略)))
の引数は
(a . ())
なので、引数が0個。すなわち引数リストが
()
に対して、引数を対応付けようがない。

2012年4月19日木曜日

Rubyでクラス定数を参照

Rubyでクラスの外からクラス定数を参照する場合。
例えばこんなクラスに対して、
class Foo
  C1 = 'constant value #1'
  C2 = 'constant value #2'
  C3 = 'constant value #3'
end

Fooの外からC1, C2, C3を参照したければ、単純に
p Foo::C1
p Foo::C2
p Foo::C3
こんな感じでアクセスすればいい。が、アクセスしたい定数名を動的に決定したい場合はどうするか。
まあ、eval系メソッドを使えば何でもできるのだが、もう少し行儀よく真面目にやるにはconst_getを使う。
(1..3).each do |i|
  p Foo.const_get("C#{i}")
end
こうすればC1, C2, C3という定数名を実行時に生成しながらアクセスできる。

他にも、定数を定義するconst_set、定義してあるかどうかを確かめるconst_defined?、定義を取り消すremove_constがある。いずれもModuleモジュールのメソッドなので、全てのクラスやモジュールに対して使用可能。

const_get, const_set, remove_const。命名規則が気持ち悪いなぁ。

2012年4月18日水曜日

Schemeはじめの1歩

さて、3週間で終わるかどうかは分からないが、ちびびちと自分の言葉でまとめていこう。

まずは式。Schemeの式(と言うかLISPのS式)は非常にシンプルで、
(コマンド 引数 引数 引数 ...)
のように、コマンドの後に(必要であれば)引数を空白区切りで並べて、全体を括弧で括るだけ。他の言語で言うところの制御構文ですら全てこのS式で表現されるので、処理系はただひたすらS式を評価していくだけ。

次にプリミティブな型。
真理値
#t, #f
真理値が要求されるところでは、#f以外は全てtrue扱い
数値
整数
12
有理数
34/5
/の前後にスペースを入れては駄目
実数
6.78
複素数
9+10i
+, -, iの前後にスペースを入れては駄目
実部が0なら実部は省略可能だが、虚部の符号は省略できない
虚部の±1は省略可能
文字
#\c (文字'c')
#\tab, #\space, #\newline等、別名定義もある
Gaucheはマルチバイト文字もOK(e.g. #\あ)
シンボル
変数や関数の名前
名前自体に型がある
最後に複合的な型。
ドット対
(1 . #\b)
他の言語の用語で言うならば2要素のタプル。
要素の型は何でもあり。2つの要素の型は一致している必要なし。
リスト(1 2 3 4)は、ドット対(1 . (2 . (3 . (4 . ()))))の略記法
ベクタ
#(5 6 7 8)
文字列
"This is string"
HTMLのタグ打ちめんどいなー。

2012年4月17日火曜日

Schemeはじめの1歩の1歩前


vi派だけどLISPを触ってみようと、思い立ったが吉日。言語仕様がコンパクトらしいSchemeを選んでみた。厳密には思い立ったのは昨日だが、細かいことは気にしない。
包括的なリファレンスは一旦置いといて、まずはこちらのサイトを教科書にさせてもらおう。

さて、Schemeの処理系にも色々あるようだけれど、どうもメジャーらしいGaucheを入れてみたのが昨日の話。まずはそのGaucheを起動してみる。

% gosh
gosh> 
ファイル名を指定しなければインタラクティブモードで起動する。プロンプトに対してSchemeの式を入力すれば、即座に評価結果を表示してくれる。
一方、ファイル名を指定して起動すれば、そのファイルを解釈して即座に実行。
% cat hello.scm
(begin (display “Hello World!”) (newline))
% gosh hello.scm
Hello World!
詳細はman。…を、そのうちきちんと読もう。

Xcodeのバージョンアップ

何となくSchemeをやってみようと思い立ち、HomebrewでGaucheをインストールしようと試みる。
% brew install gauche
何かワーニングが出たぞ? どうもXcodeが古いらしい。App Storeのアップデートには何の通知も無いものの、Xcodeを検索してみたところ、確かに手元の4.2系より新しい4.3.2が存在したので「インストール」を選択。そして1.5GBほどのファイルをダウンロードした後、DockからXcodeを起動してみると…、4.2系のまま。
マイナーバージョンが上がったからかどうかは知らないが、古いバージョンは削除されなかった。Dockにある4.2系のショートカットから起動したから、古いのが起動したのか。
結局、アプリケーションディレクトリのXcode.appが4.3.2だった。これを起動すると、途中で古いバージョンをゴミ箱に移すかどうか聞いてくれた。

2012年4月15日日曜日

ザクとうふIII

前回の失敗を繰り返さぬよう、今度はザクとうふの容器にラップを敷いて作成。
そして完成、ザク芋ようかん!

♪トウフじゃない トウフじゃない 羊羹のことさ(歌詞じゃないよ)

面倒だけどきちんと裏漉ししたので、見た目はザクながら滑らかな舌触り。しかし、豆腐ならつるっといけるサイズだけど、芋ようかんだと結構なボリュームだった。

いい加減、テレビを捨てた

チラシの裏。
以前捨てようと書いたものの、梱包が面倒で放置していたテレビを、とうとう本当に捨てた。もっとも能動的に捨てに行ったわけでなく、単に通りがかりの廃品処理業者に任せただけ。
しかしまあ、かなりリサイクル料金を吹っかけられてしまった。ギリギリ許容範囲まで値切ったものの、世の中、手間を惜しむと金がかかるように出来てるんだな〜。

部屋が広くなったから、トータルでは良しとするか。

2012年4月14日土曜日

狼が来たぞー

またKindle襲来のニュースか。今度こそ本当に来てほしいものだ。
まあ、ハードだけならとっくに来てるんだけどね。写真は一昨年購入したKindle3。

最も期待しているのは、やはり電子書籍市場の立ち上げ。出版コストが劇的に下がることで、玉石混淆の面白い市場ができてくれたら嬉しい。

2012年4月13日金曜日

1週間定期

東京スター銀行の1週間定期預金が年利0.25%。これは新生の2週間0.2%より好条件じゃないか。
…と思ったら、東京スター銀行には他行宛の振込手数料が無料になるオプションが見当たらない。いくら他行より少し多く増えるとしても、結局出すときに目減りするのではなぁ。

いい加減、定期預金以外の運用も考えた方がいいかな〜。

2012年4月12日木曜日

カレーと自炊

スラドで見たネタにマジレス。
元ネタをざっくりまとめると、新入生に対して「どんなに手間暇かけて作ったカレーでも食うのは一瞬。虚しくなっちまうから作るなYO!」という話。

いや、違うのだよ。カレーの問題点は断じてそこではない。
自炊におけるカレーの長所は、誰が作っても安定してカレーだと思えるものが出来上がること。ただし、裏を返せばこれがカレーの最大の短所となる。すなわち、出来上がりが容易に想像できてしまうのだ。

そう。あからさまな安牌を切る行為は、思考を伴う創造ではなく単なる作業に成り下がってしまう。作業の繰り返しはマンネリ感を生み出し、マンネリは自炊の継続を妨げる。

あの食材をこう使ったら美味そうだ。ついでにこの食材も組み合わせるのはどうだろう? そんなことを仕事中に想像しながら、実践した結果を次の機会にフィードバックする。そんなPDCAサイクルを回し続けることが、自炊継続のコツだと思う自炊9年生。

2012年4月10日火曜日

XMLからYAMLへ変換

テキストファイルとは言えども生のXMLは読みづらいので、YAMLに変換するRubyスクリプトを書いてみた。

#! /usr/bin/ruby
require 'enumerator'
require 'rexml/document'
require 'yaml'
def xml2tree(node)
  ret = {}
  key = node.name
  val = {}
  val['attributes'] = node.attributes
  val['text'] = node.text
  children = node.enum_for(:each_element).map do |c|
    xml2tree(c)
  end
  val['children'] = children unless children.empty?
  ret[key] = val
  ret
end
doc = REXML::Document.new(ARGF)
tree = xml2tree(doc.root)
print tree.to_yaml
パスの通ったところにxml2yamlという名前で置いて
% xml2yaml foo.xml | less
のように使う。

2012年4月9日月曜日

HDD三国志

HDDが欲しいけど、相変わらずの高値に手が出せない。いや、買えないことは無いが、洪水前の値段を知っていると、何となく今の値段で買うのは悔しい。早く元の値段に戻ってくれないかなぁ。

そんなことを考えながら、そういえばHDD屋さんも随分と減ったものだと思い、ちょっと近年の買収劇を整理してみた。
東芝も地味に3.5インチをやるんだっけ。

2012年4月8日日曜日

俺達2012

チラシの裏。
ライオンズの中継ぎ以降が弱いのは分かっちゃいたけど、それにしたって逆転負けしすぎだ。誰だよ、ゴンザレスなんて連れて来たのは…。

大石を引き当てたときは、後ろの問題が解決するかと期待したんだけどなぁ。先発転向すると言ってから、すっかり話題にもなっていないのはどうなんだ?

ザクとうふII

ぶっちゃけ、ザクとうふの容器で作れば何でもザクになるだろうと思い、アボカド豆腐をザクにしようと試みた。

…が、失敗しますた。アボカド豆腐がザクとうふの容器から抜けず、結局スプーンですくって食べることに。ゼラチンが少なかったか、ゼラチンで固まるはずの豆腐の水を切りすぎたのが、敗因かなぁ。まあ、デザートとしては十分あり。

おまけ。豆腐1丁とアボカド1個ではザクとうふの容器に収まり切らなかったので、アボカドの皮に再充填した。ラップを被せておかなかったので、空気に触れた部分が変色してしまった。

ザクはさておき、豆腐と生クリームの組み合わせには可能性を感じた。

2012年4月7日土曜日

よく考えなくてもお金は大事だよ

もうすぐ定期預金が満期を迎えるので、次はどうするかざっと眺めてみた。が、相も変わらず低金利にがっかり。まあ、デフレだから実質金利は悪くないのかもしれないけど…。

とりあえず、仕組み預金を除いた中では一番ましな新生銀行の2週間満期預金で、夏のボーナスキャンペーンまで様子見かなぁ。新生から他行宛の振込手数料は、ネットなら少なくとも月1回は無料なので、実質金利が手数料で台無しになることはないみたい。

2012年4月6日金曜日

ザクとうふ

一部で話題のザクとうふが最寄りのイオンにあったので、購入してみた。1丁(機?)158円。
パッケージ表面。
裏面。充填豆腐だから、確かに絹とは違う 。
ザク、大地に立つ?
裏面。普通の豆腐なら、こっちが上。
開封。昆布茶をすすりながら、何もかけずに食してみた。
メインカメラをやられただけだ!

2012年4月5日木曜日

我が生涯に一片の悔い無し?

チラシの裏。
以前読んで、いつか頭の中で整理しようと思っていたコラム。

ナースが聞いた「死ぬ前に語られる後悔」トップ5

トップ5の項目のみ引用。

  1. 自分自身に忠実に生きれば良かった
  2. あんなに一生懸命働かなくても良かった
  3. もっと自分の気持ちを表す勇気を持てば良かった
  4. 友人関係を続けていれば良かった
  5. 自分をもっと幸せにしてあげればよかった

要するに〇〇ということか。…と、今回は一言でまとめたりしないが、人生の最期にすることが後悔というのは嫌だなぁ。

もし今死ぬとしたら、走馬灯の最高潮はシレン2の最果てをクリアするシーンかなw

2012年4月4日水曜日

extended attributeの操作

extended attributeについては以前書いたが、今回はその操作方法。
ずばり、標準で入っているxattrというコマンドでできる。以下、そのヘルプメッセージ。

usage: xattr [-l] [-r] [-s] [-v] [-x] file [file ...]
       xattr -p [-l] [-r] [-s] [-v] [-x] attr_name file [file ...]
       xattr -w [-r] [-s] [-x] attr_name attr_value file [file ...]
       xattr -d [-r] [-s] attr_name file [file ...]
       xattr -c [-r] [-s] file [file ...]
The first form lists the names of all xattrs on the given file(s).
The second form (-p) prints the value of the xattr attr_name.
The third form (-w) sets the value of the xattr attr_name to the string attr_value.
The fourth form (-d) deletes the xattr attr_name.
The fifth form (-c) deletes (clears) all xattrs.
options:
  -h: print this help
  -l: print long format (attr_name: attr_value and hex output has offsets and
      ascii representation)
  -r: act recursively
  -s: act on the symbolic link itself rather than what the link points to
  -v: also print filename (automatic with -r and with multiple files)
  -x: attr_value is represented as a hex string for input and output
オプション無しで設定されている属性の名前を列挙。-pオプションで指定した名前の属性の値を表示。-wオプションで指定した名前の属性に値を設定。-dオプションで指定した名前の属性を削除。-cオプションで全属性を削除。
Automatorのフォルダアクションを使えば、~/Downloadsに落としたそばからxattr -cを適用したりできるかな?

2012年4月3日火曜日

Automatorって何だ?

何だかMacらしくない使い方ばかりしている気がするので、Automatorとやらを使ってみようと思い立った。

GUI操作も含めた一連の作業をワークフローという形でまとめ、そのワークフローを再利用して楽をしよう。…と言うのが主機能らしいが、それよりもフォルダアクションという機能が面白い。
フォルダアクションというのは、特定のフォルダへファイルをコピー(or 移動)すると、そのファイルに対して自動的に行われる作業。例えばディレクトリ~/png/に画像ファイルを置くと自動的にPNGに変換してくれる、なんてこともできる。Finderによるファイル操作だけでなく、コマンドラインから実行したcpにも反応してくれた。

作成したフォルダアクションは、~/Library/Workflows/Applications/Folder Actions/以下に保存される模様。

2012年4月1日日曜日

文字コードの変換


文字コードの変換スクリプト。iconvが文字コードを判別してくれないから、RubyのKconvを使って自動判別と変換をしている。
#! /usr/bin/ruby
require 'kconv'
require 'optparse'
# option
def kname(str)
  str = str.upcase rescue ''
  if str =~ /^J/
    Kconv::JIS
  elsif str =~ /^S/
    Kconv::SJIS
  elsif str =~ /^E/
    Kconv::EUC
  elsif str =~ /^U.*16/
    Kconv::UTF16
  elsif str =~ /^U/
    Kconv::UTF8
  elsif str =~ /^A/
    Kconv::AUTO
  else
    nil
  end
end
option = {
  :from => 'auto',
  :to => nil,
  :lf => true
}
begin
  myname = File.basename($0).split(/\./)
  myname.shift
  option[:to] = myname.pop unless myname.empty?
  option[:from] = myname.pop unless myname.empty?
rescue
end
ARGV.options do |opt|
  opt.on('-f CODE', '--from CODE') do |v|
    option[:from] = v
  end
  opt.on('-t CODE', '--to CODE') do |v|
    option[:to] = v
  end
  opt.on('--crlf') do
    option[:lf] = false
  end
  opt.on('--lf') do
    option[:lf] = true
  end
  opt.parse!
end
option[:from] = kname(option[:from])
option[:to] = kname(option[:to])
raise "invalid option(s)" unless option[:from] and option[:to]
# main
ARGV.each do |filename|
  result = 'done'
  begin
    ctx = File.read(filename)
    File.open(filename, 'w') do |f|
      ctx = ctx.kconv(option[:to], option[:from])
      ctx.gsub!("\r", '') if option[:lf]
      f.write(ctx)
    end
  rescue
    result = 'failed'
  end
  $stderr.print("#{filename} : #{result}\n")
end
これをパスの通ったところにj2jという名前で置いて、例えば
% j2j -tu foo.txt bar.txt
と実行すれば、foo.txtとbar.txtをUTF-8に変換する。複数ファイルを入力する場合、入力ファイルの文字コードはバラバラでもOK。フィルタではなく指定ファイルを直接変更するので注意。
おまけ機能。実行スクリプトの名前のサフィックスで-tオプションのデフォルト値を指定できる。例えばj2j.sjisという名前のシンボリックリンクを作っておけば
% j2j.sjis baz.txt
でbaz.txtをShift_JISに変換できる。

いい加減、コードのコピペはやめたいなー。どこかコード置き場を借りようかな。

文字コードの判別

指定されたファイルの文字コードを判別して表示するスクリプト。雑だけれど、改行コードも見ている。

#! /usr/bin/ruby
require 'kconv'
class String
  def guess
    encode = Kconv.guess(self) rescue Kconv::UNKNOWN
    ret = case encode
    when Kconv::JIS
      'jis'
    when Kconv::EUC
      'euc'
    when Kconv::SJIS
      'sjis'
    when Kconv::BINARY
      'bin'
    when Kconv::ASCII
      'ascii'
    when Kconv::UTF8
      'utf8'
    when Kconv::UTF16
      'utf16'
    else
      'unknown'
    end
    unless ret == 'bin' or ret == 'unknown'
      if include?("\r\n")
        ret << ', CRLF'
      elsif include?("\r")
        ret << ', CR'
      elsif include?("\n")
        ret << ', LF'
      else
        ret << ', unknown'
      end
    end
    ret
  end
end
if ARGV.empty?
  print $stdin.read.guess, "\n"
else
  len = ARGV.map do |name|
    name.length
  end.max
  ARGV.each do |name|
    begin
      File.open(name) do |fp|
        printf("%-#{len}s : %s\n", name, fp.read.guess)
      end
    rescue
    end
  end
end
パスの通ったところにguessという名前で置いて
% guess foo.txt bar.txt
のように使う。ファイル名を省略したら標準入力を対象とする。
やってることは、単にRubyのKconv.guessへ丸投げ。