変数

このセクションでは Curl® 言語について説明します。具体的には、以下の項目について説明します。

変数入門

要約:
  • 変数は、letdef を使用して宣言することができます。
  • 初期化されていない変数は、暗黙的に既定値が割り当てられます。
  • 変数の型を宣言しない場合、型は、def 構文を使用している場合は初期値から暗黙的に適用され、そうでない場合は any 型が適用されます。
  • コードに any データ型の変数を使用すると実行に時間がかかったり、より多くのリソースを使用したりすることがあります。
変数とは、値をメモリに格納するための場所のことです。それぞれの変数には識別子とデータ型があり、識別子は名前またはハンドルともよばれます。作成中のプログラムで変数を定義すると、その後はプログラム内で変数名を使って値にアクセスできます。値が変更できない constant として宣言しない限り、変数を定義した後でも値を変更することが可能です。
変数は、let および def 構文を使用して宣言することが可能ですが、これについては後で詳しく説明しています。また変数は、if-non-null のような他の構文や、type-switch のcase 文の中でも導入することができます。
変数を定義することにより、メモリを予約して初期値を変数に代入するよう実行環境に指示します。変数を宣言するときは、変数の名前とデータ型を指定します。変数に割り当てられるメモリの量は、データ型によって異なります。変数を宣言するとき、オプションで変数の値を初期化することができます。変数を初期化しない場合、既定値が変数に代入されます。def 構文を使用して定義された変数には、明示的イニシャライザが必要です。
変数の型を宣言しない場合、変数の型は自動的に選択されます。変数が def を使用して宣言された場合は、変数の型はそれに代入される値の型と同じものが適用されます。変数が let を使用して宣言された場合は、any データ型になります。any データ型は、名前からもわかるとおり、任意のデータ型を格納します。
any データ型をインクルードすると、Curl 言語に多くの機能が追加されます。初めての開発者は型の指定を気にかける必要が無いので、Curl 言語によるプログラム作成の学習が容易になることがよくあります。また、型指定を持たないスクリプト言語の経験がある人の場合、Curl 言語への移行はさらに容易です。型指定のないコードの方が読みやすいと多くの人が主張しています。ただし、any データ型を使用すると実行時に関連する負荷がかかるので注意が必要です。つまり any データ型を使用する変数があると、コードの実行時により長い時間がかかったり、より多くのリソースを使用することがあります。any データ型の詳細については、「any データ型」のセクションを参照してください。
型の指定がない let 宣言は若干コード実行速度が遅くなる可能性があるため、allow-implicit-any-declarations? コンパイラ ディレクティブを false に設定することにより、この形式での記述を無効にすることができます。またこれは、fast?safe?careful? または stringent? のいずれかのディレクティブを true に設定することによっても行うことができます。詳しくは、「コンパイラ ディレクティブ」の章を参照してください。
変数を宣言すると、変数のデータ型と一致しない値は代入されません。変換とキャスティングの詳細については、「データ型の変換」セクションを参照してください。

識別子の選択

要約:
  • 英字、日本語、または下線が開始文字となります。
  • 以降は、英字、日本語、数字、下線、疑問符、およびハイフンのみを使用します。
  • 識別子では、大文字と小文字および全角と半角が区別されます。
識別子は変数、プロシージャ、クラス、またはその他の Curl 言語ソース コードで作成できるものに付ける名前です。識別子を選択するときは、数字で始まらない文字の組合せを使用します。
ローマンアルファベットをコードで使用しない開発者は、Unicode® 文字セットにある多数の言語を使用して識別子を記述できます。 例えば、日本の開発者は、漢字、ひらがな、カタカナ、ローマ字、ASCII を使用して変数を表記できます。 変数は大文字と小文字および全角と半角が区別されます。
特に、識別子を選択するときは次のルールに従います。
たとえば、有効な Curl 言語識別子は次のようになります。
以下のリンクをクリックすると、言語別の識別子の例が表示されます。
Curl 言語の構文には、その他のたいていのプログラム言語とは異なり、技術的に予約されている特別な用語があります。そのため、これらの Curl 言語用語を識別子として使用するのは構文違反ではありませんが得策ではありません。たとえば、用語 field は新しい型の各インスタンス内で個別な型のデータ用に領域を予約するためにクラスを定義するときに使用されます。Curl 言語では、field 識別子の付いた変数を宣言したり、いくつかのクラスのフィールド名としてこの用語を使用する変数を宣言することさえもできます。ただし、こうしたことは可能ですが、コードが読みにくくなりデバッグもより困難になります。そのため、Curl 言語のコマンドや修飾子を識別子として使用するのは推奨しません。
識別子は任意のものを選択できます。 ただし、理解しやすい識別子を選択して、自分自身またはコードを読んで管理する他の開発者にとっても、 簡潔で理解しやすいコードを作成するべきです。 一貫性を保つため、次の表に記載したガイドラインの概要に従うことを推奨します。 (日本語を含む)表意文字を使う場合は、 以下のガイドラインに類似したものまたはローカルな慣習にしたがってください。
識別子の型ガイドライン
パッケージ各単語間にハイフンの付いたすべて大文字の文字を使用します。PACKAGE-NAME
クラス各単語について、最初の文字に大文字を使用します。ClassName
クラス メンバ各単語間にハイフンの付いたすべて小文字の文字を使用します。class-member-name
プロシージャ各単語間にハイフンの付いたすべて小文字の文字を使用します。procedure-name
変数または定数各単語間にハイフンの付いたすべて小文字の文字を使用します。variable-name
列挙型各単語ごとに、最初の文字が大文字を使用します。MyType
列挙型の要素各単語間にハイフンの付いたすべて小文字の文字を使用します。type-element

予約語

要約:
  • 識別子を選択するときは、予約語を使用しないでください。
識別子を選択するときは、予約語を使用しないでください。Curl 言語のこのリリースにおける予約語は次のとおりです。
...andasaconstruct-supercurl-file-signaturedefdivfalseisalet
さらに、(上記の表に記載されているもの以外にも) 構文などで使用されるキーワード名を付けて変数を定義するとコードが解析できなくなる一定のコンテキストがあります。そうしたコンテキストの例としては次のものがあります。

グローバル変数およびグローバル定数の宣言

グローバル変数およびグローバル定数はトップレベル コードでのみ宣言できます。(トップレベル コードは、他のコード ブロック内でネストしていないコードであることに注意してください。)アプレットでは、グローバル変数およびグローバル定数を宣言する let または def ステートメントを中かっこで囲う必要があります。それ以外の場合、宣言は通常のテキストとして処理され表示されます。パッケージでは、let/def ステートメントを中かっこで囲う必要はありませんが、スタイルとしては、グローバル変数を let または def を使用して宣言されたローカル変数と区別するために、中かっこを使用することをお勧めします。グローバル変数またはグローバル定数を宣言する let/def ステートメントの構文は次のとおりです。
構文: {let [access] [modifier-list] name[:type][ = value]}

または

{def [access] [modifier-list] name[:type] = value}
説明:
access
オプションのアクセス属性です。有効な属性は public および package です。access を指定しない場合、アクセス属性は既定で package になります。
public を指定してパブリック変数またはパブリック定数を宣言します。全てのソースコードはパブリック変数またはパブリック定数にアクセスできます。
package を指定して package-protected 変数または定数を宣言します。同じパッケージ内のコードだけがこの変数または定数にアクセスできます。
modifier-list修飾子のリスト (省略可能)。有効な修飾子は constant および deprecated だけです。グローバル定数を宣言するときは、constant を指定します。constant を指定する場合は、明示的な value も指定しなければなりません。def 構文を使用する場合は、constant 属性は自動的に割り当てられ、明示的に指定されません。このグローバル変数または定数を新規に作成するコードの中で使うことが推奨されていない場合、deprecated を使用します。これらは将来のバージョンでサポートされなくなる可能性があります。複数の修飾子を指定する場合は、各修飾子をスペースで区切ります。
name変数または定数の名前です。Curl 言語における変数の命名規則では、複数の単語をつなぐ際に、小文字とハイフンの使用が推奨されています (たとえば variable-name)。
type変数または定数のデータ型です。有効なデータ型を指定します。value が指定されていない場合は、type が必要です。let 構文が使用されている場合に、type が指定されていない場合は、any が割り当てられ、def 構文が使用されている場合は、コンパイル時の value と同じ型が割り当てられます。
value変数または定数の値です。value は変数ではオプションですが、定数には必須です。type が指定されていない場合は、value が必要です。value が指定されていない場合は、必要に応じて null または0 または false が適用されます。
次に例を示します。
{let int-var:int = 4}
{let dbl-var:double}
{let constant int-const:int = 9}
{let constant char-const:char = 'a'}
{let public char-var:char}
{def pi = 3.14159}
注意: access および modifier-list を任意の順序で指定できます。たとえば、
let public constant ...
および
let constant public ...
はいずれも有効です。
複数の変数または定数が同じ修飾子とアクセス属性を持つ場合、1 つの let/def ステートメントでそれらを宣言できます。以下の構文を使います。
構文: {let [modifier] [access] name[:type][ = value],
      name[:type][ = value],
      ...
      name[:type][ = value]
}


または

{def [modifier] [access] name[:type] = value,
      name[:type] = value,
      ...
      name[:type] = value
}
次に例を示します。
{let public
    int-var:int = 4,
    dbl-var:double
}
{def public
    int-const = 9,
    char-const = 'a'
}

ローカル変数および定数の宣言

コード ブロック内でローカル変数とローカル定数を宣言できます。変数のスコープは、変数の宣言されるコード ブロック内に限られます。ローカル変数では、中かっこで let/def ステートメントを囲うのはオプションで、スタイルとしてはお勧めしません。let を使用して定義されたローカル変数は constant として宣言できますが、通常は単に def 構文を使用するほうが簡単です。ローカル変数を宣言する let/def ステートメントの構文は次のとおりです。
構文: let [constant] name[:type][ = value]

または

def name[:type] = {metvar value}
説明:
name変数名です。Curl 言語における変数の命名規則では、複数の単語をつなぐのに小文字とハイフンの使用が推奨されています (たとえば variable-name )。
type変数のデータ型です。有効なデータ型を指定します。value が指定されていない場合、type が必要です。type が指定されていない場合は、let 構文が使用されている場合は any が適用され、def 構文が使用されている場合は value と同じコンパイル時の型が適用されます。
value
変数の初期値です。type が指定されていない場合、value が必要です。value が指定されていない場合は、必要に応じて null または 0 またはfalse が適用されます。
変数が constant 属性を持つ場合、初期値を指定する必要があります。
次に例を示します。
{do
    let int-var:int = 4
    let dbl-var:double
    let char-var = 'z'
    def message = "Hello World"
}
また、1 つの let/def ステートメントを使用して複数のローカル変数を宣言できます。以下の構文を使います。
構文: let name[:type][ = value],
    name[:type][ = value],
    ...
    name[:type][ = value]


または

def name[:type] = value,
    name[:type] = value,
    ...
    name[:type] = value
次に例を示します。
{do
    let int-var:int = 4,
        dbl-var:double,
        char-var = 'z'

    def one = 1, two = 2
}

クラス変数および定数の宣言

クラス変数は、グローバル変数に似ています。ただし、変数を参照するときはクラス名も使用する必要があります。さらに、変数宣言はトップレベル コードではないので、宣言を中かっこで囲うのはオプションです。クラス変数を宣言するときは、クラス メンバの通常のアクセス属性と修飾子を指定できます。詳細については、「クラス定数、変数およびプロシージャ」を参照してください。

letdef の違い

def 構文は Curl API のバージョン 6.0 で導入されました。これにより型を明示的に指定することなく定数を定義することができます。letdef の主な違いは、def は暗黙的に定数になり、明示的に value 式を要求し、厳しいコンパイラ ディレクティブのもとでも type がオプションであるということです。let を使用した場合と異なり、any 型を代入する代わりに、def 構文は定数を初期化するのに使用した value 式から型を適用します。適用される型は、value 式の コンパイル時の型で、実行時の型ではありません。つまり、この型は value 式のコンパイル時に決定され、実際の実行時の型はより具体的な型である可能性があります。イニシャライザごとに1つの変数がある def ステートメントでは、通常次のステートメント
def name = value
let constant name:{compile-time-type-of value} = value
と同等です。
たとえば、次のコード ボディー
def message = "hi"
def numbers = {IntVec 1,2,3,4}
let constant message:String = "hi",
let constant numbers:IntVec = {IntVec 1,2,3,4}
と同等です。
次の式と同等のものはありません。
def (name1, name2) = values
これは複数の値を持つ式のコンパイル時の型を計算する構文がないためですが、原則は同じで以下のようなコードを書き込めます。
def (val, n-consumed) = {string.to-int}
def 宣言は明示的な型を指定しますが、通常そうする必要はありません。
次の例では、letdef 構文の明示的な型の違いを示しています。let を使用して定義された変数は、明示的 any 型で、def を使用して定義された変数は、#String 型となります。これは hello プロシージャの宣言で返されたものです。両方の場合で、値の実際の実行型は String となります。

例: letdef を用いた明示的な型の変数の宣言
{define-proc {hello}:#String
    {return "hello"}
}

{value
    def def-s = {hello}
    let let-s = {hello}
    
    {format
        "def-s type is '%s', let-s type is '%s'",
        {compile-time-type-of def-s},
        {compile-time-type-of let-s}
    }
}

グローバル変数とローカル変数の違い

要約:
  • トップレベル コードでは、変数の定義は同じ let 式の後の部分で定義されている変数にアクセスすることができます。
  • コード ブロックでは、変数定義は定義済みの変数にアクセスできるだけです。
let 式はトップレベル コード内とコード ブロック内でスコープの特徴がわずかに異なります。トップレベル コードでは、変数の定義は同じ let 式の後の部分で定義されている変数にアクセスすることができます。しかしコード ブロックでは、変数定義は定義済みの変数にアクセスできるだけです。たとえば、次の定義はトップレベル コードでは有効ですが、コード ブロックでは無効になります。
{let
    a:int=b,
    b:int=0
}
次のコードは、構文エラーを生成するプロシージャ コード ブロック内の let 式を示します。
{define-proc public {qwerty}:int
    let a:int=b,
        b:int=0
    {return a}
}

{qwerty}
SyntaxError:'イニシャライザ内の b にアクセスすることはできません。'

独自の定義における変数の使用

要約:
  • Curl 言語では、たいていの開発者になじみのある方法で式の値を変数にバインド出来ます。
  • ただし、プロシージャそのものの本体を実行せずに匿名プロシージャを作成します。
  • これは、匿名プロシージャ内で定義された変数を参照できることを意味します。
Curl 言語は Lisp や Scheme プログラミング言語から非常に強力なプログラム機能をいくつか取り入れています。C++ および Java™ プログラミング言語の開発者は、これらの機能が持つ意味を完全には理解していないかもしれません。そうしたプログラミング機能の 1 つに、他のコードの中間で (つまり、トップレベル以外で) 匿名プロシージャを作成する機能があります。これらのプログラミング機能を正しく理解して適用すれば、Curl 言語の開発者は驚異的な柔軟性とパワーを手に入れることができます。
Curl 言語は、ほとんどの開発者にとってなじみのある方法で式を変数にバインドします。let ステートメントを使用して変数を宣言および初期化する場合、ほとんどのプログラミング言語と同様に変数を初期化する式が評価されます。これはもちろん独自定義の中で変数を使用できないことを意味します。たとえば、以下のコードはエラーを発生させます。
{value
    let x:int = x + 1
}
SyntaxError:'イニシャライザ内の x にアクセスすることはできません。'
ただし、一定の環境下ではその定義内で変数を参照できます。特に、変数の参照が匿名プロシージャ内で行われるときは変数を参照できます。(匿名プロシージャは proc とも言われ、「プロシージャ」の章で説明しています。)匿名プロシージャ定義の値はプロシージャ自身です。プロシージャが定義されているときは匿名プロシージャの本体は実行されませんが、プロシージャが使用されるときは実行されます。匿名プロシージャの本体を実行する前に匿名プロシージャを変数にバインドするので、匿名プロシージャ内で変数を参照できます。これはたくさんの便利な応用が考えられます。
匿名プロシージャを黙示的に使用する機能の 1 つはイベント ハンドラを指定する on 式です。これは、変数定義内の on 句が定義済みの変数を参照でき、定義済みアイテムの修正のようなタスクを実行できることを意味します。たとえば、次のコードではクリックすると色が変わるボタンを宣言しています。ボタン宣言のon 式にはボタンの参照が含まれています。
サンプルは、最初に変数 bCommandButton 型で宣言し、b をクリックされた時にテキストの色を赤に変える CommandButton クラスのインスタンスで初期化しています。これを行うためには、ボタン宣言の Action 句は、宣言されようとしている変数を参照します。

例: 独自定義での変数の使用、パート 1
{let b:CommandButton =
    {CommandButton label="I like red",
        {on Action do
            set b.color = "red"
        }
    }
}

|| Display "b".
{value b}
プロシージャ本体の実行前に匿名プロシージャを作成できるようにすることにより、Curl 言語は非常に強力なプログラム機能を提供します。ただし、このプログラミング機能を正しく使用するためには、最終的に実行されるときの式の値を知っていなくてはなりません。ループの繰り返しごとに異なる匿名プロシージャが作成され、ループ外で定義された同じ変数を各プロシージャが参照する場合は、この問題は特に複雑になります。このセクションの残りの部分では、これらの問題を説明するための例を示します。
次の例は、ループの繰り返しごとにループ インデックス変数を VBox に追加する for ループを示します。VBox には繰り返しごとのループ インデックス変数の値が含まれています。

例: 独自定義での変数の使用、パート 2
{value
    || Declare and initialize a VBox "message".
    let message:VBox = {VBox}

    || For each iteration through the loop, add the loop
    || index to "message".
    {for index:int = 0 to 3 do
        {message.add index}
    }

    || Display "message".
    {value message}
}
次の例は、おそらく予期しない出力が生成されます。ループ内の各繰り返しについて、この例はボタンを VBox に追加します。ボタン上のラベルにはループ インデックス変数の値が含まれます。ボタンをクリックすると、この例ではループ インデックス変数の値が他の VBox の表示に追加されます。ただしこの例では、ループのスコープ外で宣言された変数が式に含まれると、ループ内で匿名プロシージャを変数にバインドすることによる予期しない結果を示します。
CommandButton 宣言のon 式は評価されずにバインドします。CommandButton の結果の 1 つをクリックすると、on 式が評価され、ループ インデックス変数を VBox に追加します。ただし、ループは終了し、ループ インデックス変数はループの実行を終了させた値に設定されています。ループの実行を終了したループ インデックス変数の値は、クリックされた CommandButton に関わりなく VBox に追加されます。

例: 独自定義での変数の使用、パート 3
{value
    || Declare and initialize two VBoxes; "buttons" and
    || "message".
    let buttons:VBox = {VBox}
    let message:VBox = {VBox}

    || For each iteration through the loop, add a CommandButton
    || to "buttons".
    {for index:int = 0 to 3 do
        || If you click on the CommandButton, add the loop index
        || to "message".
        let b:CommandButton = {CommandButton label="index is... " & index,
                                  {on Action do
                                      {message.add index}
                                  }
                              }
        {buttons.add b}
    }

    || Display "buttons" and "message".
    {VBox
        {value buttons},
        {value message}
    }
}
所期の結果を得るには、ループ インデックス変数の値を保持する for ループ内に変数を作成し、この変数を on 式で使用します。変数は for ループのスコープ内で宣言されるので、変数のコピーはループを通して各繰り返しごとに保持されます。on 式が評価されるとき、その CommandButton について変数の適切なコピーが使用されます。

例: 独自定義での変数の使用、パート 4
{value
    let buttons:VBox = {VBox}
    let message:VBox = {VBox}

    {for index:int = 0 to 3 do
        || Declare an int "temp" and initialize it to
        || the value of "index"
        let temp:int = index
        || If you click on the CommandButton, add the
        || value of "temp" to "message".
        let b:CommandButton = {CommandButton label="index is... " & temp,
                                  {on Action do
                                      {message.add temp}
                                  }
                              }
        {buttons.add b}
    }

    {VBox
        {value buttons},
        {value message}
    }
}
この問題をより詳しく説明するには、変数が for ループ外で代わりに宣言され、ループ内の各繰り返しについて更新される状況を考慮します。また、この例は CommandButton がクリックされたときに on 式を評価するので、変数の現在の値が VBox の表示に追加されます (CommandButton が作成されたときには変数の値は追加されません)。

例: 独自定義での変数の使用、パート 5
{value
    let buttons:VBox = {VBox}
    let message:VBox = {VBox}
    || Declare an int "temp"
    let temp:int

    {for index:int = 0 to 3 do
        || Assign the value of "index" to "temp"
        set temp = index
        let b:CommandButton = {CommandButton label="index is... " & temp,
                                  {on Action do
                                      {message.add temp}
                                  }
                              }
        {buttons.add b}
    }

    {VBox
        {value buttons},
        {value message}
    }
}