023. Objective-CプログラマーのためのSC言語入門(2) またりさまClassを実装

SC言語編に突如突入したSCCR23回目.
言語仕様を眺めても言語は理解できない.何か作っていくなかで本当の理解は得られるのです!
ということで
前回,クラスの初歩までやったので
今回はまたりさまクラスをつくることにする.

またりさまとは

またりさまは三輪眞弘教授の逆シミュレーション音楽の第一弾です.

逆シミュレーション音楽
http://www.iamas.ac.jp/~mmiwa/rsm.html

またりさま
http://www.iamas.ac.jp/~mmiwa/XORensemble.html

・8人のプレーヤーが同じ方向に輪になって座る。(全員が次の人の背中を見ている、輪になった電車ごっこ状態)
・全員が両手に楽器、右手に鈴、左手にカスタネットをもつ。
・「鈴カケ」のルールに従って、自分の肩がたたかれたら自分の前に座っている人の肩をたたいて楽器を鳴らす。

「鈴カケ」のルール:
・鈴を鳴らすときは次の人の右肩をたたいて音を出す。
・カスタネットを鳴らすときは次の人の左肩をたたいて音を出す。

それぞれは自分が最後に鳴らした楽器によって毎回「スズ状態」と「カケ状態」(それぞれスズ、カスタネットに対応する)の2種類の状態のどちらかになり、以下の規則に従って、次の番に鳴らすべき楽器を決める。

スズ状態の時、<同じ楽器を鳴らす>
・自分の背中で鈴が鳴らされたら自分も鈴を鳴らす。(スズ状態のまま)
・自分の背中でカスタネットが鳴らされたら自分もカスタネットを鳴らす。(その後、カケ状態になる)

カケ状態の時、<もうひとつの楽器を鳴らす>
・自分の背中で鈴が鳴らされたら自分はカスタネットを鳴らす。(カケ状態のまま)
・自分の背中でカスタネットが鳴らされたら自分は鈴を鳴らす。(その後、スズ状態になる)

これを8人が連続して次々と繰り返し行う。

さて,この状態の変化を数値で考える.

スズ状態の時
スズ : スズ = スズ
スズ : カケ = カケ

カケ状態の時
カケ : スズ = カケ
カケ : カケ = スズ

スズを0
カケを1とすると

0 : 0 = 0

0 : 1 = 1

1 : 0 = 1

1 : 1 = 0

となります.
これはXOR演算です.
C言語で書くなら

printf(“0 ^ 0 = %d\n”, 0 ^ 0);

printf(“0 ^ 1 = %d\n”, 0 ^ 1);

printf(“1 ^ 0 = %d\n”, 1 ^ 0);

printf(“1 ^ 1 = %d\n”, 1 ^ 1);

SCで実行してみるとよい.
SCのXOR演算はbitXor.

//1行ずつ実行

bitXor(0,0);

bitXor(0,1);

bitXor(1,0);

bitXor(1,1);

>>0

>>1

>>1

>>0

となります.

またりさまに戻ると,自分の状態は前の人と自分の状態(0か1か)で決まります.

初期値が

0 1 1 0 1 1 0 1

として,右から見て最初の桁(1)を最初の人とすると
最初の人はカスタネットを鳴らします.
次の人はスズなので,
最初の人(1) ^ 次の人(0) => 次の人の状態が1になる

ということになります.

なので

0 1 1 0 1 1 0 1

から

0 1 1 0 1 1 1 1

になる.

またりさまではメンバーは円形に繋がっているわけなので,0番の人は7番の人と繋がっていることになります.
なので,7番目の人は0番目の人の肩を叩く(XOR演算する).

ということで,

0 1 1 0 1 1 0 1で始めると

01101101

01101111

01101011

01101011

01111011

01011011

01011011

11011011

11011010

11011010

11011110

11010110

11010110

11110110

10110110

10110110

10110111

10110101

10110101

10111101

という風に変化していく.

これは,2進数ですね.

10進数に直すと

01101101 = 109

01101111 = 111

01101011 = 107

01101011 = 107

01111011 = 123

01011011 = 91

01011011 = 91

11011011 = 219

11011010 = 218

11011010 = 218

11011110 = 222

11010110 = 214

11010110 = 214

11110110 = 246

10110110 = 182

10110110 = 182

10110111 = 183

10110101 = 181

10110101 = 181

10111101 = 189

こうなる.

SCで実装してみる
まずはクラスの設計を考える.

まずは全体の状態が必要.

player[8]
みたいに配列で扱うと簡単ですが,そんなことせずとも,プレーヤーは整数一個で表現できる.
上の表を見ると,109一個で01101101が表現できているわけです.

var decimal = 2r01101101;

decimal.postln;

>> 109

2進数表現を得るには,Integer ClassにasBinaryStringというmethodがある.

asBinaryString { | width=8 |

    ^this.asStringToBase(2, width)

}

var decimal = 109;

decimal.asBinaryString.postln;

>>01101101

8bitの場合,255が最大なので
2の8乗は256.ということは0~255.

var decimal = 255;

decimal.asBinaryString.postln;

>>11111111

var decimal = 256;

decimal.asBinaryString.postln;

>>00000000

あら,桁があふれた.

asBinaryStringでwidthが8(8bit)になっているので
widthに9を渡してやる.

var decimal = 256;

decimal.asBinaryString(9).postln;

>>100000000

でた.

asBinaryString { | width=8 |

    ^this.asStringToBase(2, width)

}

の | |という書式が気になります.なんだこれは.

Function Creation via Partial Application

f = {|x| x + 2 };

という書き方で,引数が定義できるらしい.

f = {|x| x + 2 };

f.value(3);

>> 5

f = {arg x; x + 2 };

f.value(3);

>> 5

おんなじこと.

では,またりさまを01101101(109)で始めた場合で,次の状態を計算する方法を考える.

1番目のBit(1)と2番目のBit(0)でXORして,結果を2番目のBitに保存したい.

特定のBitが0か1か取得するには

2r01101101 >> 0 & 1;

2r01101101 >> 1 & 1;

2r01101101 >> 2 & 1;

2r01101101 >> 3 & 1;

2r01101101 >> 4 & 1;

2r01101101 >> 5 & 1;

2r01101101 >> 6 & 1;

2r01101101 >> 7 & 1;

で各Bitが1か0か計算できる.

01101101 >> 2 & 1;

を例にとると
01101101を >> 2すると右にビットがずれて
00011011になる.
100000001なので

00000001

00011011

をAND演算.

00000001

00011011

00000001


ということで,3桁目は1であることが分かる.

では,3桁目が1の数字を作るには

var bin = 1 << 2;

bin.asBinaryString.postln;

>>00000100

これでできる.

ここまで押さえたところで例題.

01101101

で,3桁目と4桁目をXORしたいとする.
3桁目を取得.

var decimal = 2r01101101;

var bit = decimal >> 2 & 1;

bit.postln;

>> 1

3桁目のBitを取得して,他は0なBit表現を作る

var decimal = 2r01101101;

var bit = decimal >> 2 & 1;

bit = bit << 3;

bit.asBinaryString.postln;

>>00001000

元の数字とXORする


var decimal = 2r01101101;

var bit = decimal >> 2 & 1;

bit = bit << 3;

decimal = bitXor(decimal, bit);

decimal.asBinaryString.postln;

>> 01100101

01101101

01100101

になりました.

(>>は +>>を使うべきかもしれん)

Matari Classを実装

じゃ,これを踏まえてMatari Classを実装してみます.

必要なインスタンス変数は

  • 何人のまたりさまなのか
  • 今の状態(0010101とか)
  • 今誰の番なのか

必要なMethodは

  • 初期値と何人かを引数にしてNew
  • 一人一人のアクションが必要.
  • 状態を2進数で見たい

で実装するとこうなる.

Matari : Object

{

    var <>numofPlayer;

    var <>decimal;

    var <>index;

    

    *new{arg numofPlayer=2, decimal=0, index=0;

        ^super.newCopyArgs(numofPlayer,decimal,index);

    }

    

    matari{

        var nextIndex = (index + 1) % numofPlayer;

        index = index % numofPlayer;

        decimal = bitXor(decimal, (decimal >> index) & 1 << nextIndex);

    

        index = index + 1;

    }

    

    asBinaryString{

        ^decimal.asBinaryString(numofPlayer).value;

    }

}


かなりシンプルに書けた.

indexというのは,今何番目の人の番か?というのに使います.
そのindexによって,上記何番目の人と何番目の人をXORするかを判断.
このClassをMatari.scとでもして前回書いたClass用フォルダに入れてコマンド + Kでコンパイルして

var matari = Matari.new(8,109);

matari.asBinaryString.postln;

matari.matari();

matari.asBinaryString.postln;

matari.matari();

matari.asBinaryString.postln;

>>01101101

  01101111

  01101011


おお,ちゃんと動く.

延々書くのも頭が悪いのでforで書くと

var matari = Matari.new(8,109);

matari.asBinaryString.postln;

for(0,44100,{

    matari.matari();

    matari.asBinaryString.postln;

    }

);


きっとPlayerが一人だとBUGります.あと,32人超えると動かないはず.

んじゃ,今度はこれを使って何ができるかを考えていく.

このまたりさまのアルゴリズムを使ったシンセサイザーであるまたりさまBinaryはこちら

Leave a Reply