Squeak Smalltalk で万華鏡を書いてみた件


http://d.hatena.ne.jp/scinfaxi/20080719/1216413198 経由で。


まずは、オリジナルの Ruby コードの雰囲気を反映させつつ Squeak Smalltalk に移植してみました。

Ruby の rand() と違い Smalltalk の Integer>>#atRandom は 1 から指定した数までの乱数を発生すること、Smalltalk では配列のインデックスのスタートが 1 であること、Ruby での a[i]=v は Smalltalk では a at: i put v とメッセージ送信式で書かないといけない(条件分岐も同じ)こと、などが比較的目立つ違いかと思います。

| b a n c |

"万華鏡の具"
b := #('+' '☆' 'o' '*' '。' '+' '`' '、' '‥' '.' ',' 'O'
    '0' '●' '◎' '○' 'L' 'i' '@' '★' '、' ';' ':' '┗'
    '┣' '━' '┃' '┏' '┓' '┛' '┣' '┳' '┫' '┻' '╋' '┯'
    '┨' '┷' '┿' '┝' '┰' '┥' '┸' '╂' '─' '│' '┌' '┐'
    '┘' '└' '├' '┬' '┤' '┴' '┼').
a := Array new: 100 withAll: ' '.

25 timesRepeat: [
    n := 45 atRandom.
    c := b atRandom.
    (n between: 1 and: 5) ifTrue: [
        a at: n put: c.
        a at: 11-n put: c.
        a at: 90+n put: c.
        a at: 101-n put: c
    ] ifFalse: [ (n between: 11 and: 15) ifTrue: [
        a at: n put: c.
        a at: 31-n put: c.
        a at: 70+n put: c.
        a at: 101-n put: c
    ] ifFalse: [ (n between: 21 and: 25) ifTrue: [
        a at: n put: c.
        a at: 51-n put: c.
        a at: 50+n put: c.
        a at: 101-n put: c
    ] ifFalse: [ (n between: 31 and: 35) ifTrue: [
        a at: n put: c.
        a at: 71-n put: c.
        a at: 30+n put: c.
        a at: 101-n put: c
    ] ifFalse: [ (n between: 41 and: 45) ifTrue: [
        a at: n put: c.
        a at: 91-n put: c.
        a at: 10+n put: c.
        a at: 101-n put: c
    ]]]]]
].

"表示部分"
World findATranscript: nil.
Transcript cr; show: ('{1} {2} {3} {4} {5} {6} {7} {8} {9} {10}
{11} {22} {13} {14} {15} {16} {17} {18} {19} {20}
{21} {22} {23} {24} {25} {26} {27} {28} {29} {30}
{31} {32} {33} {34} {35} {36} {37} {38} {39} {40}
{41} {42} {43} {44} {45} {46} {47} {48} {49} {50}
{51} {52} {53} {54} {55} {56} {57} {58} {59} {60}
{61} {62} {63} {64} {65} {66} {67} {68} {69} {70}
{71} {72} {73} {74} {75} {76} {77} {78} {79} {80}
{81} {82} {83} {84} {85} {86} {87} {88} {89} {90}
{91} {92} {93} {94} {95} {96} {97} {98} {99} {100}' format: a)


で、同じことを行列オブジェクト(a Matrix)を使ってもっとシンプルに書けないかと試してみたのがこちら。

まず 10×10 の行列を用意し、その左上 5×5 の範囲に、25 個の具をまき散らします。最後に行方向と列方向に鏡像を作って完成です。

| 万華鏡の具 行列 |
万華鏡の具 := #(
    '+' '☆' 'o' '*' '。' '+' '`' '、' '‥' '.' ',' 'O'
    '0' '●' '◎' '○' 'L' 'i' '@' '★' '、' ';' ':' '┗'
    '┣' '━' '┃' '┏' '┓' '┛' '┣' '┳' '┫' '┻' '╋' '┯'
    '┨' '┷' '┿' '┝' '┰' '┥' '┸' '╂' '─' '│' '┌' '┐'
    '┘' '└' '├' '┬' '┤' '┴' '┼').
行列 := Matrix new: 10 element: ' '.

"具をまき散らす"
25 timesRepeat: [行列 at: 5 atRandom at: 5 atRandom put: 万華鏡の具 atRandom].

"鏡像の生成"
1 to: 5 do: [:行# | 行列 atRow: 11-行# put: (行列 atRow: 行#)].
1 to: 5 do: [:列# | 行列 atColumn: 11-列# put: (行列 atColumn: 列#)].

"表示"
World findATranscript: nil.
1 to: 10 do: [:行# |
    Transcript cr.
    (行列 atRow: 行#)
        do: [:各々 | Transcript show: 各々]
        separatedBy: [Transcript space]]


これを Ruby に逆移植すると、こんな感じ。

Ruby の Matrix は、ちょっと用途が違うみたいで使えなかったので、配列の配列で表現しています。このため、列を操作するのに #transpose を使っていったん行と列の入れ替えを行ない、列操作を行操作に代えて行なってから再び #transpose で元に戻すことをしています。

万華鏡の具 = [
  "","","","","","","","","","","","",
  "","","","","","","","","","","","",
  "","","","","","","","","","","","",
  "","","","","","","","","","","","",
  "","","","","","",""]
行列 = Array.new(10){Array.new(10){" "}}

#具をまき散らす
25.times{ 行列[rand(5)][rand(5)] = 万華鏡の具.choice }

#鏡像の生成
(0..4).each{ |行#| 行列[9-行#] = 行列[行#] }
行列 = 行列.transpose
(0..4).each{ |行#| 行列[9-行#] = 行列[行#] }
行列 = 行列.transpose

#表示
行列.each{ || puts 行.join(" ") }