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

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

Ioが面白い・その0 の 演習問題に挑む

演習問題が残ってた。

7つの言語 7つの世界

7つの言語 7つの世界


フィボナッチ数列は2つの連続する1で始まる。以降、それぞれの數は直前の2つの數の和になっている。1、1、2、3、5、8、13、21、…という具合だ。フィボナッチ数列のn番目の數を見つけるプログラムを書け。例えばfib(1)は1、fib(4)は3となる。また、この問題を再帰とループを用いて解け。

再帰(Recursive)のfibRと、ループ(Loop)のfibL

fibR := method(n,
    if(n <= 2, 1, fibR(n - 1) + fibR(n - 2))
)

for(i, 1, 10,
    writeln("fibR(", i, ") = ", fibR(i))
)

fibL := method(n,
    if(n <= 2, return 1)
    n1 := 1
    n2 := 1
    f  := 2
    (n - 2) repeat(f = n1 + n2; n2 = n1; n1 = f)
    f
)

for(i, 1, 10,
    writeln("fibL(", i, ") = ", fibL(i))
)

実行結果。

$ io self-study1.io 
fibR(1) = 1
fibR(2) = 1
fibR(3) = 2
fibR(4) = 3
fibR(5) = 5
fibR(6) = 8
fibR(7) = 13
fibR(8) = 21
fibR(9) = 34
fibR(10) = 55
fibL(1) = 1
fibL(2) = 1
fibL(3) = 2
fibL(4) = 3
fibL(5) = 5
fibL(6) = 8
fibL(7) = 13
fibL(8) = 21
fibL(9) = 34
fibL(10) = 55

分母がゼロのときゼロを返すように/演算子を変更するには、どうすればよいか。

Number div := Number getSlot("/")
Number /   := method(n,
    if(n == 0, 0, self div(n))
)

writeln("10 / 5 = ", 10 / 5)
writeln("10 / 3 = ", 10 / 3)
writeln("10 / 2 = ", 10 / 2)
writeln("10 / 0 = ", 10 / 0)

実行結果。

$ io self-study2.io 
10 / 5 = 2
10 / 3 = 3.3333333333333335
10 / 2 = 5
10 / 0 = 0

2次元配列のすべての要素の数値の総和を求めるプログラムを書け。

2次元配列の総和なのでsum2Dと名付けてみました。

Io> List sum2D := method(map(sum) sum)
==> method(
    map(sum) sum
)
Io> list(list(1,2,3), list(4,5,6), list(7,8,9)) sum2D
==> 45

リストに、そのリストのすべての数値の平均を算出するスロットmyAverageを追加せよ。リストに数値が含まれていない場合は何が起こるか?(おまけ:リスト内に数値以外の項目を見つけたら、Io例外を発生させるようにしてみよ。)

単純な実装。

Io> List myAverage := method(select(type == Number type) average)
==> method(
    select(type == Number type) average
)
Io> list(1,2,3,"one","two","three") myAverage
==> 2
Io> list("one","two","three") myAverage

  Exception: nil does not respond to '/'
  ---------
  nil /                                A3_List.io 6
  List average                         Command Line 1
  List myAverage                       Command Line 1

単純な実装では、数値が含まれないばあい例外が発生した。


数値以外が含まれていたら例外を発生させる。selectですべての要素について数値か否かを調べているので効率は良くないと思う。

Io> List myAverage := method(if(select(type != Number type) size == 0, average, Exception raise("including not Number")))
==> method(
    if(select(type != Number type) size == 0, average, Exception raise("including not Number"))
)
Io> list(1,2,3) myAverage
==> 2
Io> list(1,2,3,"one","two","three") myAverage

  Exception: including not Number
  ---------
  Exception raise                      Command Line 1
  List myAverage                       Command Line 1

2次元リストを実現するプロトタイプを書け。dim(x, y)メソッドは、要素数がxのリストyのリストを割り当てる。set(x, y, value)はリストに値を設定し、get(x, y)はリストの値を取得する。

ボーナス問題:(new_matrix get(y, x)) == matrix get(x, y)が成立するように、元のリストの転置メソッドを書け。

行列をファイルに書き出し、ファイルから行列を読み込め。

次のスクリプトを「Matrix.io」というファイル名で保存して、以降のスクリプトの実行はこのファイルがあるフォルダで実行するものとします。

Matrix := Object clone do(
    dim := method(x, y,
        matrix   := Matrix clone
        matrix x := x
        matrix y := y
        matrix do(
            values := List clone setSize(y)
            for(i, 0, y - 1, values atPut(i, List clone setSize(x)))
        )
    )

    set := method(x, y, value,
        values at(y) atPut(x, value)
    )

    get := method(x, y,
        values at(y) at(x)
    )

    setValues := method(
        elems := call evalArgs
        i := 0
        for(row, 0, y -1,
            for(col, 0, x - 1,
                self set(col, row, elems at(i))
                i = i + 1
            )
        )
        self
    )

    transpose := method(
        matrix := dim(y, x)
        for(row, 0, y - 1,
            for(col, 0, x - 1,
                matrix set(row, col, get(col, row))
            )
        )
        matrix
    )

    asString := method(
        "[" .. values map(elem, "[" .. elem join(", ") .. "]") join(", ") .. "]"
    )

    asScript := method(
        call target type .. " dim(" .. x .. ", " .. y .. ") setValues(" .. values map(join(", ")) join(", ") .. ")"
    )
)


行列を作って、値を入れて、転置する。

Io> m := Matrix dim(3,5)
==> [ [nil, nil, nil], [nil, nil, nil], [nil, nil, nil], [nil, nil, nil], [nil, nil, nil] ]
Io> for(y, 0, 4, for(x, 0, 2, m set(x, y, y * 3 + x)))
==> list(12, 13, 14)
Io> m
==> [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14] ]
Io> m transpose
==> [ [0, 3, 6, 9, 12], [1, 4, 7, 10, 13], [2, 5, 8, 11, 14] ]


ファイルへ保存。

m := Matrix dim(3, 5)

for(y, 0, 4,
    for(x, 0, 2,
        m set(x, y, y * 3 + x)
    )
)

fileName := "matrix.txt"

file := File with(fileName) openForUpdating
file write(m asScript, "\n")
file close

ファイルから読み出し。

fileName := "matrix.txt"

file := File with(fileName) openForReading
m2 := doString(file contents)
file clone

m2 println

実行結果。

$ io self-study6.io 
$ cat matrix.txt 
Matrix dim(3, 5) setValues(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)
$ io self-study7.io 
[ [0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14] ]


スクリプトの形で保存して読み込みはdoString頼みってのがちょっとあざとい感じがしなくもないですが。それでもdoFile("matrix.txt")とするのはこらえて、Fileを使って読み出してみました。