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になる.
1は00000001なので
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はこちら.