2017/04/29

SuperColliderの関数と変数

関数

sclangでは {} で囲んだ部分が関数になります。

{} 内の最後の式の評価値が返ってきます。関数の途中で値を返すことはできないようです。

関数を呼び出すには value を使います。

f = { |a, b| a + b };
f.value(4, 5).postln;   // 9
postln(value(f, 4, 5)); // 9 上と同じ。

g = { |a(10)| a * a * a }; // 引数のデフォルトを設定。
g.value.postln;            // 1000

h = { |a, b ... arr| // ... arr で残りの引数をArrayにしてarrに代入。
  var sum = 0;
  arr.do{ arg item; sum = sum + item }; // doは他言語のforeachに相当。
  sum * a * b
};
h.value(2, 3, 4, 5, 6).postln; // 90

() ブロックと組み合わせると以下のような書き方ができます。

f = { |a, b| a + b };
f.value(
  c = 10.rand;
  c.postln;
  c, c
);

変数

以降の例は一行ずつではなく、範囲選択して実行してください。エラーの種類が変わる場合があります。

var で変数を宣言します。 var による変数の宣言は関数の始めでしか行えません。また一番外側のスコープは関数とみなされています。

var alpha = 100;
var beta = 200;

alpha.postln; // 100
beta.postln;  // 200

// var gamma; // コメントを外すとエラー。

変数のスコープについて確認してみます。

var alpha = 100;
var beta = { |value|
  var gamma = 200;
  value + gamma.value
};

beta.value(alpha).postln; // 300

// gamma.postln; // コメントを外すとエラー。

グローバル変数

これまでの例では var による宣言なしに変数を使っている箇所がありました。それらは全てInterpreterによって用意されたグローバル変数です。

InterpreterはSuperCollider IDEの起動と共にインスタンスが作られ [a-z] をグローバル変数として保持しています。その中でも s は特殊でServerが代入されます。 s は再代入などによって変更しないことが推奨されています。

a = 100;
b = 200;

a.postln; // 100
b.postln; // 200

c = 300;  // グローバル変数なのでOK。

環境変数とEnvironment

sclangでは環境変数(Environment Variable)という変数を使うことができます。 ~ から始まる名前は currentEnvironment に所属する環境変数となります。

currentEnvironment.postln; // Environment[  ]

~alpha = 100;
~alpha.postln; // 100

~beta = 200;
~beta.postln; // 200

currentEnvironment.postln; // Environment[ (beta -> 200), (alpha -> 100) ]

SuperCollider IDEで新しいセッションを開始した時は currentEnvironment には topEnvironment が代入されています。 topEnvironment はどこからでも参照できます。

var env = Environment.make;

(currentEnvironment === topEnvironment).postln; // true
(currentEnvironment === env).postln;            // false

~value = 100;
~value.postln; // 100

env.push;

(currentEnvironment === topEnvironment).postln; // false
(currentEnvironment === env).postln;            // true

~value.postln; // nil
~value = "hoge";
~value.postln; // hoge

topEnvironment.at(\value).postln; // 100

env.pop;

(currentEnvironment === topEnvironment).postln; // true
(currentEnvironment === env).postln;            // false

~value.postln; // 100

Environmentはスタックに格納されているようです。 currentEnviromnet は常にスタックの一番上を指しています。新しいEnvironmentを作った時は .push でスタックに挿入できます。 .pop はどのEnvironmentから呼び出されるかにかかわらず常にスタックの一番上を取り出すようです。

var env1 = Environment.make;
var env2 = Environment.make;

var checkEnv = {
  [
    (currentEnvironment === topEnvironment),
    (currentEnvironment === env1),
    (currentEnvironment === env2)
  ]
};

checkEnv.value.postln; // [ true, false, false ]
env1.push;

checkEnv.value.postln; // [ false, true, false ]
env2.push;

checkEnv.value.postln; // [ false, false, true ]
env1.pop;

checkEnv.value.postln; // [ false, true, false ]
env2.pop;

checkEnv.value.postln; // [ true, false, false ]