変数と関数 - Cinderella Documentation
Description: The user's guide to the Interactive Geometry Software Cinderella

コンテンツにスキップ

# 変数と関数

CindyScript では、変数と関数は事前に宣言される必要はありません。それらは必要に応じて作られ、明確な型を持ちません。ここが他の多くのプログラミング言語と異なるところです。この節では、どんな状況下で関数や変数が作られるかを学びます。また、変数を削除したり初期化する方法や変数の適用範囲についても学びます。

### 関数の定義

CindyScript で関数を定義することはとても簡単です。関数の名前を決め、変数のリストを作り、関数の内容を書きます。引数の型や関数の型を明示する必要はありません。 では、簡単な関数の例を提示しましょう。関数 `f` を次のように定義します。

    f(n):=sum(1..n,i,i^2)

この例ではこの例では、1から `n` までの２乗の和を計算します。従って、 `f(4)` を実行すると `30` を表示します。

２つ以上の引数を持つ関数も同じように定義できます。 a と b が2次元のベクトルを表す次の関数では、このベクトルによって定義される正方形を描きます。

    sq(a,b):=(
      n=(b-a);
      n2=(-n_2,n_1);
      draw(a,b);
      draw(a,a-n2);
      draw(b,b-n2);
      draw(a-n2,b-n2);
    )

このコードでは少し面白いことが起こります。まず、このコードは原則として手続き的になっています。関数の本体は`(命令文_1; … ;命令文_k)`の形をしています。さらに、関数は変数 n と n2 を使います。これらの変数は、関数が最初に呼ばれたときに作られますが、局所変数ではありません。その値は、あとで関数が呼ばれたときにも参照されます。

この関数の戻り値は、関数の中で最後の文で評価された値です。したがって、次の関数では、3つの数の平均値を計算して戻り値とします。

    mean(a,b,c):=(
      sum=a+b+c;
      sum/3;
    )

関数は型を明示しないので、引数に実数以外のものを与えることも可能です。関数は可能な限り多様に解釈され、その中で使われている演算の一般性によってのみ制限を受けます。例えば, `mean([3,4],[2,7],[4,7])` は `[3,6]`を意味します。

### 再帰的関数

関数は再帰的に定義することもできます。再帰の各段階において、パラメータが更新されます。次のコードでは数の階乗を計算します。

    fac(n):=if(n==0,1,n*fac(n-1));

つぎのやや複雑なコードでは、２つの正の整数の最大公約数を計算します。ユークリッドの互除法です。

    gcd(a,b):=if(b==0,                //再帰の終了条件
                   a,                 //最後にaを戻り値とする
                   if(b>a,            //bがaより大きければ
                     gcd(b,a),        //aとbを入れ替えて再帰
                     gcd(b,mod(a,b))  //そうでなければaをbで割った余りを用いて再帰
                     )
                 );

### 変数の定義

CindyScript における変数は、コード内で最初に現れたときに定義されます。変数はプログラムが終わるまで存在します。変数はどんな種類のもの(数、文字列、ブール値、リスト、幾何学的な点、あるいはプログラムでさえ)も含みます。 次のプログラムでは x に 4 を、 b に `[9,27]` を、 c に`[18,54]` を代入します。

    x=3;
    b=[x^2,x^3];
    c=2*b;

関数で定義される変数は、関数の外側からも見えます。例外は、関数のパラメータと、明示的に定義された局所変数です。次のプログラムは、変数の有効範囲を例示します。

    f(x):= (
      x=x+x;
      println(x);
      y="User"
    );
    x="Hello ";
    y="World";
    println(x+y);
    f(x);
    println(x+y);

実行結果は次の通りです。

    Hello World
    Hello Hello
    Hello User

関数の中における局所変数は `regional(…)` 命令によって明示的に定義されます。関数の外に出れば自動的に消去されます。さきほどのプログラムを少し変更した次のプログラムでは `y` は、関数の中で局所変数です。

    f(x):= (
      regional(y);
      x=x+x;
      println(x);
      y="User";
    );
    x="Hello ";
    y="World";
    println(x+y);
    f(x);
    println(x+y);

実行結果は次の通りです。

    Hello World
    Hello Hello
    Hello World

ループ変数も局所変数とみなされます。

### 関数内での変数の拘束

関数の中で使われる変数は、それが局所変数として定義されていないときは、関数の実行後も利用できる状態にあります。また、関数の評価に影響を与えるような値を持つかも知れません。 　たとえば、次のコードを見てください。

    a=3;
    timesa(x):= x*a;
    println(timesa(2));
    a=5;
    println(timesa(2));

実行結果は次のようになります。

    6
    10

`timesa(2)` の戻り値（評価結果）は大域的変数 `a` の値に依存し、まず `a=3` として評価されます。そのあと、 `a=5` と再定義されると、関数 `timesa` の結果は変化します。これはあるときにはそのつもりで実行されることもあれば、そうでないこともあります。関数を定義したときに、その値が変化しないように「凍結」させたいこともあるでしょう。 `::=` で関数を定義すれば変数の値を凍結することができます。この演算子は、変数に割り当てられた値をすべてコピーし、関数内に拘束します。先ほどのコードを次のように変えます。

    a=3;
    timesa(x)::= x*a;
    println(timesa(2));
    a=5;
    println(timesa(2));

実行結果は次の通りです。

    6
    6

関数が呼ばれるたびに、変数 `a` には元の値が書き戻されます。この拘束のプロセスは、関数内で使われるすべての変数に及ぶだけではありません。 それは、関数の実行に関係する すべての 変数に及びます。

この拘束を回避する方法があります。変数の値は修飾子を用いると明確に設定できます。次のコードを見てください。

    a=3;
    timesa(x)::= x*a;
    println(timesa(2));
    println(timesa(2,a->10));

結果は次のようになります。

    6
    20

### 定数

数学では、円周率πや虚数単位iのような定数がよく使われます。πは「pi」、iはそのままiです。これらの定数は CindyScript ではあらかじめ定義されています。ですから、複素数を 3+i*5 のように書くことが可能です。しかし、これらの変数の値を定義しなおすこともできます。したがって、ループ変数としてこれらの変数を使うこともできます。 次のプログラムがこのことを示しています。

    println(i);
    repeat(4,i,println(i));
    println(i);

実行結果は次のようになります。

    0 + i*1
    1
    2
    3
    4
    0 + i*1

もしも、複素数を使うときに、変数 i が何かの値で上書きされてしまった場合には、 `complex([0,1])`演算子によって戻すことができます。その他の既定の定数は、論理で使う true と false 空リストの nil です。

もう一つ、重要な既定の変数があります。幾何学的要素の名前は、その名前の変数として定義されたものとみなします。したがって、例えば、点 A は変数`A` によってアクセスできます。この件については 幾何学要素へのアクセスに詳しく書かれています。

なお、Cindyscriptでは、ネピア数eは用意されていません。eを使うときは、exp()関数を用いて e=exp(1) とします。

### ユーザー定義データ

ユーザーによる定義データを幾何の要素に結びつけることができます。簡単ですが強力なものです。そのためには、演算子 `:` を使います。コロンのあとの任意の文字列がデータにアクセスするためのキーとして付け加えられます。このキーで任意の値が付加されます。

使用法を例で説明しましょう。A と B は幾何のオブジェクトです。次のコードは、それらにいくつかのデータを付加します。

    A:"age"=17;
    B:"age"=34;
    A:"haircolor"="brown";
    B:"haircolor"="blonde";

データは同じキーでアクセスされます。したがって、次のコードでは

      forall(allpoints(),p,
        println(p:"age");
        println(p:"haircolor");
    )

出力結果は次のようになります。

    17
    brown
    34
    blonde

幾何オブジェクトに付与されているキーのリストには `keys(...)` 演算子でアクセスできます。したがって先ほどの例では、次のコードにより

    print(keys(A));

次の結果を得ます。

    ["age","haircolor"];

付与するデータはリストで与えることもできます。これにより、変数のようなカスタムデータを作ることができます。次のコードはこの例です。

      a=[];
      a:"data"=18
      print(a:"data")

注意: この機能は今後変更される可能性があります。データ構造のようなものをサポートすることが計画されています。ですから、将来のリリースにおいて互換性を持たなくなるかもしれません。