エンジニアのソフトウェア的愛情

または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか

Ioが面白い・その0 の その7 他はロッカールームに戻ったがフォワードはぎりぎりまでシュート練習を続ける

「3日目」の章、の2日目。

7つの言語 7つの世界

7つの言語 7つの世界

メッセージで自分が持っていないスロットを指定された場合にはプロトタイプにforward

Rubyには呼び出されたメソッドが未定義だった場合に呼び出される[http://doc.ruby-lang.org/ja/1.8.7/method/Object/i/method_missing.html:title=method_missing]メソッドがありますが、Ioで同様の動作をするのがforwardメソッドです。

Io> foo := Object clone
==>  Object_0x4e4bf0:

Io> foo hoge

  Exception: Object does not respond to 'hoge'
  ---------
  Object hoge                          Command Line 1

Io> foo forward := method("[foo " .. call message name .."]")
==> method(
    "[foo " .. call message name .. "]"
)
Io> foo hoge
==> [foo hoge]

foohogeメッセージを送ったとき、forwardに細工をする前はfoohogeに応答できませんが、forwardを定義してやるとメッセージに応答するメソッドがなかった場合に動作しているのがわかります。

Ruby on Railsはこのしくみを効果的に使って実現しています(記憶が正しければ)。


練習の題材に、役に立ちそうな立たなそうな「ローマ数字を解釈する」というのを選んでみました。


まず。Number VIとやると6と返ってくるようにしてみます。

このためにRomanNumeralというオブジェクトをインポートしていますが、これは話題の中心でないので後ろの方で解説します。文字列に対してたとえば"VI" fromRomanNumeralとすると数値の6が返るようなしくみを実現している、とだけ覚えておいてください。


まず。そのままではメッセージに応答できないことを確認。

Io> Number VI

  Exception: Number does not respond to 'VI'
  ---------
  Number VI                            Command Line 1

RomanNumeralを読み込んで、Numberforwardを書き換えます。

Io> RomanNumeral
==>  RomanNumeral_0x5f6840:
  C                = 67
  D                = 68
  I                = 73
  L                = 76
  M                = 77
  RomanNumeralDigits = "MDCLXVI"
  V                = 86
  X                = 88
  fromRomanNumeral = method(...)
  romanNumeralDigit = method(s, ten, five, one, ...)
  type             = "RomanNumeral"

Io> Number forward := method(n := call message name fromRomanNumeral; if(n != 0, n, nil))
==> method(
    n := call message name fromRomanNumeral; if(n != 0, n, nil)
)

実行結果。

Io> Number VI
==> 6
Io> Number III
==> 3
Io> Number IIII
==> nil

メッセージ名をローマ数字と解釈して数値に変換されているのがわかります。

マスター名前空間でもforwardする

Ioではすべてのメッセージにレシーバがいて、レシーバを指定しなかったばあいはマスター名前空間(いわゆるグローバルな空間)のオブジェクトにメッセージが送られます。

と、いうこととは。マスター名前空間にもforwardを定義することができます。

Io> forward := method(n := call message name fromRomanNumeral; if(n != 0, n, nil))
==> method(
    n := call message name fromRomanNumeral; if(n != 0, n, nil)
)
Io> VI
==> 6
Io> III
==> 3
Io> MCMXCIX
==> 1999

計算もできます(数値ですから)。

Io> XXXIII + LXV
==> 98
Io> XXXIII + LXV * X
==> 683


なんかあっさり片付いてしまった気がする。

そんなわけで、つづく。あとひとつ、の予定。

ローマ数字パーサの実装

ここからは補足。

続きを読む