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

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

Ioが面白い・その0 の その4 もっと柔軟運動

「2日目」のつづき。「2日目」というのは、Ioのエントリを書き始めて2日目という意味でなくて、本の章の名前です。

7つの言語 7つの世界

7つの言語 7つの世界

演算子

OperatorTableオブジェクトに登録することで自作の演算子を使えるようになります。


OperatorTableオブジェクトの作りを見てみると。

Io> OperatorTable
==> OperatorTable_0x348ff0:
Operators
  0   ? @ @@
  1   **
  2   % * /
  3   + -
  4   << >>
  5   < <= > >=
  6   != ==
  7   &
  8   ^
  9   |
  10  && and
  11  or ||
  12  ..
  13  %= &= *= += -= /= <<= >>= ^= |=
  14  return

Assign Operators
  ::= newSlot
  :=  setSlot
  =   updateSlot

To add a new operator: OperatorTable addOperator("+", 4) and implement the + message.
To add a new assign operator: OperatorTable addAssignOperator("=", "updateSlot") and implement the updateSlot message.

通常の演算子(Operators)と代入演算子(Assign Operators)の一覧が表示され、ご丁寧なことに登録の仕方も表示されます。通常の演算子であればaddOperatorメソッドを使えばよいようです。数字は結合の優先順位です。


本にならって真偽値の排他的論理和xorを定義してみます。

まず演算子xorを登録します。

Io> OperatorTable addOperator("xor", 11)
==> OperatorTable_0x348ff0:
Operators
(中略)
  11  or xor ||
(後略)

次に実装。昨日のエントリでも書いたように真偽値の演算は真偽値をあらわすオブジェクトtruefalseのメソッドとして実装されています。

実装。

Io> true xor := method(cond, if(cond, false, true))
==> method(cond, 
    if(cond, false, true)
)
Io> false xor := method(cond, if(cond, true, false))
==> method(cond, 
    if(cond, true, false)
)

実行結果。

Io> false xor false
==> false
Io> false xor true
==> true
Io> true xor false
==> true
Io> true xor true
==> false


ここでちょっと疑問。演算子を登録しないでメソッドだけ設定したらどうなるのか?
インタプリタを起動しなおして。true xorfalse xorを上と同じように設定。

実行結果。

Io> false xor false
==> false
Io> false xor true
==> true
Io> true xor false
==> false
Io> true xor true
==> true


さてなにが起こったのか。


xorを次のようにしてみて引数の様子を見てみます。

Io> true xor := method(cond, ("cond = " .. cond) println; if(cond, false, true)) 
==> method(cond, 
    ("cond = " .. cond) println; if(cond, false, true)
)
Io> false xor := method(cond, ("cond = " .. cond) println; if(cond, true, false))
==> method(cond, 
    ("cond = " .. cond) println; if(cond, true, false)
)

実行結果。

Io> false xor false
cond = nil
==> false
Io> false xor true
cond = nil
==> true
Io> true xor false
cond = nil
==> false
Io> true xor true
cond = nil
==> true

引数の値はいずれもnil、つまり引数なしと扱われています。
これはつまり、true xor true(true xor()) trueと解釈されるということのようです。true xorは引数condnilになるためtrueを返します。このためtrue trueという式になるのですが、任意のオブジェクトにtrueを送るとtrueが返るようです。falseについても同様。

Io> Object true
==> true
Io> Object false
==> false

このため全体としては式の末尾の真偽値がそのまま式の値となる、ということのようです。


ところで。
演算子には+とか*とかあるじゃないですか。そいう記号を使った演算子を作りたいばあいどうするのか?
たとえば不一致を評価する演算子!=の替わりに<>を使いたいとか思ったとき、どうすればいいか。


答え。<>にメソッドを設定すればOK。

Io> OperatorTable addOperator("<>", 6)
(略)
Io> Object <> := method(arg, self != arg)
==> method(arg, 
    self != arg
)

実行結果。

Io> 1 <> 1
==> false
Io> 1 <> 2
==> true

ちなみに。今回のように別のメソッドとまったく同じ動作をするものを用意したいとき、上記のようにメソッドを定義する替わりに値をそのまま借りてしまうという方法があります。

Io> Object <> := Object getSlot("!=")
==> Object_!=()

この例では。!=の値を取り出してそのまま<>に設定してしています。


記号もスロット名に使えるとなると、こんなこともできてしまいます。

Io> 5 factorial
==> 120
Io> Number ! := Number getSlot("factorial")
==> Number_factorial()
Io> 5!
==> 120
Io> 10!
==> 3628800


Io面白いっ!


…「2日目」はまだつづく。