Chapel
Chapelは、並列処理に重点を置いた高水準のプログラミング言語です。並列処理を簡単に、安全で、効率的に行うことができるように設計されています。Chapelは、C++、Java、Pythonなどの他のプログラミング言語の影響を受けています。
このチュートリアルでは、Chapelの基礎を学びます。Chapelのインストール方法、基本的な構文、データ型、制御構造、関数、クラスなどについて説明します。また、並列処理の記述方法についても説明します。
このチュートリアルは、Chapelの初心者向けに書かれています。Chapelの経験が豊富な方でも、このチュートリアルから何か新しいことを学ぶことができるかもしれません。
このチュートリアルが、Chapelの基礎を学び、並列処理を記述するための強力なツールを習得するのに役立つことを願っています。
Chapelとは
[編集]Chapel は、並列処理に重点を置いた高水準のプログラミング言語です。2009年にクレイ・スーパーコンピュータ社によって最初に開発され、それ来、オープンソースプロジェクトとして開発が続けられています。Chapel は、C++、Java、Python などの他のプログラミング言語の影響を受けています。
Chapel の目標は、並列処理を簡単に、安全で、効率的に行うことができる言語を作ることです。そのために、Chapel には並列処理をサポートする多くの機能が用意されています。例えば、並列配列、並列関数、並列タスクなどがあります。Chapel には、並列処理を記述するための多くの高レベルの構文も用意されています。例えば、forall 文、par 文、task 文などがあります。
Chapel は、強力で用途の広いプログラミング言語です。並列処理を簡単に、安全で、効率的に行うことができる言語を探している人には最適です。
インストール
[編集]Chapelの基本を学ぶのであれば、処理系のインストールは必要なく、Attempt This Onlineなどのオンラインの実行環境をブラウザーから利用することが出来ます。
また、処理系をインストールする場合、DockerのコンテナやHomebrewのパッケージを利用することが出来ます。
そうではなく、ソースコードからビルドする場合は、GitHubでソースコードが公開されています。
コンパイルと実行
[編集]Chapel は、コンパイル型言語なので、ソースコードを用意し、コンパイルして出来た実行ファイルを実行します。
Chapel のソースコードの拡張子は、.chpl
を使うのが一般的です。
最小限のコード
[編集]- hello.chpl
writeln("Hello, world!"); // コンソールに「Hello, world!」と表示する
上記の、hello.chpl
をエディタで編集し保存し以下のように実行します。
$ cat hello.chpl writeln("Hello, world!"); // コンソールに「Hello, world!」と表示する $ chpl hello.chpl $ ./hello Hello, world!
製品水準のコード
[編集]上記の1行からなるソースコードは、なにかの調べ物をするときには便利です。 しかし、このまま機能を加えていくと、どこからプログラムが始まるかなどが、コードが大規模になるとわかりにくくなります。
次のコードは、それらを改修した製品水準のものです。
- hello_config.chpl
/* このプログラムは、概念的にはhello.chplと非常に似ていますが、 構造化されたプログラミングスタイルを使用し、 モジュール、設定定数、およびmain()手続きを明示的に定義するという点で異なります。 */ // 'Hello'という名前のモジュールを定義します。 // ソースファイルがモジュールを定義しない場合、拡張子".chpl"を除いたファイル名が、それに含まれるコードのモジュール名となります。 // したがって、hello.chplの自動モジュール名は'hello'になります。 module Hello { // 初期化式により、メッセージを出力するための設定定数として "message" を宣言します。 // 初期化式により、型は自動的に文字列と推論されます。 // 任意の設定定数や変数と同様に、デフォルト値は実行可能ファイルのコマンドラインで上書きすることができます // (例:--message="Good evening universe!")。 config const message = "Hello, world!"; // モジュール内のトップレベルのコードは、Chapelプログラムの実行開始時にモジュールが初期化されるときに実行されます。 // したがって、hello.chplでは、ファイルスコープでのwriteln()の存在は暗黙のモジュールの初期化の一部であり、 // プログラムの起動時に実行されます(main()関数や他のトップレベルのコードがなかったため、これがプログラムが行うすべてです)。 // ここでは、モジュールのmain()手続きを定義します。 // これは、モジュールが初期化された後に実行されるプログラムのエントリーポイントです。 // 明示的または暗黙的に使用するモジュールが初期化された後に実行されます。 proc main() { writeln(message); } }
内容は、山盛りのコメント通りですので、コンパイルして実行してみます。
$ chpl hello_config.chpl $ ./hello_config Hello, world! $ ./hello_config --message="Good evening universe!" Good evening universe! $ ./hello_congig -h FLAGS: ====== -h, --help : print this message -a, --about : print compilation information -nl <n> : run program using n locales (equivalent to setting the numLocales config const) -q, --quiet : run program in quiet mode -v, --verbose : run program in verbose mode -b, --blockreport : report location of blocked threads on SIGINT -t, --taskreport : report list of pending and executing tasks on SIGINT --gdb : run program in gdb -E<envVar>=<val> : set the value of an environment variable CONFIG VAR FLAGS: ================= -s, --<cfgVar>=<val> : set the value of a config var -f<filename> : read in a file of config var assignments CONFIG VARS: ============ Built-in config vars: printModuleInitOrder: bool dataParTasksPerLocale: int(64) dataParIgnoreRunningTasks: bool dataParMinGranularity: int(64) memTrack: bool memStats: bool memLeaksByType: bool memLeaks: bool memMax: uint(64) memThreshold: uint(64) memLog: string memLeaksLog: string memLeaksByDesc: string numLocales: int(64) Hello config vars: message: string
このようにChapelで作ったプログラムは、コマンドラインのハンドリングが処理系によって予めお膳立てされているので、コマンドラインインターフェイスは自ずと統一されます。 他方、Chapel以外で書かれたプログラムに特有のコマンドラインインターフェイスの作法があった場合、移植に工夫が必要になります。
変数の初期化と代入
[編集]Chapelでは、変数に値を代入することができます。変数名は英数字とアンダースコア(_)を使用できますが、数字で始めることはできません。 また、大文字と小文字は区別されます。 変数を宣言したときに初期値を与えるには、等号(=)を使用します。以下の例では、変数xを数値10で初期化しています。
var x = 10;
変数に代入された値は、後で変更することができます。以下の例では、変数xに数値20を再度代入しています。
x = 20;
以下は、2つ以上の変数を一度に宣言する例です。
var a = 1, b = 2, c = 3;
このように、コンマで区切って変数を列挙することで、それぞれの変数に対して別々の値を一度に代入することができます。上記の例では、a
に1、b
に2、c
に3が代入されます。
- まとめ
var x = 10; writeln("x = ", x); x = 20; writeln("x = ", x); var a = 1, b = 2, c = 3; writeln("a = ", a, ", b = ", b, ", c = ", c);
- 実行結果
x = 10 x = 20 a = 1, b = 2, c = 3
定数
[編集]Chapelでは、変数の他に定数の宣言もサポートされています。 定数は初期化された後に値を変更できない変数と考えることができます。 Chapelでは、「実行時定数」と「パラメータ」の2つの種類の定数がサポートされており、それぞれ異なるキーワードで導入されます。
実行時定数
[編集]実行時定数は、キーワード const
を使って宣言し、実行時に値が確定する定数です。
実行時定数は、キーワード config で修飾すると、実行時のコマンドラインから値を変更することが出来ます。
const gravity = 9.8;
この例では、gravity
という実行時定数を宣言しています(floatと推定されます)。
この実行時定数は、重力加速度を表す実行時定数であり、値は 9.8
であり、後から値を変更することはできません。
パラメータ
[編集]パラメータは、キーワード param
を使って宣言し、コンパイル時に値が確定している定数です。コンパイル時定数とも呼ばれます。
param debug: bool = false;
この例では、debug
というパラメータを bool 型と宣言しています。
このパラメータは名前から、デバッグ用のコードの有効無効を切り替えるのに使うと考えられます。
パラメータが、他の言語の条件コンパイルに相当する機能に使えます。
- まとめ
const gravity = 9.8; writeln("gravity = ", gravity); // gravity = 0.0; -- error: illegal lvalue in assignment param debug: bool = false; writeln("debug = ", debug); // debug = true; /* error: illegal lvalue in assignment * * error: parameter set multiple times */
- 実行結果
gravity = 9.8 debug = false
識別子(Identifiers)
[編集]Chapelにおける識別子は、小文字または大文字の英字またはアンダースコア( _ )で始まり、任意の小文字、大文字、数字、アンダースコア( _ )、ドル記号( $ )のシーケンスで続く文字列です。
- 合法な例
abc myInt _123 unit$
- 違法な例
0abc // 先頭に数字は使えない $unit // 先頭にドル記号( $ )は使えない if // キーワードは使えない .xyz // ドット( . )は識別子に使えない
Charplでは、ドル記号( $ )を識別子の2文字目以降であれば使うことが出来ます。
キーワード(Keywords)
[編集]Chapelのキーワード一覧 キーワード 分類 説明 _ 特殊文字 - タプルの要素
- 識別子 as _
- 数値リテラルの可読性のための区切り
align 型修飾子 アライメントを指定する - 範囲式 align 式
- ドメイン式 align 式
as 型変換 値を別の型に変換する - 式 as 変数種 識別子
- 式 as 識別子
- 識別子 as 識別子
- 識別子 as _
atomic 並行処理 アトミック操作を行う - atomic 型
begin タスク begin [ インテント ] 文 bool データ型 真理値を表す型 borrowed クラス型 借用された参照を表す型 break 制御構造 繰り返しループから脱出する - break [ ラベル ] ;
by 制御構造 ストライドを指定する - 範囲リテラル by 整数リテラル
- ドメイン式 by 式
- 範囲式 by ステップ式
bytes データ型 バイト列を表す型 catch 例外処理 例外を捕捉する class クラス クラスを定義する cobegin 並行処理 複数のタスクを開始する coforall 並行処理 分散並列forループを実行する complex データ型 複素数を表す型 config 構成 実行時オーバーライド const 修飾子 実行時定数として宣言する continue 制御構造 繰り返しループの次の繰り返しに進む defer リソース解放 スコープを抜けるときに実行するブロックを登録する delete リソース解放 オブジェクトを削除する dmapped 配置 配置マッパーを指定する do 制御構造 単文を実行あるいは定義する domain データ型 ドメインを表す型 else 制御構造 if文の条件不成立 enum 列挙型 列挙型を定義する except 例外処理 例外の種類を指定する export インポート/エクスポート モジュールのシンボルをエクスポートする extern インポート/エクスポート 外部のシンボルを使用する false データ型 偽を表す定数 for 制御構造 繰り返しループを実行する forall 並行処理 FORALL foreach 制御構造 コレクションの各要素に対して繰り返し処理を行う forwarding モジュール 転送関数を定義する if 制御構造 条件分岐を行う imag データ型 虚数を表す型 implements インターフェース インターフェースを実装する import モジュール モジュールをインポート in 制御構造 forループ内でイテレータを指定するなど include モジュール インクルード index データ型 インデックス型を表す型 inline 修飾子 インライン関数を指定する inout 修飾子 入出力引数を指定する int データ型 整数を表す型 interface インターフェース インターフェースを定義する iter データ型 イテレータを定義 label 制御構造 ループやブロックにラベルを付ける let 修飾子 変数を定数として宣言する lifetime 修飾子 変数のライフタイムを指定する local 修飾子 ローカル変数を指定する locale モジュール ロケールを指定する manage 修飾子 管理されたメモリを指定する module モジュール モジュールを定義する new オブジェクト作成 新しいオブジェクトを作成する nil データ型 無効な参照を表す定数 noinit 修飾子 変数を初期化しない none リテラル nothing型の唯一のインスタンス nothing 型 noneだけを取る on データ型 オフセットを表す型 only データ型 オフセットを表す型 operator 演算子 カスタムオ演算子を定義する otherwise 制御構造 select文のデフォルト処理を指定する out 修飾子 出力引数を指定する override オーバーライド メソッドをオーバーライドする owned 修飾子 所有権を持つ変数を指定する param 修飾子 コンパイル時定数の宣言や、for param pragma 前処理 プラグマ __primitive __primitive __primitive private 修飾子 プライベートなメンバを指定する prototype 修飾子 プロシージャのプロトタイプを指定する proc プロシージャ プロシージャを定義する public 修飾子 パブリックなメンバを指定する real データ型 実数を表す型 record データ型 レコードを定義する reduce ループ 集約演算を実行する ref 修飾子 参照渡しを指定する require モジュール 依存関係を指定する return 制御構造 値を返して処理を終了する scan ループ 入力ストリームから値を読み込む select 制御構造 複数の条件の中から一つを選択する serial 並行処理 直列実行を指定する shared 修飾子 共有変数を指定する single 修飾子 シングルトンオブジェクトを指定する sparse データ型 スパースデータを表す型 string データ型 文字列を表す型 subdomain データ型 サブドメインを表す型 sync 並行処理 同期を行う then 制御構造 if文の条件に対応する単文を指定する this モジュール 現在のオブジェクトを指す throw 例外処理 例外をスローする throws 例外処理 プロシージャが例外をスローすることを宣言する true データ型 真を表す定数 try 例外処理 例外を捕捉して処理を行う type データ型 型を表す型 uint データ型 符号なし整数を表す型 union データ型 共用体を表す型 unmanaged データ型 管理されていないメモリを指定する use モジュール 別のモジュールを使用する var 変数宣言 変数を宣言する void 型 関数が値を返さない時の戻値型 when 制御構造 select文の式に対応する条件と処理を指定する where 制約 関数の引数の制約 while 制御構造 条件が真の間、反復する with クロージャ taskとforallのインテントクロージャ yield ジェネレータ 値を生成する zip イテレータ 複数のコレクションを同時に反復処理する
[TODO:分類と説明の充実]
予約済キーワード
[編集]以下の識別子は、将来の使用のために予約されているキーワードです。
データ型
[編集]プリミティブ型
[編集]Chapelには、様々なデータ型があります。その中でプリミティブ型は以下の通りです。
プリミティブ型 型 デフォルトサイズ その他のサイズ デフォルト初期化 bool 実装依存 8, 16, 32, 64 false int 64 8, 16, 32 0 uint 64 8, 16, 32 0 real 64 32 0.0 imag 64 32 0.0i complex 128 64 0.0+0.0i string n/a ""
- Chapelでは、変数は使う前に宣言する必要があり、その時にデータ型を明示的に指定します。また、宣言時に初期化を行うことが出来、その場合は型推論を行い型を省略できます。
- いままで、ほとんどの例では変数の型を明示せずに型推論で型を決めていましたが、明示的に型を指定できます。
// 変数・定数と設定 var x: real = 3.14; // real型の変数を宣言し3.14をセットする。 var flag: bool; // bool型の変数flagを宣言し初期値はセットしない(暗黙のfalseがセットされる)。 var z = -2.0i; // imag型の変数zを宣言し-2.0iをセットする。 const epsilon: real = 0.01; // real型の実行時定数epsilonを宣言し0.01をセットする param debug: bool = false; // bool型のコンパイル時定数debugを宣言しfalseをセットする。 config const n: int = 100; // 実行時に設定可能な定数nを宣言し100で初期化(Ex: $ ./prog --n=4) config param d: int = 4; // コンパイル時に設定可能な定数dを宣言し4で初期化(Ex: $ chpl -sd=3 x.chpl)
プリミティブ型のリテラル表現
[編集]Chapelのプリミティブ型のほとんどは、デフォルトのビット幅で値を指定するための対応するリテラル形式をサポートしています。 他のビット幅の値は、型変換(キャストまたは型強制)を使って取得します。
真理値
[編集]bool型は、falseと
を2つのリテラル値としてサポートします:
true
- bool.chpl
var a: bool = true; var b = false; writeln(a); // => true writeln(b); // => false writeln(a.type:string); // => bool writeln(b.type:string); // => bool
- 上記のコードでは、真理値型の変数
a
とb
を宣言し、それぞれにtrue
とfalse
の値を代入しています。 writeln(a)
およびwriteln(b)
によって、それぞれの変数の値が出力されます。結果として、a
の値はtrue
であり、b
の値はfalse
であることが示されます。writeln(a.type:string)
およびwriteln(b.type:string)
によって、それぞれの変数の型情報が出力されます。結果として、両変数の型はbool
であることが示されます。- 真理値型の変数は
true
またはfalse
のいずれかの値を持ちます。これを利用して条件分岐や論理演算を行うことができます。また、Chapelでは真理値型を使用してループの制御や条件式の評価など、プログラムの制御フローを制御するために広く使用されます。
整数
[編集]Chapelには整数型があります。以下に、Chapelの整数型に関するコード例と解説を示します。
- int.chpl
var a: int = 10; var b = -5; var c: int(16) = 32767; var d: uint(8) = 255; writeln(a); // => 10 writeln(b); // => -5 writeln(c); // => 32767 writeln(d); // => 255 writeln(a.type:string); // => int(64) writeln(b.type:string); // => int(64) writeln(c.type:string); // => int(16) writeln(d.type:string); // => uint(8)
- 上記のコードでは、整数型の変数
a
、b
、c
、d
を宣言し、それぞれに整数値を代入しています。 writeln
関数を使用して、それぞれの変数の値を出力しています。結果として、a
の値は 10、b
の値は -5、c
の値は 32767、d
の値は 255 であることが示されます。writeln
関数を使用して、それぞれの変数の型情報も出力しています。結果として、変数a
とb
の型はデフォルトの整数型int(64)
であり、変数c
の型は 16 ビットの整数型int(16)
、変数d
の型は 8 ビットの符号なし整数型uint(8)
であることが示されます。
Chapelでは、整数型を使用して数値演算や配列のインデックス指定、制御フローの条件式などを行うことができます。整数型のサイズはビット数で指定することもでき、デフォルトでは int(64)
が使用されますが、必要に応じて異なるビット数の整数型を定義することもできます。
int型のリテラル値は、通常、10進数の列として記述されますが、0b/0B、0o/0O、0x/0Xの接頭辞により、2進数、8進数、16進数の値として表現することも可能です:
i = 0b1001; // 9 i = 0o177; // 127 i = 0x1ff; // 511
Chapelでは、負の整数リテラルは存在せず、正の整数リテラルに単項否定演算子(-)を適用するだけであることに注目すべきです。
uint型は独自のリテラル形式を持ちません。しかし、int(64)に収まらないがuint(64)に収まる整数リテラルは、符号なし整数値とみなされます。
したがって、以下の宣言では、最初の変数はint型、2番目の変数はuint型と推定されます。
var m = 2000000000; var l = 1000000000000000;
より小さな整数をuintとして表現するためには、型変換(キャストまたは型強制)を使用する必要があります。
実数型
[編集]実数型は、10進数または指数型によるリテラル値をサポートしています:
- real.chpl
var a: real(64); // 64ビットの実数型 var b: real(32); // 32ビットの実数型 var c: real; // アーキテクチャに自然な実数型 writeln(a.type:string); // => real(64) writeln(b.type:string); // => real(32) writeln(c.type:string); // => real(64) a = 3.14; // 小数表現 b = 2.5e-12; // 指数表現 c = 0x1.7p-1; // 16進数表現 writeln(a); // => 3.14 writeln(b); // => 2.5e-12 writeln(c); // => 0.71875
- まず、
a
、b
、c
という名前の3つの変数を宣言しています。a
は64ビットの実数型、b
は32ビットの実数型、c
はアーキテクチャによって決まるデフォルトの実数型です。 - 次に、各変数の型情報を出力しています。結果として、
a
の型はreal(64)
、b
の型はreal(32)
、c
の型はデフォルトのアーキテクチャに基づくreal(64)
であることが示されます。 - その後、各変数に値を代入しています。
a
には小数表現の3.14
、b
には指数表現の2.5e-12
、c
には16進数表現の0x1.7p-1
が代入されています。 - 最後に、各変数の値を出力しています。結果として、
a
の値は3.14
、b
の値は2.5e-12
、c
の値は0.71875
であることが示されます。 - これにより、異なるビット幅の実数型を使用して値を表現し、それぞれのビット幅に基づく演算や計算を行うことができることがわかります。また、Chapelはさまざまな数値表現方法をサポートしており、小数表現、指数表現、16進数表現などを使用して実数値を代入することができます。
10進数形式では、整数リテラルと区別するために.を使用する必要があります。10.
は、Chapelでは有効な浮動小数点リテラル値ではないことに注意してください。
これは、整数値に対してメソッド呼び出しを行う際の構文の曖昧さのためです。このため、代わりに10.0
が使用されなければならない。
また、.707
は0.707
の短縮表記として有効です(特に推奨はしません)。
指数形式では、ベースとなる10進数値を整数値にしたり、大文字のEを使用して指数を指定することができることに注意してください。したがって、次の4つの代入の値はすべて等価である:
var f: real; f = 123.0e2; f = 123e2; f = 123.0E2; f = 123E2;
虚数値
[編集]Chapelでは、虚数型を表現するためにキーワードimag
が使用されます。
- imag.chpl
var a: imag(64); // 64ビットの虚数型 var b: imag(32); // 32ビットの虚数型 var c: imag; // アーキテクチャに自然な虚数型 writeln(a.type:string); // => imag(64) writeln(b.type:string); // => imag(32) writeln(c.type:string); // => imag(64) a = 3.0i; // 虚数リテラルを代入 b = 2.5:imag; // imagに強制変換して虚数を代入 // c = 1.7; error: Cannot assign to imag(64) from real(64) writeln(a); // => 3.0i writeln(b); // => 2.5i writeln(c); // => 0.0i
- 最初の部分では、3つの異なる虚数型の変数
a
、b
、c
を宣言しています。a
は64ビットの実数型の虚数を表し、b
は32ビットの実数型の虚数を表します。c
はアーキテクチャに自然な虚数型を表します。 - また、
writeln
文を使用して各変数のデータ型を表示しています。 - 次に、虚数型の変数に値を代入する例です。
a
には虚数リテラル3.0i
を代入しています。b
には2.5
をimag
キーワードを使用して強制的に虚数型に変換して代入しています。一方、c
には実数型の値1.7
を代入しようとするとエラーが発生します。 - 最後の部分では、各変数の値を表示しています。
a
とb
はそれぞれ代入された値がそのまま表示されますが、c
には初期化時に値が設定されていないため、デフォルト値の0.0i
が表示されます。
複素数
[編集]複素数型は、リテラル値のネイティブフォーマットをサポートしていません。その代わり、複素数値は通常、実数値と虚数値の加算や減算によって表現される。例えば、以下のようになります:
- complex.chpl
var cmplx1: complex; writeln(cmplx1); // => 0.0 + 0.0i writeln((-9.0).type:string); // => real(64) writeln((0i).type:string); // => imag(64) writeln((-9.0+0i).type:string); // => complex(128) writeln(sqrt(-9.0)); // => nan writeln(sqrt(-9.0+0i)); // => 0.0 + 3.0i writeln(sqrt(0i)); // => 0.0 + 0.0i writeln((-9.0) ** 0.5); // => nan writeln((-9.0+0i) ** 0.5); // => 1.83697e-16 + 3.0i writeln((0i) ** 0.5); // => 0.0 - 0.0i writeln(abs(3.0+4.0i)); // => 5.0 writeln(carg(3.0+4.0i)); // => 0.927295 writeln(conjg(3.0+4.0i)); // => 3.0 - 4.0i writeln(cproj(3.0+4.0i)); // => 3.0 + 4.0i
cmplx1
は複素数型の変数で、初期値は指定されていません。そのため、初期化されていない変数はデフォルトで0.0 + 0.0iとなります。writeln
文によってcmplx1
の値が出力されます。type
プロパティを使って、数値の型を表す文字列を取得しています。-9.0
は実数型(real
)であり、0i
は虚数型(imag
)であり、-9.0+0i
は複素数型(complex
)です。それぞれの値の型が出力されます。sqrt
関数は平方根を計算する関数です。-9.0
の平方根は実数では表現できないため、結果はNaN(非数)になります。-9.0+0i
および0i
の平方根は複素数として計算され、結果が出力されます。**
演算子はべき乗を計算する演算子です。-9.0
の0.5乗は実数では表現できないため、結果はNaNになります。-9.0+0i
および0i
の0.5乗は複素数として計算され、結果が出力されます。abs
関数は複素数の絶対値を計算します。ここでは、複素数3.0+4.0i
の絶対値を求めています。絶対値は複素数の大きさを表し、この場合は5.0となります。carg
関数は複素数の偏角を計算します。ここでは、複素数3.0+4.0i
の偏角を求めています。偏角は複素平面上での角度を表し、この場合は約0.927295となります。conjg
関数は複素数の共役を計算します。ここでは、複素数3.0+4.0i
の共役を求めています。共役は虚部の符号を逆転させた複素数を表し、この場合は3.0-4.0i
となります。cproj
関数は複素数の射影を計算します。ここでは、複素数3.0+4.0i
の射影を求めています。射影は複素数そのものを表し、この場合は3.0+4.0i
となります。- これらの関数は複素数に対してさまざまな演算を行い、複素数の性質や値を操作するためのものです。それぞれの関数がどのような計算を行っているかを理解し、複素数に関する処理を行う際に利用することができます。
文字列リテラル
[編集]文字列のリテラル値は、一重引用符または二重引用符を使って表現することができます。例えば、次のような文字列は、合法的な文字列値です:
var s: string; s = "hello"; s = 'hi';
これによる影響としては、文字列リテラル内で他のタイプの引用符を追加作業なしで使用できることです。
s = "he said, 'hi there!"";
キャストと暗黙の型変換(coercion)
[編集]プログラミングを行っていると、ある型の値を別の型に変換する必要が生じます。
- coercion.chpl
var s : string = "1234"; writef("\"%s\"(%s) : int = %u\n", s, s.type:string, s:int); s = "0x5678"; writef("\"%s\"(%s) : int = 0x%xu\n", s, s.type:string, s:int); var i = 1; // int(64)と推論 var f = 2.73; // real(64)と writef("i.type = %s, f.type = %s\n", i.type:string, f.type:string); f = i; // 合法:暗黙の型変換(coercion) // i = f; -- error: Cannot assign to int(64) from real(64) i = f:int; // intに明示的なキャストが必要(小数点以下を失うほか、桁あふれする可能性も) var cmp = i == f; // 合法:暗黙の型変換(coercion)
- 実行結果
"1234"(string) : int = 1234 "0x5678"(string) : int = 0x5678 i.type = int(64), f.type = real(64)
- 文字列の整数へのキャスト
- 値の型を .type で得たあと文字列へのキャスト
- writef()関数を使った書式化(機能的には printf() 相当ですが書式化文字列が違います)
- 16進文字列の整数へのキャスト
- 整数と実数の間の代入におけるキャストの要否
などが示されています。特に、キャスト以外にも .type プロパティや writef() といった飛び道具がいきなり出てきましたが、Chapelのプログラミングに頻繁に登場するので、この機会におぼえましょう。
参照(References)
[編集]ref
は、C++の参照と似たような動作をします。
Chapelでは、ref
は初期化された変数以外の変数とエイリアスを作ることはできません。
- references.chpl
var i = 10; ref r = i; writeln(i, " == ", r); // => 10 == 10 i = -123; // 変数iを変更(r に参照されている) writeln(i, " == ", r); // => -123 == -123 r = 99999999; // 参照rを変更(参照先は i) writeln(i, " == ", r); // => 99999999 == 99999999
同じスコープの変数の参照を作っても、別名が出来るだけであまり楽しいことはありませんが、プロシージャの引数に参照を使うと、文字通り参照渡しが可能となり、呼び出し元の変数に値を返すことが出来ます。
列挙型
[編集]Chapelでは、キーワード enum を使って列挙型(enumeration type)を定義することができます。列挙型は特定の値のセットを表し、それぞれの値に名前を付けることができます。
- enum.chpl
enum season { Spring, Summer, Autumn, Winter }; var s = season.Summer; select s { when season.Spring do writeln("春"); when season.Summer do writeln("夏"); when season.Autumn do writeln("秋"); when season.Winter do writeln("冬"); }
- 実行結果
夏
構造型
[編集]構造型( Structured Types )には、
があります。
クラス
[編集]クラスは、変数や定数(フィールド)および関数やイテレータ(メソッド)を含むことができる型です。 新しいクラスは、キーワードclassを使用して宣言されます。
- point.chpl
class Point { var x, y: int; proc print do writeln("x = ", x, ", y = ", y); } var pt = new Point(21, 45); pt.print; var pt2 = new Point(y = -1, x = 9); pt2.print;
x = 21, y = 45 x = 9, y = -1
キーワードnewは、コンストラクタを呼び出すことによりクラスのインスタンスを作成します。 上記のクラスPointは、コンストラクタを宣言していないため、コンパイラが生成したコンストラクタが使用されます。 コンパイラが生成したコンストラクタは、クラス内の各フィールドに対する引数を持っています。 クラスが初期化されると、そのメソッドを呼び出すことができます。 クラスには、解放方法を決定するさまざまなメモリ管理戦略があります。 あらためて詳しく説明しますが、今のところ、new Point(...)は、new owned Point(...)と書いたのと同じです。 ownedはこれらのメモリ管理戦略の一つです。
メモリ管理戦略
[編集]クラスのタイプには、メモリ管理戦略が含まれています。現在サポートされている戦略は、owned、 、unmanaged、borrowedの4つです。
// unmは手動で管理されるインスタンスを参照します。 var unm: unmanaged Point = new unmanaged Point(); // メモリを解放するためには、deleteを呼び出す必要があります。 delete unm; // ownが参照するインスタンスは、スコープ外になると削除されます。 var own: owned Point = new owned Point(1, 10); //特定のインスタンスに対して同時に複数のowned Pointが参照することはできませんが、所有権は他の変数に移動することができます。 var own2 = new Point(1, 10); assert(own.type == own2.type); // 上記の例では、new Point(...)はnew owned Point(...)の略記法として使用できます。なぜなら、クラスのデフォルトのメモリ管理戦略はownedであるためです。 // shareが参照するインスタンスは参照カウントされます。 // つまり、複数のshared Point変数が同じインスタンスを参照し、最後の変数がスコープ外になると解放されます。 var share: shared Point = new shared Point(1, 10); var tmp: borrowed Point = (new owned Point(1, 10)).borrow(); // tmpが参照するインスタンスは、スコープ外になると削除されます。所有権は他の変数に移動することはできません。 // 他のクラスポインタから借用することも可能です。その方法の1つは、borrow()メソッドを直接呼び出すことです。 var b1 = own.borrow();
レコード
[編集]レコードは、変数や定数(フィールド)だけでなく、関数やイテレータ(メソッド)も含めることができる型です。
レコードはクラスと多くの類似点がありますが、いくつか重要な違いがあります。
- クラスは継承と仮想ディスパッチをサポートしていますが、レコードはサポートしていません。
- 異なるクラス変数は同じインスタンスを参照できますが、レコード変数は異なるメモリを参照します。
- コピー初期化や代入はレコードに対して実装できますが、クラスにはできません。
レコード型は、キーワードrecord
を使用して宣言することができます。
- record.chpl
record GeoCoord { var longitude, latitude: real; } proc main() { var sites = [ "東京駅" => new GeoCoord(139.7673068, 35.6809591), "シドニー・オペラハウス" => new GeoCoord(151.215278, -33.856778), "グリニッジ天文台" => new GeoCoord(-0.0014, 51.4778), ]; for name in sites.domain do writeln(name, sites[name]); }
グリニッジ天文台(longitude = -0.0014, latitude = 51.4778) 東京駅(longitude = 139.767, latitude = 35.681) シドニー・オペラハウス(longitude = 151.215, latitude = -33.8568)
ユニオン
[編集]Chapelのユニオン( union )は、複数の異なる型の値を同じメモリ領域で保持する仕組みです。
ここまではCと同じですが、Chapelの union では、有効なフィールドは1つだけです。 どのフィールドが有効かは、最後に代入されたフィールドによってかわります。
- union.chpl
union U { var i: int; var r: real; } proc main() { var u: U; u.i = 123; // writeln(u.r); error: halt reached - illegal union access writeln(u.i); u.r = 2.73; // writeln(u.i); error: halt reached - illegal union access writeln(u.r); writeln(isUnionType(U)); }
123 2.73 true
タプル
[編集]var t = (1, 3.2, "xyz"); writeln("t = ", t, ", x.type = ", t.type:string); writeln("t.size = ", t.size); for x in t do write(x, "; "); writeln(); writeln("t(0) = ", t(0)); writeln("t(1) = ", t(1)); writeln("t(2) = ", t(2)); var (a, b, _) = t; writeln("a = ", a, ", b = ", b); for prime in ( 2, 3, 5, 7, 11) do write(prime, ", "); writeln();
t = (1, 3.2, xyz), x.type = (int(64),real(64),string) t.size = 3 1; 3.2; xyz; t(0) = 1 t(1) = 3.2 t(2) = xyz a = 1, b = 3.2 2, 3, 5, 7, 11,
データ並列型
[編集]データ並列型( data parallel types )には、
があります。
範囲
[編集]
範囲(Range)は整数などの一定の値のシーケンスを表し、通常、下限と上限の境界を定義します。
範囲の値は、..
演算子を使用して指定することができます。下限は ..
の左側に指定し、上限は右側に指定します。
// r は範囲で、1 から 10、1 から 9、10 から 1 までです。 // r.type は range です。 // r:array は、1 から 10、1 から 9、10 から 1 までの配列です。 for r in (1..10, 1..<10, 1..10 by -1) do writeln("r = ", r, ", r.type = ", r.type:string, ", r:array = [", for i in r do i, "]"); writeln(); var sum = 0; // sum は 0 で初期化された変数です。 for i in 1..10 do // i は 1 から 10 までの整数です。 sum += i; // sum に i を加算します。 writeln("sum = ", sum); writeln(); // reduce 演算子は、範囲の要素に左辺のメソッドを順に適用します。 // + reduce (1..10) は、1 から 10 の合計を計算します。 writeln("+ reduce (1..10) = ", + reduce (1..10));
r = 1..10, r.type = range(int(64),bounded,false), r:array = [1 2 3 4 5 6 7 8 9 10] r = 1..9, r.type = range(int(64),bounded,false), r:array = [1 2 3 4 5 6 7 8 9] r = 1..10 by -1, r.type = range(int(64),bounded,true), r:array = [10 9 8 7 6 5 4 3 2 1] sum = 55 + reduce (1..10) = 55
アライメント
[編集]Chapel の Range における align キーワードは、範囲を表す値のシーケンスがそのストライドに対してどのようにアライメントされるかを指定するために使用されます。 1 または -1 以外のストライドを持つ範囲では、アライメントは重要です。たとえば、ストライドが 2 の範囲は 2 の倍数であるインデックスのみをサポートします。範囲の値のシーケンスがストライドに対して整列していない場合、範囲は空になります。
for i in 1..10 by 2 do write(i, " "); writeln(); for i in 1..10 by 2 align 2 do write(i, " "); writeln();
1 3 5 7 9 2 4 6 8 10
- 最初のコードは、範囲を 1 から 10 まで、ストライドを 2 で走査します。つまり、1、3、5、7、9 を出力します。
- 2 番目のコードは、範囲を 1 から 10 まで、ストライドを 2 で走査し、2 でアラインします。つまり、2 の倍数である 2、4、6、8、10 を出力します。
ドメイン
[編集]
ドメインは、イテレーション空間の指定、配列の定義、スライスなどの集約操作を定義するために使用されるインデックス集合の一級表現です。
config const n = 10; var RD: domain(3) = {1..n, 1..n, 1..n}; writeln("RD = ", RD, ", RD.type = ", RD.type:string); // expandメソッドは、オフセット引数の符号に応じて拡張または縮小された新しいドメインを返します。 writeln("RD.expand((1,1,1)) = ", RD.expand((1,1,1))); writeln("RD.expand((-1,-1,-1)) = ", RD.expand((-1,-1,-1))); // exteriorメソッドは、現在のドメインの外部部分である新しいドメインを返します。 // 正のオフセットは高い範囲から外部部分を取得し、負のオフセットは低い範囲から外部部分を取得します。 writeln("RD.exterior((1,1,1)) = ", RD.exterior((1,1,1))); writeln("RD.exterior((-1,-1,-1)) = ", RD.exterior((-1,-1,-1))); // interiorメソッドは、現在のドメインの内部部分である新しいドメインを返します。 // オフセットの符号は、外部の場合と同様に高い範囲または低い範囲を使用することを意味します。 writeln("RD.interior((1,1,1)) = ", RD.interior((1,1,1))); writeln("RD.interior((-1,-1,-1)) = ", RD.interior((-1,-1,-1))); // translateメソッドは、オフセットで現在のドメインを移動した新しいドメインを返します。 writeln("RD.translate((1,1,1)) = ", RD.translate((1,1,1))); writeln("RD.translate((-1,-1,-1)) = ", RD.translate((-1,-1,-1))); writeln(); // サブドメインは、親ドメインを基準に宣言されるため、親と同じタイプを持つドメインです。 // サブドメインは、親ドメインのインデックスセットの部分集合を表しますが、この制約は現在の実装では強制されていません。 //矩形のサブドメインを作成します。 var RSD1, RSD2 : subdomain(RD); // サブドメインは最初は空です。 writeln("RSD1 = ", RSD1, ", RSD1.type = ", RSD1.type:string); writeln("RSD2 = ", RSD2, ", RSD2.type = ", RSD2.type:string); writeln(); // スライシングを使用して元の矩形ドメインの一部を選択することができます。 RSD1 = RD[..n/2, .., ..]; // ドメインの半分を取得 RSD2 = RD[n/2+1.., .., ..]; // 残りの半分を取得 writeln("RSD1 = ", RSD1, ", RSD1.type = ", RSD1.type:string); writeln("RSD2 = ", RSD2, ", RSD2.type = ", RSD2.type:string); // 正規ドメインのスパースなサブドメインを作成します。 var SSD: sparse subdomain(RD); writeln("SSD = ", SSD, ", SSD.type = ", SSD.type:string); // 最初は空です。
RD = {1..10, 1..10, 1..10}, RD.type = domain(3,int(64),false) RD.expand((1,1,1)) = {0..11, 0..11, 0..11} RD.expand((-1,-1,-1)) = {2..9, 2..9, 2..9} RD.exterior((1,1,1)) = {11..11, 11..11, 11..11} RD.exterior((-1,-1,-1)) = {0..0, 0..0, 0..0} RD.interior((1,1,1)) = {10..10, 10..10, 10..10} RD.interior((-1,-1,-1)) = {1..1, 1..1, 1..1} RD.translate((1,1,1)) = {2..11, 2..11, 2..11} RD.translate((-1,-1,-1)) = {0..9, 0..9, 0..9} RSD1 = {1..0, 1..0, 1..0}, RSD1.type = domain(3,int(64),false) RSD2 = {1..0, 1..0, 1..0}, RSD2.type = domain(3,int(64),false) RSD1 = {1..5, 1..10, 1..10}, RSD1.type = domain(3,int(64),false) RSD2 = {6..10, 1..10, 1..10}, RSD2.type = domain(3,int(64),false) SSD = { } , SSD.type = DefaultSparseDom(3,int(64),domain(3,int(64),false))
ドメインマップ
[編集]ドメインマップ(Domain Maps)は、Chapelプログラムでドメインと配列のマッピング方法を指定するために使用されます。ドメインマップに関連するキーワードとその説明をコードを交えて解説します。
- dmapped(ドメインマップの指定)
- キーワードdmappedは、ドメインまたは配列のマッピング方法を指定するために使用されます。以下のコード例では、キーワードdmappedを使用してドメインマップを指定しています。
use BlockDist; type BlockDom = domain(2) dmapped Block({1..5,1..6});
- dmap(ドメインマップの作成)
- キーワードdmapは、ドメインマップのインスタンスを作成するために使用されます。以下のコード例では、キーワードdmapを使用してドメインマップを作成しています。
use BlockDist; var MyBlockDist: dmap(Block(rank=2)) = new dmap(new Block({1..5,1..6}));
- ドメインリテラル(Domain Literal)
- ドメインリテラルは、ドメインの範囲を指定するために使用されます。以下のコード例では、ドメインリテラルを使用してドメインの範囲を指定しています。
use BlockDist; var MyDomLiteral = {1..2,1..3};
- ドメイン代入(Domain Assignment)
ドメイン代入は、ドメインのインデックスセットを右辺の式から左辺のドメインに転送する操作です。以下のコード例では、ドメイン代入が行われています。
use BlockDist; var Dom1: domain(2) dmapped Block({1..5,1..6}) = {1..5,1..6}; var Dom2: domain(2) = Dom1;
配列
[編集]Chapelでは、配列は配列のインデックスセットを指定する角かっこで囲まれた式によって指定されます。 その後に配列の要素の型が続きます。矩形配列は、インデックスが整数または整数のタプルであり、標準の多次元、直交座標のインデックスセットをサポートします。
- array.chpl
// 空の配列 ary0 を定義し、その属性を表示する var ary0: [1..0] int; writeln("ary0 = ", ary0, ", ary0.type = ", ary0.type:string, ", ary0.domain = ", ary0.domain); // 長さ10の配列 ary1 を定義し、その属性を表示する var ary1: [1..10] real; writeln("ary1 = ", ary1, ", ary1.type = ", ary1.type:string, ", ary1.domain = ", ary1.domain); // 配列ary1の2番目と7番目の要素を指定して、値を代入する ary1(2) = 20.0; ary1(7) = 70.0; // ary1の属性と値を表示する writeln("ary1 = ", ary1, ", ary1.type = ", ary1.type:string, ", ary1.domain = ", ary1.domain); // 配列ary1の3番目から5番目の要素に一括で値を代入する ary1(3..5) = 35.5; // ary1の属性と値を表示する writeln("ary1 = ", ary1, ", ary1.type = ", ary1.type:string, ", ary1.domain = ", ary1.domain); // 配列ary1全体に同じ値を代入する ary1 = 123.7; // ary1の属性と値を表示する writeln("ary1 = ", ary1, ", ary1.type = ", ary1.type:string, ", ary1.domain = ", ary1.domain); // 整数値を含む配列ary2を定義する var ary2 = [2, 3, 5, 7, 11]; // ary2の属性と値を表示する writeln("ary2 = ", ary2, ", ary2.type = ", ary2.type:string, ", ary2.domain = ", ary2.domain); // 等差数列を生成して、ary3に代入する var ary3 = for i in 1..10 do i:real; // ary3の属性と値を表示する writeln("ary3 = ", ary3, ", ary3.type = ", ary3.type:string, ", ary3.domain = ", ary3.domain); // ary3の要素全体に2倍する ary3 *= 2; // ary3の属性と値を表示する writeln("ary3 = ", ary3, ", ary3.type = ", ary3.type:string, ", ary3.domain = ", ary3.domain); // 4x4の行列ary4を定義する const n = 4; var ary4: [1..n, 1..n] real; // 配列ary4の対角要素に1を代入する(単位行列) forall (i, j) in ary4.domain do if i == j then ary4(i, j) = 1.0; // ary4の属性と値を表示する writeln("ary4 = \n", ary4, ",\n ary4.type = ", ary4.type:string, ", ary4.domain = ", ary4.domain);
- 実行結果
ary0 = , ary0.type = [domain(1,int(64),false)] int(64), ary0.domain = {1..0} ary1 = 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0, ary1.type = [domain(1,int(64),false)] real(64), ary1.domain = {1..10} ary1 = 0.0 20.0 0.0 0.0 0.0 0.0 70.0 0.0 0.0 0.0, ary1.type = [domain(1,int(64),false)] real(64), ary1.domain = {1..10} ary1 = 0.0 20.0 35.5 35.5 35.5 0.0 70.0 0.0 0.0 0.0, ary1.type = [domain(1,int(64),false)] real(64), ary1.domain = {1..10} ary1 = 123.7 123.7 123.7 123.7 123.7 123.7 123.7 123.7 123.7 123.7, ary1.type = [domain(1,int(64),false)] real(64), ary1.domain = {1..10} ary2 = 2 3 5 7 11, ary2.type = [domain(1,int(64),false)] int(64), ary2.domain = {0..4} ary3 = 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0, ary3.type = [domain(1,int(64),false)] real(64), ary3.domain = {1..10} ary3 = 2.0 4.0 6.0 8.0 10.0 12.0 14.0 16.0 18.0 20.0, ary3.type = [domain(1,int(64),false)] real(64), ary3.domain = {1..10} ary4 = 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0, ary4.type = [domain(2,int(64),false)] real(64), ary4.domain = {1..4, 1..4}
ですが、配列リテラルのように配列のインデックスの開始値を明示しない配列もあります。このうような場合は、バージョン1.21までは1から開始し、バージョン1.22以降は0から開始します。
このため 1.21 と 1.22 の間でChapelのソースコードには非互換性があります。
この影響は、可変長引数を持つ手続きの引数順位や、多次元配列のdomainのdim()のインデックスにもあります。
バージョン1.22のリリースは、2020年4月とそれほど昔ではないので、サンプルコードを参照したりドキュメントを読むときは、どのバージョンを想定しているのか注意が必要です。
また、コードを書く上では、添字の開始を0や1と決めつけずに、array.domain.lowを使ってアクセスするなど、予防的なコードを書きましょう。文字列集合をドメインとした配列
[編集]配列の添字(=ドメイン)には、文字列など整数でない型を用いることも出来ます。
proc main() { var ages = [ "John" => 25, "Mary" => 30, "Alice" => 28 ]; // 要素へのアクセス writeln("ages[\"John\"] = ", ages["John"]); // 25 // 要素の変更 ages["Mary"] = 32; // ドメインを使った反復 for name in ages.domain do write(name, "(",ages[name], ") "); writeln(); }
ages["John"] = 25 Mary(32) John(25) Alice(28)
ドメインの動的な変更
[編集]配列と結びついたドメインを動的に変更することで連想配列に近いことが出来ます。
proc main() { var Colours = {"Red", "Green", "Blue"}; var rgb: [Colours] int; rgb["Red"] = 0xff0000; rgb["Green"] = 0x00ff00; rgb["Blue"] = 0x0000ff; for colour in Colours do writef("%s(#%06xu) ", colour, rgb[colour]); writeln(); Colours += "Yellow"; rgb["Yellow"] = rgb["Red"] + rgb["Green"]; for colour in Colours do writef("%s(#%06xu) ", colour, rgb[colour]); writeln(); Colours += "White"; rgb["White"] = rgb["Red"] + rgb["Green"] + rgb["Blue"]; Colours += "Black"; rgb["Black"] = 0; for colour in Colours do writef("%s(#%06xu) ", colour, rgb[colour]); writeln(); Colours -= "Yellow"; for colour in Colours do writef("%s(#%06xu) ", colour, rgb[colour]); writeln(); Colours += "Yellow"; for colour in Colours do writef("%s(#%06xu) ", colour, rgb[colour]); writeln(); }
Blue(#0000ff) Red(#ff0000) Green(#00ff00) Yellow(#ffff00) Blue(#0000ff) Red(#ff0000) Green(#00ff00) Yellow(#ffff00) White(#ffffff) Black(#000000) Red(#ff0000) Blue(#0000ff) Green(#00ff00) White(#ffffff) Black(#000000) Red(#ff0000) Blue(#0000ff) Green(#00ff00) Yellow(#000000) White(#ffffff) Black(#000000) Red(#ff0000) Blue(#0000ff) Green(#00ff00)
同期型
[編集]同期型( synchronization types )には、
があります。
sync
[編集]single
[編集]atmic
[編集]式と演算子
[編集]Chapelでは、多くの種類の式と演算子がサポートされています。ここでは、最もよく使用される式と演算子のいくつかについて説明します。
数学演算子
[編集]数学演算子には、加算、減算、乗算、除算、べき乗、剰余があります。次の例は、これらの演算子の使用方法を示しています。
var a: int = 1234, b: int = 5678; a = a + b; // 加算 a = a - b; // 減算 a = a * b; // 乗算 a = a / b; // 除算 a = a ** b; // べき乗 a = a % b; // 剰余
論理演算子
[編集]論理演算子には、論理積、論理和、論理否定があります。次の例は、これらの演算子の使用方法を示しています。
var a: bool = true, b: bool = false; a = a && b; // 論理積 a = a || b; // 論理和 a = !a; // 論理否定
関係演算子
[編集]関係演算子には、大なり、大なりまたは等しい、小なり、小なりまたは等しい、等しい、異なるが含まれています。次の例は、これらの演算子の使用方法を示しています。
var a: int = 1234, b: int = 5678; a > b; // 大なり a >= b; // 大なりまたは等しい a < b; // 小なり a <= b; // 小なりまたは等しい a != b; // 異なる a == b; // 等しい
ビット演算子
[編集]ビット演算子には、左シフト、右シフト、ビット否定、ビット排他的論理和が含まれています。次の例は、これらの演算子の使用方法を示しています。
var a: int = 1234; a << 10; // 左シフト a >> 5; // 右シフト ~a; // ビット否定 a ^ b; // ビット排他的論理和
複合代入演算子
[編集]複合代入演算子には、加算代入、乗算代入、除算代入、べき乗代入、左シフト代入、右シフト代入が含まれています。次の例は、これらの演算子の使用方法を示しています。
var a: int = 1234; a += 10; // 加算代入 a *= 5; // 乗算代入 a /= 2; // 除算代入 a **= 3; // べき乗代入 a <<= 1; // 左シフト代入 a >>= 1; // 右シフト代入
交換演算子
[編集]交換演算子には、値の交換を行う```<=>`演算子が含まれています。次の例は、この演算子の使用方法を示しています。
var a: int = 1234, b: int = 5678; a <=> b; // aとbの値を交換する
演算子の優先度と結合方向
[編集]Chapel の演算子一覧表(優先度の高い順) 演算子 用途 . () [] メンバーアクセス、呼び出し、インデックス new (右) コンストラクター呼び出し : キャスト ** (右) べき乗 reduce scan dmapped 畳み込み、スキャン、ドメインマップの適用 ! ~ (右) 論理およびビット演算の否定 * / % 乗算、除算、剰余 単項 + - (右) 正の同一性、否定 << >> 左シフト、右シフト & 論理/ビット演算の論理和 ^ 論理/ビット演算の排他的論理和 | 論理/ビット演算の論理和 + - 加算、減算 .. ..< 範囲と開範囲の構築 <= >= < > 順序付き比較 == != 等価比較 && 論理積の短絡評価 | 論理和の短絡評価 by # align 範囲ストライド、カウント、アライメント in ループ式 if
forall
for条件式、並列イテレータ式、シリアルイテレータ式 , 式リスト
- 註)「 (右)」と特記なき限り左結合
n += 1
およびn += 1
はあります。
制御構造
[編集]Chapelも、多くのプログラミング言語と同じく、分岐と反復の制御構造を持ちます。
分岐
[編集]Chapelには、ifとselectの2つの分岐構文があります。
if
[編集]ifは、条件式に基づき分岐し、分岐先を評価します。 ifの値は、分岐先の式の値です。
var i = 0; if i == 0 then writeln("零"); else writeln("非零"); if i == 0 { writeln("== 0"); } else { writeln("!= 0"); } var s = if i == 0 then "ゼロ" else "非ゼロ"; writeln(s); var q = 0.0 / 0.0; if q == 0.0 then writeln("zero"); else if q < 0.0 then writeln("negative"); else if q < 0.0 then writeln("positive"); else writeln(q);
- 実行結果
零 == 0 ゼロ nan
if は、値を返すのでif文ではなくif式とも言えますが、値を使わない場合はelseを省略できます。 値を使うにelseを省略すると Syntax error となります。
- 構文(EBNF)
条件文 ::= "if" 条件式 "then" 文 [ "else" 文 ] | "if" 条件式 ブロック文 [ "else" 文 ] | "if" 制御変数宣言 "then" 文 [ "else" 文 ] | "if" 制御変数宣言 ブロック文 [ "else" 文 ] ブロック文 ::= "{" 文 { 文 } "}" 制御変数宣言 ::= "var" 識別子 "=" 式 | "const" 識別子 "=" 式
then
が突く場合とブロック文が続く場合があり、then を使うとPythonの様なインデント記法に見えますが、インデントは見栄えだけのものです。
select
[編集]select は、1つの式を評価してそれと一致する条件に対応する文を実行します。
var x = 4; select x { when 0 do writeln("zero"); when 1 do writeln("one"); when 2, 3, 4, 5, 6, 7, 8, 9, 10 { writeln("two to ten"); } otherwise writeln("something else"); }
- 実行結果
two to ten
- 構文(EBNF)
select文 ::= "select" 式 { when文列 } when文列 ::= when文 | when文 when文列 when文 = "when" 式リスト "do" 文 | "when" 式リスト block-文 | "otherwise" 文 | "otherwise" "do" 文 式リスト = 式 | 式 "," 式リスト
when の式リストには、範囲やパターンマッチングは書けないので、使い勝手はよくありませんので、多くの場合はifやマッピングを使うことになるでしょう。
また、列挙型( enum )とselect文を組み合わせて使うと、コードがより明確になります。この場合は、ifよりも好ましい選択です。
反復
[編集]
while-do
[編集]while-do文は、式が真である場合、続く文あるいはブロック文を繰り返します。
var i = 0; while i < 5 { writeln("i = ", i); i += 1; }
- 実行結果
i = 0 i = 1 i = 2 i = 3 i = 4
- このコードは、変数
i
を 0 に初期化し、変数i
が 5 未満である間、次の処理を繰り返します。- 変数
i
の値を出力します。 - 変数
i
に 1 を加算します。
- 変数
- このコードは、0 から 4 までの数字を出力します。
whileはfor系に比べて使い所が少ないですが、ファイル読み込みが典型的です。
- file-read.chpl
use FileSystem; use IO; proc main() { const f = open("/etc/passwd", ioMode.r); defer { f.close(); } const r = f.reader(); defer { r.close(); } var line:string; while r.readLine(line) do write(line); }
use FileSystem;
とuse IO;
は、ファイルシステムと入出力操作を利用するモジュールをインポートします。proc main()
は、メインプロシージャです。const f = open("/etc/passwd", ioMode.r);
は、ファイル/etc/passwd
を開きます。ファイルモードはioMode.r
で、読み取り専用です。defer { f.close(); }
は、ファイルf
をクローズするデストラクターを記述しています。const r = f.reader();
は、ファイルf
のリーダを取得します。defer { r.close(); }
は、リーダr
をクローズするデストラクターを記述しています。var line:string;
は、行を格納する変数です。while r.readLine(line) do
は、リーダr
から行を読み込み、変数line
に格納します。このループは、リーダr
から読み取れる行がある間、継続します。write(line);
は、行line
を出力します。
このコードは、ファイル /etc/passwd
の内容を読み込み、標準出力に表示します。
- 構文(EBNF)
while-do文 ::= "while" 式 "do" 文 | "while" 式 ブロック文 | "while" 制御変数宣言 "do" 文 | "while" 制御変数宣言 ブロック文
do-while
[編集]do-while文は、文あるいはブロック文をまず実行し、式が真であれば繰り返します。
var i = 0; do { writeln("i = ", i); i += 1; } while i > 5;
- 実行結果
i = 0
- このコードは、変数
i
を0で初期化し、ループの前に条件を評価しないdo-whileループです。 - ループ内の処理は、
writeln("i = ", i);
という文で、i
の値を表示します。 - また、
i += 1;
の文により、i
の値が1増えます。 - ループの条件式は
i > 5
ですが、最初の評価時点ではi
の値は1です。そのため、条件i > 5
は成り立たず、ループは1回だけ実行されます。
- 構文(EBNF)
do-while文 ::= "do" 文 "while" 式
for
[編集]forループは範囲(range)、ドメイン(domain)、配列(array)、イテレータ(iterator)、または反復可能な任意のクラスを反復処理します。
var ary = [ 2, 3, 5, 7 , 11]; writeln("Apply for loop to ", ary); for i in ary do write(i * i, " "); var rng = 23 .. 31; writeln("\n\nApply for loop to ", rng); for i in rng do write(2**i, " "); writeln();
- 実行結果
Apply for loop to 2 3 5 7 11 4 9 25 49 121 Apply for loop to 23..31 8388608 16777216 33554432 67108864 134217728 268435456 536870912 1073741824 2147483648
- 1つ目のコードでは、
var ary = [2, 3, 5, 7, 11];
という行で、配列ary
を定義しています。この配列には、2、3、5、7、11という数値が格納されています。 - その後、
Apply for loop to [2, 3, 5, 7, 11]
という行で、forループを配列ary
に適用することを示しています。 - ループの中では、各要素を変数
i
に代入し、その要素の2乗を計算して表示しています。具体的には、i * i
を計算して結果を表示しています。 - 2つ目のコードでは、
var rng = 23 .. 31;
という行で、範囲23 .. 31
を定義しています。この範囲には、23から31までの整数が含まれています。 - その後、
Apply for loop to 23 .. 31
という行で、forループを範囲rng
に適用することを示しています。 - ループの中では、範囲内の各値を変数
i
に代入し、その値の2の累乗を計算して表示しています。具体的には、2 ** i
を計算して結果を表示しています。
- 構文(EBNF)
for文 ::= "for" インデックス変数宣言 "in" 反復可能式 "do" 文 | "for" インデックス変数宣言 "in" 反復可能式 ブロック文 | "for" 反復可能式 "do" 文 | "for" 反復可能式 ブロック文 インデックス変数宣言 ::= 識別子 | タプルでグループ化された識別子のリスト 反復可能式 ::= 式 | "zip" "(" 式リスト ")"
連結反復処理(Zippered Iteration)
[編集]キーワードzipを使用して複数の反復対象式をループ内でトラバースする場合、各反復対象が生成する対応する式は、ループのインデックス変数であるタプルとして結合されます。これを連結反復処理と呼びます。 zip()式の最初の反復対象は、ループの反復をリードし、反復空間のサイズと形状を決定します。その後の式はリード反復対象に従います。 これらのフォロワー反復対象は、リード反復対象によって生成される値の数と形状に適合することが期待されます。 たとえば、最初の反復対象がm行n列の2次元配列である場合、後続の反復対象も2次元m×n空間での反復をサポートする必要があります。
for (i, j) in zip(1..3, 4..8 by 2) do write(i, ":", j, ", ");
- 実行結果
1:4, 2:6, 3:8,
パラメータ付きforループ(Parameter For Loops)
[編集]パラメータ付きforループでは、インデックス変数が変数ではなくパラメータとして展開されます。
- 構文(EBNF)
パラメータ付きfor文 ::= "for" "param" 識別子 "in" パラメータ反復可能式 "do" 文 | "for" "param" 識別子 "in" パラメータ反復可能式 ブロック文 パラメータ反復可能式 ::= 範囲リテラル | 範囲リテラル "by" 整数リテラル
Break、Continue、およびLabel文
[編集]
- break文は、含まれるループから抜け出し、直後の文で実行を再開します。
- continue文は、含まれるループの本体の末尾にジャンプし、そこから実行を再開します。
- label文は、breakとcontinueがよりネストされていないループを終了または再開できるように、特定のループに名前を付けるために使用されます。ラベルは、for-、while-do、do-while文にのみ付けることができます。
- break文にラベルがあると、そのラベルに対応するループ文に続く最初の文から実行が継続されます。
- continue文にラベルがある場合、一致するラベルを持つループ文の本文の終わりで実行が継続される。
- 一致するラベルを持つループ文が存在しない場合、コンパイル時エラーが発生します。
並列処理
[編集]並列処理を制御構造に1つに含めるのは奇異に感じられるかもしれません。 ここでは、for文の構文の延長にある forall cofaorall と foreach について扱います。
forall
[編集]forallループは範囲(range)、ドメイン(domain)、配列(array)、イテレータ(iterator)、または反復可能な任意のクラスを反復処理します。 ここまでは、forループと同じですが、forループが単一のタスクによって直列に実行されるのに対し、forallループは任意の数のタスクを使用してループを実行することができます。 その結果、単一のタスクを使用して直列に実行される場合もありますし、反復の数に応じて任意の数のタスクを使用することもできます
var D = {1..1024}; var ary: [D] int; forall i in D do ary[i] = i; var sum = 0; for i in D do sum += ary[i]; writeln("sum = ", sum);
- 実行結果
sum = 524800
- 最初のループは、配列の要素にインデックスを代入しています。この処理は並列化できるのでforallが使えます。
- 二番目のループは、配列の要素の合計を求めていますが、前の要素までの小計がわかるまでは計算にかかれないので、forallは使えずforでシリアルに処理しています。
coforall
[編集]coforall文は、関連する同種のタスクを任意の数作成するために使用できます。構文的には、forループ文と似ていますが、forの代わりにcoforallキーワードを使用します。 動作上、coforallループは、ループの各反復ごとに独立したタスクを作成し、それぞれがループ本体のコピーを実行します。助記的には、coforallループはconcurrent forallと考えることができます。つまり、各反復が並行タスクである並列ループです。 cobegin文と同様に、元のタスクは、coforallの反復に対応する子タスクが完了するまで進行しません。そして、cobeginと同様に、元のタスクはその直接の子供だけを待ちます。子孫は待ちません。
foreach
[編集]foreachループは、ループの反復が順序に依存しないことを示します。 これにより、コンパイラはループをベクトル化したり、実行をGPUにオフロードしたりすることができます。 対照的に、forループは順序に依存し、forallループはイテレータに基づいて並列タスクを作成し、反復を分散することができます。
手続き
[編集]手続き( procedure )は、キーワード proc
を使って定義します。
手続きの定義と実行
[編集]- proc.chpl
proc ipow(base: int, times: int) : int { if times < 0 then halt("invalid"); var result = 1; var i = 0; while i < times { result *= base; i += 1; } return result; } proc main() { writeln("ipow(2, 0) = ", ipow(2, 0)); writeln("ipow(2, 1) = ", ipow(2, 1)); writeln("ipow(2, 2) = ", ipow(2, 2)); writeln("ipow(2, 3) = ", ipow(2, 3)); writeln("ipow(10, 4) = ", ipow(10, 4)); writeln("ipow(2, -1) = ", ipow(2, -1)); }
- 実行結果
ipow(2, 0) = 1 ipow(2, 1) = 2 ipow(2, 2) = 4 ipow(2, 3) = 8 ipow(10, 4) = 10000 proc.chpl:4: error: halt reached - invalid
再帰的呼び出し
[編集]- recursive.chpl
proc ipow(base: int, times: int) : int { if times < 0 then halt("invalid"); else if times == 0 then return 1; else if times == 1 then return base; return base * ipow(base, times - 1); } proc main() { writeln("ipow(2, 0) = ", ipow(2, 0)); writeln("ipow(2, 1) = ", ipow(2, 1)); writeln("ipow(2, 2) = ", ipow(2, 2)); writeln("ipow(2, 3) = ", ipow(2, 3)); writeln("ipow(10, 4) = ", ipow(10, 4)); writeln("ipow(2, -1) = ", ipow(2, -1)); }
ディフォルト引数
[編集]- say_hello.chpl
proc say_hello(who: string = "World") { writeln("Hello ", who, "!"); } proc main() { say_hello(); say_hello("Universe"); }
- 実行結果
Hello World! Hello Universe!
可変長引数
[編集]- varargs.chpl
proc sum(args: int ...?n) : int { writeln("<", args.type:string, ">"); var result = args(0); for i in 1..<n do result += args(i); return result; } proc main() { writeln("sum(3) = ", sum(3)); writeln("sum(2,3) = ", sum(2,3)); writeln("sum(1,2,3) = ", sum(1,2,3)); }
<1*int(64)> sum(3) = 3 <2*int(64)> sum(2,3) = 5 <3*int(64)> sum(1,2,3) = 6
キーワード引数
[編集]- named_args.chpl
proc func(arg1: int, arg2: string) { writeln("arg1 = ", arg1, ", arg2 = ", arg2); } proc main() { func(123, "abc"); func(arg1=256, arg2="XYZ"); func(arg2="Zero", arg1=0); }
- 実行結果
arg1 = 123, arg2 = abc arg1 = 256, arg2 = XYZ arg1 = 0, arg2 = Zero
イテレータ
[編集]イテレータ( Iterators )は、キーワード iter
を使って定義します。
def
を使用していました。
コンパイラは、関数本体にyieldステートメントが存在するかどうかを検査することで、プロシージャかイテレータかを判断していました。
1.4の変更により、これらの2つのケースを明確に区別するために、キーワードproc
とiter
を導入しました。
イテレータの定義と実行
[編集]// イテレータの定義 iter MyIterator(start: int, end: int): int { var current = start; // イテレーション本体 while current <= end { yield current; current += 1; } } // イテレータの使用 var myIter = MyIterator(1, 5); for i in myIter do writeln("i = ", i);
- 実行結果
i = 1 i = 2 i = 3 i = 4 i = 5
この例では、MyIterator
というイテレータを定義しています。イテレータは、start
からend
までの整数を順に生成します。
MyIterator
の定義では、iter
キーワードを使用してイテレータを宣言しています。イテレータの本体はwhile
ループで構成されており、current
の値をイテレータとして返すたびに、yield
ステートメントを使用してその値を生成します。そして、current
を1増やし、次の値を生成するための準備をします。
イテレータを使用するために、MyIterator
を呼び出してmyIter
という変数に代入しています。その後、for
ループを使用して、myIter
からイテレータが生成する値を順番に取得し、writeln
関数を使って出力しています。
手続きとイテレータの比較
[編集]基本的なアルゴリズムが共有する手続きとイテレータを比較してみましょう。
- 素数のリストを返す手続き
proc get_primes(n: int){ use List; var primes = new list(int); for i in 2..n { var flag = true; for prime in primes { if i % prime == 0 { flag = false; break; } } if flag { primes.append(i); } } return primes; } var p = get_primes(100); writeln([i in p] i);
- 実行結果
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
:* このアルゴリズムの主役は、primesというリストです。 :* primes は名前の通り素数のリストで、その中に因数がない数が素数で primes 自身に追加します。
- 素数を順に返すイテレータ
iter gen_primes() { use List; var primes = new list(int); for i in (2..) { var flag = true; for prime in primes { if i % prime == 0 { flag = false; break; } } if flag { primes.append(i); yield i; } } } var p: [1..100] int; for (i, j) in zip(gen_primes(), 1..) { if j > p.size then break; p[j] = i; } writeln([i in p] i);
- 実行結果
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541
:* このアルゴリズムの主役は、primesというリストです。 :* primes は名前の通り素数のリストで、その中に因数がない数が素数で primes 自身に追加します。
素数を順に返すイテレータ(別解)
[編集]- 素数を順に返すイテレータ(別解)
iter primegen() { var n = 2; var D: domain(int); // empty set of integer var sieve: [D] int; while true { if !D.contains(n) || sieve[n] == 0 { yield n; D += n * n; sieve[n * n] = n; } else { var factor = sieve[n]; D += n + factor; sieve[n + factor] = factor; sieve[n] = 0; } n += 1; } } proc main() { for prime in primegen() { write(prime, " "); if prime > 50 then break; } writeln(); }
- 実行結果
2 3 5 7 11 13 15 17 19 21 23 27 29 31 33 35 37 39 41 43 45 47 51
演算子メソッド
[編集]演算子メソッド( Operator Methods )は、キーワード operator
を使って定義します。
演算子メソッドは、Chapelで演算子の振る舞いを定義するために使用されます。これにより、特定の型に対して演算子をオーバーロードすることができます。
演算子メソッドは、次の構文で定義されます。
operator <operatorSymbol>(lhs: T, rhs: T): T { // 演算子の振る舞いを定義するコード // ... return result; // 演算結果を返す }
上記の構文で、<operatorSymbol>
は演算子を表すシンボル(例: +
, -
, *
, /
)を指定します。lhs
とrhs
は同じ型T
の引数であり、演算子の左辺と右辺の値を表します。
operator <operatorSymbol>(lhs: T, rhs: T): T { // 演算子の振る舞いを定義するコード // ... return result; // 演算結果を返す }
演算子メソッドの定義と実行
[編集]record Point { var x: int; var y: int; // 和 operator +(lhs: Point, rhs: Point) do return new Point(lhs.x + rhs.x, lhs.y + rhs.y); // 差 operator -(lhs: Point, rhs: Point) do return new Point(lhs.x - rhs.x, lhs.y - rhs.y); inline operator :(pt: Point, type t: string) { if pt.y >= 0 then return "Point(" + pt.x:string + " + " + pt.y:string + ")"; else return "Point(" + pt.x:string + " - " + (-pt.y):string + ")"; } } var p1 = new Point(2, 3); var p2 = new Point(4, 1); writeln(p1:string," + ", p2:string, " = ", (p1 + p2):string); writeln(p1:string," - ", p2:string, " = ", (p1 - p2):string); operator Point.+(lhs: Point, rhs: int) do return new Point(lhs.x + rhs, lhs.y + rhs); writeln(p1:string," + ", 2, " = ", (p1 + 2):string);
- 実行結果
Point(2 + 3) + Point(4 + 1) = Point(6 + 4) Point(2 + 3) - Point(4 + 1) = Point(-2 + 2) Point(2 + 3) + 2 = Point(4 + 5)
- レコード型
Point
を定義し、+
演算子と-
演算子をオーバーロードしています。また、キャスト演算子:
も定義しています。 - まず、和と差の演算子メソッドを定義しています。
+
演算子メソッドでは、2つのPoint
オブジェクトのx
とy
の値をそれぞれ足し合わせた新しいPoint
オブジェクトを返します。同様に、-
演算子メソッドでは、2つのPoint
オブジェクトのx
とy
の値をそれぞれ引き算した新しいPoint
オブジェクトを返します。 - 次に、キャスト演算子
:
を定義しています。これはPoint
オブジェクトを他の型に変換するための演算子です。例えば、Point(2 + 3)
のような形式の文字列を返します。ただし、y
の値が負の場合は-
を使って表現します。
モジュール
[編集]モジュールはコードのグループ化であり、各プログラムは少なくとも1つのモジュールから構成され、すべてのシンボルはあるモジュールに関連付けられます。 ファイルのすべてのコードがmoduleキーワードを使用して明示的なモジュールで囲まれていない場合、ファイル自体はファイル名(.chpl拡張子を除く)と同じ名前のモジュールとして扱われます。 コンパイラは、コマンドラインでモジュールの名前を指定するか、-Mフラグに依存することで、異なるファイルにモジュールを含めるように指示することができます。
モジュールの定義方法
[編集]
module M1 { var x = 10; var y = 20; // 3: error: symbol y is multiply defined } module M2 { var y = 30; // 7: note: also defined here } module Main { use M1; use M2; proc main() { writeln("M1.x = ", M1.x); writeln("x = ", x); writeln("M1.y = ", M1.y); writeln("M2.y = ", M2.y); // writeln("y = ", y); } }
M1.x = 10 x = 10 M1.y = 20 M2.y = 30
モジュール内の名前空間の制御
[編集]複数のモジュールを持つプログラムでは、モジュールが他のモジュールの内容にアクセスすることが一般的です。そのための出発点は、Chapelのuse文またはimport文です。これらの文は、実行可能なコードを含む任意のレキシカルスコープに挿入することができます。
デフォルトでは、use文は特定のモジュールのすべての可視シンボルを、そのuse文を含むスコープで利用可能にします。これらのシンボルは、モジュール名の接頭辞なしで直接アクセスすることができます。
module M3 { var x = 10; var y = 42; } module M4 { private var y = 100; } module Main { use M3, M4; proc main() { writeln("M3.x = ", M3.x); writeln("x = ", x); writeln("M3.y = ", M3.y); writeln("y = ", y); // writeln("M4.y = ", M4.y); error: Cannot access 'y', 'y' is private to 'M4' } }
M3.x = 10 x = 10 M3.y = 42 y = 42
他のモジュールのシンボルへのアクセス
[編集]
module M5 { var x = 10; } module M6 { var y = 42; } module M7 { var z = 123; } module Main { proc main() { import M5; writeln("M5.x = ", M5.x); // writeln("x = ", x); error: 'x' undeclared (first use this function) import M6.y; // writeln("M6.y = ", M6.y); error: 'M6' undeclared (first use this function) writeln("y = ", y); import M7.z as a; // writeln("M7.z = ", M7.z); error: 'M7' undeclared (first use this function) // writeln("z = ", z); error: 'z' undeclared (first use this function) writeln("a = ", a); } }
M5.x = 10 y = 42 a = 123
モジュールの使用時の名前空間の制御
[編集]
module M8 { var x = 10; } module Main { proc main() { { use M8 as xyz; // writeln("M8.x = ", M8.x); error: 'M8' undeclared (first use this function) writeln("xyz.x = ", xyz.x); writeln("x = ", x); } { use M8 as _; // writeln("M8.x = ", M8.x); error: 'M8' undeclared (first use this function) // writeln("_.x = ", _.x); syntax error: near '_' writeln("x = ", x); } } }
xyz.x = 10 x = 10 x = 10
例外処理
[編集]Chapelにおける例外処理は、通常のプログラミング言語とは異なる独自の機構を持っています。 Chapelでは、並列処理をサポートするために非同期タスクと呼ばれる並列実行単位があります。 非同期タスク内で例外が発生した場合、その例外をキャッチするためのメカニズムが提供されています。
例外の発生と捕捉
[編集]proc divide(a: int, b: int) throws { if a == 0 && b == 0 { throw new DivisionError("Divide zero by zero."); } if b == 0 { throw new DivisionError("Division by zero"); } return a / b; } class DivisionError: Error { var msg: string; } proc main() throws { try { writeln(divide(10, 2)); writeln(divide(0, 0)); writeln(divide(10, 0)); } catch (e: DivisionError) { writeln("Caught DivisionError: ", e.msg); } }
- この例では、
divide
という手続きがあります。この手続きは2つの整数を受け取り、いくつかの条件に基づいて除算を行います。条件に違反する場合、DivisionError
という例外をスローします。 DivisionError
は、Error
クラスを継承しており、msg
というメッセージを保持するプロパティがあります。main
手続きでは、divide
手続きを呼び出して結果を出力します。最初の呼び出しでは正常な除算が行われ、結果が表示されます。2番目の呼び出しでは、ゼロをゼロで除算するため、例外がスローされます。3番目の呼び出しではゼロで割るため、同様に例外がスローされます。try
ブロックで呼び出しを囲み、例外をキャッチするためのcatch
ブロックがあります。catch
ブロックでは、DivisionError
のインスタンスをキャッチして、エラーメッセージを表示します。- この例では、
throws
キーワードを使用して手続きとメイン関数のシグネチャに例外の可能性を示しています。これにより、例外が発生する可能性がある処理を明示的に示すことができます。
ジェネリックプログラミング
[編集]Chapelでは、ジェネリックプログラミングのサポートがあります。 ジェネリックプログラミングは、異なるデータ型に対して汎用的なアルゴリズムやデータ構造を定義するためのテクニックです。 Chapelでは、型パラメータを使用してジェネリックなコードを記述することができます。
ジェネリック関数とジェネリックデータ構造
[編集]- ジェネリック関数とジェネリックデータ構造
// ジェネリック関数 proc findMax(ary){ var D = ary.domain; var maxVal = ary[D.low]; for item in ary[D.low+1..] do if item > maxVal then maxVal = item; return maxVal; } // ジェネリックデータ構造 class Stack { type eltType; var items: [1..256] eltType; var top: int = 0; proc push(item: eltType) { top += 1; items[top] = item; } proc pop() { var item = items[top]; top -= 1; return item; } } // 使用例 var ary = [3, 7, 2, 9, 5]; writeln("Max value: ", findMax(ary)); // => Max value: 9 var stack = new Stack[int]; stack.push(1); stack.push(2); stack.push(3); writeln("Popped item: ", stack.pop()); // => Popped item: 3 writeln("Popped item: ", stack.pop()); // => Popped item: 2 stack.push(4); stack.push(5); stack.push(6); writeln("Popped item: ", stack.pop()); // => Popped item: 6 writeln("Popped item: ", stack.pop()); // => Popped item: 5 writeln("Popped item: ", stack.pop()); // => Popped item: 4 writeln("Popped item: ", stack.pop()); // => Popped item: 1
- このプログラムは、ジェネリック関数とジェネリックデータ構造を使用しています。
- まず、
findMax
という名前のジェネリック関数が定義されています。この関数は、配列ary
の要素の中から最大値を見つけるためのものです。ary
の最初の要素を初期の最大値maxVal
として設定し、ary
の2番目以降の要素についてループを行います。各要素がmaxVal
よりも大きい場合、maxVal
を更新します。最終的に、maxVal
を返します。 - 次に、
Stack
という名前のジェネリックデータ構造が定義されています。このデータ構造は、ジェネリックな要素の配列items
と、トップの位置を示す整数top
を持っています。push
プロシージャは、item
をitems
の次の位置に追加し、top
をインクリメントします。pop
プロシージャは、top
の位置から要素を取り出し、top
をデクリメントしてからその要素を返します。 - 最後に、このプログラムではこれらのジェネリック関数とジェネリックデータ構造が使用されています。
- まず、整数型の配列
aryr
が定義されています。この配列は[3, 7, 2, 9, 5]
という要素を持ちます。findMax
関数を使用してary
の最大値を見つけ、writeln
関数を使用して結果を表示します。 - 次に、
Stack
データ構造のインスタンスstack
が作成されます。stack.push
メソッドを使用して、要素1、2、3をstack
に追加します。stack.pop
メソッドを使用して要素を取り出し、結果を表示します。stack.push
メソッドとstack.pop
メソッドをさらに使用し、要素4、5、6を追加・取り出し、結果を表示します。
オブジェクト指向プログラミング
[編集]並行プログラミング
[編集]
附録
[編集]コードギャラリー
[編集]エラトステネスの篩
[編集]エラトステネスの篩を、若干 Chapel らしく書いてみました。
- エラトステネスの篩
// `eratosthenes` は、「エラトステネスの篩」を使用して、2 から `n` までの素数のリストを返します。 proc eratosthenes(n: int) { // `sieve` は、2 から `n` までの範囲の値をインデックスとする配列です。 // これは、2 から `n` までのすべての素数候補を表しています。 // 素数候補を全て暫定的に真とします。 var sieve: [2..n] bool = true; const sqrt_n = ceil(n ** 0.5); // sqrt_n は、n の平方根(切り上げ) for i in 2..sqrt_n:int do if sieve[i] then // `sieve[i]` が真の場合、`i` は素数である。 sieve[2 * i .. n by i] = false; // 素数である `i` の倍数を全て偽とする。 // 結果を表示します。 writeln(for (p, i) in zip(sieve, sieve.domain) do if p then i); } proc main() { eratosthenes(100); }
- 実行結果
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
クイックソート
[編集]クイックソートを、若干 Chapel らしく書いてみました。
- クイックソート
proc quicksort(ary: [?dom]) { var i = dom.low; var j = dom.high; const pivot = ary[(dom.low + dom.high) / 2]; while i <= j { while ary[i] < pivot do i += 1; while ary[j] > pivot do j -= 1; if i <= j then (ary[i], ary[j], i, j) = (ary[j], ary[i], i + 1, j - 1); } if dom.low < j then quicksort(ary[dom.low..j]); if i < dom.high then quicksort(ary[i..dom.high]); } proc main() { var ary = [9, 5, 7, 2, 4, 1, 8, 3, 10, 6]; writeln("Original Array:\t", ary); quicksort(ary); writeln("Sorted Array:\t", ary); }
- 実行結果
Original Array: 9 5 7 2 4 1 8 3 10 6 Sorted Array: 1 2 3 4 5 6 7 8 9 10
proc quicksort(ary: [?dom]) {
のary: [?dom]</syntaxhighlight copy>が特徴的で、配列を渡すと、ドメイン(添字範囲)も暗黙に渡ります。 :これは再帰でも応用され<syntaxhighlight lang=chapel inline>quicksort(ary[dom.low..j]);</syntaxhighlight copy>のように部分配列を意味どおりに表現しています。 ==== 最大公約数と最小公倍数 ==== {{先頭に戻る|title=コード・ギャラリーに戻る|label=コードギャラリー|style=border-top:1px solid gray;}} 最大公約数と最小公倍数を、若干 Chapel らしく書いてみました。 ;[https://ato.pxeger.com/run?1=rVRRS9tQFH7Zy_IrDsIgkbu01oli1-23lJpqILmVNJ0PQ-htOrY9DDchijiYSO1kkyooCLX46n_Y6zF1_Rc7uTeZmdtqB4OQe--53znn-84Hd7dbWSmvWs7e3kHDrz5e-PbgKpeD5cpSAVAcY1PEawdFbxieDcMTcOl4CBziyMdm1PkcvTq6OWvTFbY2vx--uelto6DvEsUONlsaFbOrhC-VIA_-isWpguXULdlC54yOj4AbSTMMuhgMsHWMwRcMAgzeUp_r_uZopzM8DVF8jT71URygaKPoju3flZiUxapXq6iO7iLY3GfA5WrIPyzVwLP8hscn4lrUNDWihPT1xcWwvUFER-E5ivDnrGgTDUK1mWhWKUu97C3XFTPTNJ_zhOVLDeBF2SOq9YbjQwlimJ4nPgDVmgc2oWDGNJ9yEkQxuEVKAerEVJptyLxEtroqautSmlNxJ3P_ZIMURc1349x3YZoScsn02a3XYwv8wb6Y1X32_d6smCr6R7PulZZy-v9mSaETm-WWbZ5oi1WzWDuTNVgiuxe9b4-29pWS6MMAxWn0uv-3Mcf1dEPyB1jzbN9yuD4lBzqbZ_BkzoDSM5hikA0RvYdZrIzPE4OZQgaejf6SIRWryxSeDd3Fqq6FO_BslDLW1XuWPGvp8_YD 最大公約数と最小公倍数]:<syntaxhighlight lang=chapel line copy> // gcd2 は、2 つの整数 m と n の最大公約数を計算します。 // if n == 0 then m else gcd2(n, m % n) は、ユークリッドの互除法に基づいて最大公約数を計算しています。 proc gcd2(m: int, n: int): int do return if n == 0 then m else gcd2(n, m % n); // gcd は、任意の長さの整数の引数の最大公約数を計算します。 proc gcd(args: int ...?n): int { var result = args(0); for i in 1..<n do result = gcd2(result, args(i)); return result; } // lcm2 は、2 つの整数 m と n の最小公倍数を計算します。 // m * n / gcd2(m, n) は、最小公倍数を計算しています。 proc lcm2(m: int, n: int): int do return m * n / gcd2(m, n); // lcm は、任意の長さの整数の引数の最小公倍数を計算します。 proc lcm(args: int ...?n): int { var result = args(0); for i in 1..<n do result = lcm2(result, args(i)); return result; } // main は、gcd2, gcd, lcm2, lcm の各関数を呼び出しています。 proc main() { writeln("gcd2(30, 45) => ", gcd2(30, 45)); writeln("gcd(30, 72, 12) => ", gcd(30, 72, 12)); writeln("lcm2(30, 72) => ", lcm2(30, 72)); writeln("lcm(30, 42, 72) => ", lcm(30, 42, 72)); }
- 実行結果
gcd2(30, 45) => 15 gcd(30, 72, 12) => 6 lcm2(30, 72) => 360 lcm(30, 42, 72) => 2520
二分法
[編集]二分法を、若干 Chapel らしく書いてみました。
- 二分法
// bisection は、関数 f が 0 に等しくなる実数 x を二分法で検索します。 // low と high は、関数 f の値が 0 より小さい範囲の下限と上限です。 // x は、現在の中点です。 // fx は、関数 f の x の値です。 proc bisection(_low: real, _high: real, f: proc(x: real): real) : real { // Chapelでは、引数はイミュータブルなのでローカル変数にコピーします。 var low = _low, high = _high; // 中点を計算します。 var x = (low + high) / 2; // f の x の値を計算します。 var fx = f(x); // fx が十分に小さい場合は、x を返します。 if (abs(fx) < +1.0e-10) then return x; // 範囲を更新します。 if (fx < 0.0) then // fx が負の場合は、low を x に設定します。 low = x; else // fx が正の場合は、high を x に設定します。 high = x; // 再帰的に呼び出し、関数 f が 0 に等しくなる x を見つけます。 return bisection(low, high, f); } proc main() { writef("%{#.################}\n", bisection(0, 3, proc(x: real): real { return x - 1; })); writef("%{#.################}\n", bisection(0, 3, proc(x: real): real { return x * x - 1; })); }
- 実行結果
0.9999999999417923 1.0000000000291038
- 旧課程(-2012年度)高等学校数学B/数値計算とコンピューター#2分法の例を Chapel に移植しました。
順列
[編集]順列を、若干 Chapel らしく書いてみました。
- 順列
proc permutation(s: [?dom] ?eltType, n: int): [1..if n == 1 then dom.size else factorial(dom.size), 1..n] eltType { var result: [1..if n == 1 then dom.size else factorial(dom.size), 1..n] eltType; if n == 1 { for (ri, si) in zip(result.domain.dim(0),s.domain) do result(ri, 1) = s(si); return result; } var i1 = result.domain.dim(0).low; for i in s.domain do { var sub = s[for j in s.domain do if i != j then j]; var pp = permutation(sub, n - 1); for p1 in pp.domain.dim(0) do { var j1 = result.domain.dim(1).low; result(i1, j1) = s(i); j1 += 1; for p2 in pp.domain.dim(1) do { result(i1, j1) = pp(p1, p2); j1 += 1; } i1 += 1; } } return result; } proc factorial(n: int): int do return if n == 0 then 1 else n * factorial(n - 1); proc main() { writeln(permutation([10, 20, 30, 40], 3)); writeln(permutation([0, 1, 2], 2)); writeln(permutation(["abc", "def", "xyz"], 3)); writeln(permutation([1, 2, 3], 1)); writeln(permutation(["abc", "def", "xyz"], 1)); }
- 実行結果
10 20 30 10 20 40 10 30 20 10 30 40 10 40 20 10 40 30 20 10 30 20 10 40 20 30 10 20 30 40 20 40 10 20 40 30 30 10 20 30 10 40 30 20 10 30 20 40 30 40 10 30 40 20 40 10 20 40 10 30 40 20 10 40 20 30 40 30 10 40 30 20 0 1 0 2 1 0 1 2 2 0 2 1 abc def xyz abc xyz def def abc xyz def xyz abc xyz abc def xyz def abc 1 2 3 abc def xyz
proc permutation(s: [?dom] ?eltType, n: int): [1..if n == 1 then dom.size else factorial(dom.size), 1..n] eltType
戻値型は二次元配列で次元ごとの要素数が引数により変化するので関数を呼び出し決定しています。また n == 1 はfactorial()では求められないので、if式で切り替えています。- 順列自身は再帰的な定義なので、定義どおりに再帰しています。
- 再帰関数は、戻値型が必須なので冒頭のやや複雑で動的な戻値型の定義が必要になります。
- このコードはジェネリックプログラミングの例にもなっています。
組み合わせ
[編集]組み合わせを、若干 Chapel らしく書いてみました。
- 組み合わせ
proc combination(s: [?dom] ?eltType, n: int): [1..if n == 1 then dom.size else factorial(dom.size) / factorial(n), 1..n] eltType { var result: [1..if n == 1 then dom.size else factorial(dom.size) / factorial(n), 1..n] eltType; if n == 1 { for (ri, si) in zip(result.domain.dim(0),s.domain) do result(ri, 1) = s(si); return result; } var i1 = result.domain.dim(0).low; for i in s.domain do { var sub = s[for j in s.domain do if j > i then j]; var pp = combination(sub, n - 1); for p1 in pp.domain.dim(0) do { var j1 = result.domain.dim(1).low; result(i1, j1) = s(i); j1 += 1; for p2 in pp.domain.dim(1) do { result(i1, j1) = pp(p1, p2); j1 += 1; } i1 += 1; } } return result; } proc factorial(n: int): int do return if n == 0 then 1 else n * factorial(n - 1); proc main() { writeln(combination([10, 20, 30, 40], 3)); writeln(combination([0, 1, 2], 2)); writeln(combination(["abc", "def", "xyz"], 3)); writeln(combination([1, 2, 3], 1)); }
- 実行結果
10 20 30 10 20 40 10 30 40 20 30 40 0 1 0 2 1 2 abc def xyz 1 2 3
- 順列と同じく戻値型が複雑で動的です。
- 組み合わせ自身は再帰的な定義なので、定義どおりに再帰しています。
- 再帰関数は、戻値型が必須なので冒頭のやや複雑で動的な戻値型の定義が必要になります。
- このコードもジェネリックプログラミングの例にもなっています。
構文
[編集]Chapel-1.30 の構文です。
program ::= toplevel-statment-list toplevel-statment-list ::= (* empty *) | toplevel-statment-list toplevel-statment toplevel-statment ::= statment | pragma-list statment pragma-list ::= "pragma" STRINGLITERAL | pragma-list "pragma" STRINGLITERAL statment ::= attribute-group attribute-receiver | statment-base statment-base ::= tryable-statment | decl-base | include-module-statment | block-statment | use-statment | import-statment | require-statment | extern-block-statment | implements-statment | interface-statment | "defer" statment | try-statment | return-statment | "break" optional-label-ident ";" | "continue" optional-label-ident ";" | "label" ident-def statment | "yield" optional-try-expression ";" | error ";" tryable-statment ::= assignment-statment | if-statment | loop-statment | select-statment | manage-statment | statment-level-expression ";" | throw-statment | "begin" optional-task-intent-list statment | "cobegin" optional-task-intent-list block-statment | "delete" expression-list ";" | "local" expression do-statment | "local" do-statment | "on" expression do-statment | "serial" expression do-statment | "serial" do-statment | "sync" statment decl-base ::= module-declaration-statment | class-level-statment attribute-receiver ::= loop-statment | decl-base | block-statment attribute-group ::= attribute-declaration-statment-list attribute-declaration-statment-list ::= attribute-declaration-statment | attribute-declaration-statment-list attribute-declaration-statment attribute-declaration-statment ::= attribute-declaration-begin optional-attribute-declaration-toolspace optional-attribute-actuals | attribute-declaration-begin optional-attribute-declaration-toolspace | attribute-declaration-begin STRINGLITERAL attribute-declaration-begin ::= "@" TIDENT optional-attribute-declaration-toolspace ::= (* empty *) | "." TIDENT optional-attribute-declaration-toolspace optional-attribute-actuals ::= "(" ")" | "(" attribute-actuals-list ")" attribute-actuals-list ::= attribute-actual | attribute-actuals-list "," attribute-actual attribute-actual ::= actual-expression module-declaration-start ::= optional-access-control optional-prototype "module" ident-def module-declaration-statment ::= module-declaration-start "{" "}" | module-declaration-start "{" statment-list "}" | module-declaration-start "{" error "}" optional-access-control ::= (* empty *) | "public" | "private" optional-prototype ::= (* empty*) | "prototype" include-module-statment ::= "include" optional-access-control optional-prototype "module" ident-def ";" block-statment-body ::= "{" "}" | "{" statment-list "}" | "{" error "}" block-statment ::= block-statment-body statment-list ::= toplevel-statment | statment-list toplevel-statment renames-list ::= expression | all-operator-name | expression "as" expression | renames-list "," expression | renames-list "," all-operator-name | renames-list "," expression "as" expression use-renames-list ::= expression | expression "as" expression | expression "as" TUNDERSCORE | use-renames-list "," expression | use-renames-list "," expression "as" expression | use-renames-list "," expression "as" TUNDERSCORE optional-only-list ::= (* empty *) | renames-list use-statment ::= optional-access-control "use" use-renames-list ";" | optional-access-control "use" expression "except" renames-list ";" | optional-access-control "use" expression "as" expression "except" renames-list ";" | optional-access-control "use" expression "as" TUNDERSCORE "except" renames-list ";" | optional-access-control "use" expression "only" optional-only-list ";" | optional-access-control "use" expression "as" expression "only" optional-only-list ";" | optional-access-control "use" expression "as" TUNDERSCORE "only" optional-only-list ";" import-statment ::= optional-access-control "import" import-list ";" import-expression ::= expression | expression "." all-operator-name | expression "as" ident-use | expression "." "{" renames-list "}" import-list ::= import-expression | import-list "," import-expression require-statment ::= "require" expression-list ";" assignment-statment ::= lhs-expression assignop-ident optional-try-expression ";" | lhs-expression "<=>" optional-try-expression ";" | lhs-expression "reduce=" optional-try-expression ";" | lhs-expression "&&=" optional-try-expression ";" | lhs-expression "||=" optional-try-expression ";" | lhs-expression "=" "noinit" ";" optional-label-ident ::= (* empty*) | TIDENT reserved-word-ident ::= "none" | "this" | "false" | "true" | internal-type-ident-def ident-def ::= TIDENT | reserved-word-ident ident-use ::= TIDENT | "this" internal-type-ident-def ::= "bool" | "int" | "uint" | "real" | "imag" | "complex" | "bytes" | "string" | "sync" | "single" | "owned" | "shared" | "borrowed" | "unmanaged" | "domain" | "index" | "locale" | "nothing" | "void" scalar-type ::= "bool" | "enum" | "int" | "uint" | "real" | "imag" | "complex" | "bytes" | "string" | "locale"| "nothing" | "void" reserved-type-ident-use ::= "sync" | "single" | "domain" | "index" do-statment ::= "do" statment | block-statment return-statment ::= "return" ";" | "return" optional-try-expression ";" class-level-statment ::= ";" | inner-class-level-statment | "public" | "private" inner-class-level-statment ::= fn-declaration-statment-complete | var-declaration-statment | enum-declaration-statment | type-alias-declaration-statment | class-decl-statment | forwarding-declaration-statment | extern-export-declaration-statment forwarding-declaration-statment ::= forwarding-declaration-start expression ";" | forwarding-declaration-start expression "except" renames-list ";" | forwarding-declaration-start expression "only" optional-only-list ";" | forwarding-declaration-start var-declaration-statment forwarding-declaration-start ::= "forwarding" extern-or-export ::= "extern" | "export" extern-export-declaration-statment-start ::= extern-or-export extern-export-declaration-statment ::= extern-export-declaration-statment-start class-start optional-inherit "{" class-level-statment-list "}" | extern-export-declaration-statment-start STRINGLITERAL class-start optional-inherit "{" class-level-statment-list "}" | extern-export-declaration-statment-start optional-expression fn-declaration-statment | extern-export-declaration-statment-start optional-expression var-declaration-type var-declaration-statment-inner-list ";" | extern-export-declaration-statment-start optional-expression "type" type-alias-declaration-statment-inner-list ";" extern-block-statment ::= "extern" EXTERNCODE loop-statment ::= "do" statment "while" expression ";" | "while" expression do-statment | "while" ifvar do-statment | "coforall" expression "in" expression optional-task-intent-list do-statment | "coforall" expression "in" zippered-iterator optional-task-intent-list do-statment | "coforall" expression optional-task-intent-list do-statment | "for" expression "in" expression do-statment | "for" expression "in" zippered-iterator do-statment | "for" expression do-statment | "for" zippered-iterator do-statment | "for" "param" ident-def "in" expression do-statment | "forall" expression "in" expression do-statment | "forall" expression "in" expression forall-intent-clause do-statment | "forall" expression "in" zippered-iterator do-statment | "forall" expression "in" zippered-iterator forall-intent-clause do-statment | "forall" expression do-statment | "forall" expression forall-intent-clause do-statment | "forall" zippered-iterator do-statment | "forall" zippered-iterator forall-intent-clause do-statment | "foreach" expression "in" expression do-statment | "foreach" expression "in" expression forall-intent-clause do-statment | "foreach" expression "in" zippered-iterator do-statment | "foreach" expression "in" zippered-iterator forall-intent-clause do-statment | "foreach" expression do-statment | "foreach" expression forall-intent-clause do-statment | "foreach" zippered-iterator do-statment | "foreach" zippered-iterator forall-intent-clause do-statment | "[" expression-list "in" expression "]" statment | "[" expression-list "in" expression forall-intent-clause "]" statment | "[" expression-list "in" zippered-iterator "]" statment | "[" expression-list "in" zippered-iterator forall-intent-clause "]" statment | "[" expression-list "]" statment | "[" expression-list forall-intent-clause "]" statment | "[" zippered-iterator "]" statment | "[" zippered-iterator forall-intent-clause "]" statment zippered-iterator ::= "zip" "(" expression-list ")" if-statment ::= "if" expression "then" statment | "if" expression block-statment | "if" expression "then" statment "else" statment | "if" expression block-statment "else" statment | "if" ifvar "then" statment | "if" ifvar block-statment | "if" ifvar "then" statment "else" statment | "if" ifvar block-statment "else" statment | "if" expression assignop-ident expression "then" statment | "if" expression assignop-ident expression block-statment | "if" expression assignop-ident expression "then" statment "else" statment | "if" expression assignop-ident expression block-statment "else" statment ifvar ::= "var" ident-def "=" expression | "const" ident-def "=" expression interface-statment ::= "interface" ident-def "(" ifc-formal-list ")" block-statment | "interface" ident-def block-statment ifc-formal-list ::= ifc-formal | ifc-formal-list "," ifc-formal ifc-formal ::= ident-def implements-type-ident ::= TIDENT | "bool" | "int" | "uint" | "real" | "imag" | "complex" | "bytes" | "string" | "locale" | "nothing" | "void" | "none" | "this" | "false" | "true" | "domain" | "index" implements-statment ::= "implements" ident-def "(" actual-list ")" ";" | implements-type-ident "implements" ident-def ";" | implements-type-ident "implements" ident-def "(" actual-list ")" ";" ifc-constraint ::= "implements" ident-def "(" actual-list ")" | implements-type-ident "implements" ident-def | implements-type-ident "implements" ident-def "(" actual-list ")" try-statment ::= "try" tryable-statment | "try!" tryable-statment | "try" block-statment catch-expression-list | "try!" block-statment catch-expression-list catch-expression-list ::= (* empty *) | catch-expression-list catch-expression catch-expression ::= "catch" block-statment | "catch" catch-expression-inner block-statment | "catch" "(" catch-expression-inner ")" block-statment catch-expression-inner ::= ident-def | ident-def ":"expression throw-statment ::= "throw" expression ";" select-statment ::= "select" expression "{" when-statment-list "}" | "select" expression "{" error "}" when-statment-list ::= (* empty*) | when-statment-list when-statment when-statment ::= "when" expression-list do-statment | "otherwise" statment | "otherwise" "do" statment manager-expression ::= expression "as" var-declaration-type ident-def | expression "as" ident-def | expression manager-expression-list ::= manager-expression | manager-expression-list "," manager-expression manage-statment ::= "manage" manager-expression-list do-statment class-decl-statment ::= class-start optional-inherit "{" class-level-statment-list "}" | class-start optional-inherit "{" error "}" class-start ::= class-tag ident-def class-tag ::= "class" | "record" | "union" optional-inherit ::= (* empty *) | ":"expression-list class-level-statment-list ::= (* empty *) | class-level-statment-list class-level-statment | class-level-statment-list attribute-declaration-statment-list class-level-statment | class-level-statment-list pragma-list class-level-statment | class-level-statment-list pragma-list attribute-declaration-statment-list class-level-statment enum-declaration-statment ::= enum-header-lcbr enum-list "}" | enum-header-lcbr error "}" enum-header-lcbr ::= "enum" ident-def "{" enum-list ::= enum-item | enum-list "," | enum-list "," enum-item | attribute-declaration-statment-list enum-item | enum-list "," attribute-declaration-statment-list enum-item enum-item ::= ident-def | ident-def "=" expression lambda-declaration-start ::= "lambda" lambda-declaration-expression ::= lambda-declaration-start req-formal-list optional-ret-tag optional-type optional-throws-error optional-lifetime-where function-body-statment linkage-spec-empty ::= (* empty *) linkage-spec ::= linkage-spec-empty | "inline" | "override" optional-fn-type-formal-list ::= (* empty *) | fn-type-formal-list fn-type-formal-list ::= fn-type-formal | fn-type-formal-list "," fn-type-formal fn-type-formal ::= named-formal | required-intent-tag ":"formal-type | formal-type optional-fn-type-ret-type ::= (* empty *) | ":"formal-or-ret-type-expression fn-type ::= "proc(" optional-fn-type-formal-list ")" optional-ret-tag optional-fn-type-ret-type optional-throws-error fn-expression ::= fn-type block-statment-body | fn-type "alias" expression fn-declaration-statment-complete ::= fn-declaration-statment fn-declaration-statment ::= fn-declaration-statment-inner optional-ret-tag optional-ret-type optional-throws-error optional-lifetime-where optional-function-body-statment fn-declaration-statment-inner ::= fn-declaration-statment-start optional-this-intent-tag fn-ident optional-formal-list | fn-declaration-statment-start optional-this-intent-tag assignop-ident optional-formal-list | fn-declaration-statment-start optional-this-intent-tag fn-declaration-receiver-expression "." fn-ident optional-formal-list | fn-declaration-statment-start optional-this-intent-tag fn-declaration-receiver-expression "." assignop-ident optional-formal-list | fn-declaration-statment-start optional-this-intent-tag error optional-formal-list fn-declaration-statment-start ::= linkage-spec proc-iter-or-operator fn-declaration-receiver-expression ::= ident-expression | "(" expression ")" fn-ident ::= ident-def | op-ident | ident-def "!" op-ident ::= "&" | "|" | "^" | "~" | "==" | "!=" | "<=" | ">=" | "<" | ">" | "+" | "-" | "*" | "/" | "<<" | ">>" | "%" | "**" | "!" | "by" | "#" | "align" | "<=>" | "init=" | ":" assignop-ident ::= "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "**=" | "&=" | "|=" | "^=" | ">>=" | "<<=" all-operator-name ::= op-ident | assignop-ident formal-var-arg-expression ::= "..." | "..." expression optional-formal-list ::= (* empty *) | "(" formal-list ")" req-formal-list ::= "(" ")" | "(" formal-list-inner ")" formal-list-inner ::= formal | formal-list-inner "," formal formal-list ::= (* empty *) | formal-list-inner formal ::= named-formal formal-ident-def ::= ident-def | TUNDERSCORE named-formal ::= optional-formal-intent-tag formal-ident-def optional-colon-formal-type optional-init-expression | pragma-list optional-formal-intent-tag formal-ident-def optional-colon-formal-type optional-init-expression | optional-formal-intent-tag formal-ident-def optional-colon-formal-type formal-var-arg-expression | pragma-list optional-formal-intent-tag formal-ident-def optional-colon-formal-type formal-var-arg-expression | optional-formal-intent-tag "(" tuple-var-declaration-statment-inner-list ")" optional-colon-formal-type optional-init-expression | optional-formal-intent-tag "(" tuple-var-declaration-statment-inner-list ")" optional-colon-formal-type formal-var-arg-expression optional-formal-intent-tag ::= (* empty *) | required-intent-tag required-intent-tag ::= "in" | "inout" | "out" | "const" "in" | "const" "ref" | "const" | "param" | "ref" | "type" optional-this-intent-tag ::= (* empty *) | "param" | "ref" | "const" "ref" | "const" | "type" proc-iter-or-operator ::= "proc" | "iter" | "operator" optional-ret-tag ::= (* empty *) | "const" | "const" "ref" | "ref" | "param" | "type" optional-throws-error ::= (* empty *) | "throws" optional-function-body-statment ::= ";" | function-body-statment function-body-statment ::= block-statment-body | "do" toplevel-statment | return-statment query-expression ::= TQUERIEDIDENT optional-lifetime-where ::= (* empty *) | "where" expression | "lifetime" lifetime-components-expression | "where" expression "lifetime" lifetime-components-expression | "lifetime" lifetime-components-expression "where" expression lifetime-components-expression ::= lifetime-expression | lifetime-components-expression "," lifetime-expression lifetime-expression ::= lifetime-ident "=" lifetime-ident | lifetime-ident "<" lifetime-ident | lifetime-ident "<=" lifetime-ident | lifetime-ident "==" lifetime-ident | lifetime-ident ">" lifetime-ident | lifetime-ident ">=" lifetime-ident | "return" lifetime-ident lifetime-ident ::= TIDENT | "this" type-alias-declaration-statment ::= type-alias-declaration-statment-start type-alias-declaration-statment-inner-list ";" type-alias-declaration-statment-start ::= "type" | "config" "type" type-alias-declaration-statment-inner-list ::= type-alias-declaration-statment-inner | type-alias-declaration-statment-inner-list "," type-alias-declaration-statment-inner type-alias-declaration-statment-inner ::= ident-def optional-init-type optional-init-type ::= (* empty *) | "=" expression var-declaration-type ::= "param" | "const" "ref" | "ref" | "const" | "var" var-declaration-statment ::= "config" var-declaration-type var-declaration-statment-inner-list ";" | var-declaration-type var-declaration-statment-inner-list ";" var-declaration-statment-inner-list ::= var-declaration-statment-inner | var-declaration-statment-inner-list "," var-declaration-statment-inner var-declaration-statment-inner ::= ident-def optional-type optional-init-expression | "(" tuple-var-declaration-statment-inner-list ")" optional-type optional-init-expression tuple-var-declaration-component ::= TUNDERSCORE | ident-def | "(" tuple-var-declaration-statment-inner-list ")" tuple-var-declaration-statment-inner-list ::= tuple-var-declaration-component | tuple-var-declaration-statment-inner-list "," | tuple-var-declaration-statment-inner-list "," tuple-var-declaration-component optional-init-expression ::= (* empty *) | "=" "noinit" | "=" optional-try-expression formal-or-ret-type-expression ::= expression ret-type ::= formal-or-ret-type-expression | reserved-type-ident-use | error colon-ret-type ::= ":" ret-type | error optional-ret-type ::= (* empty *) | colon-ret-type optional-type ::= (* empty *) | ":"expression | ":"reserved-type-ident-use | error formal-type ::= formal-or-ret-type-expression | reserved-type-ident-use colon-formal-type ::= ":" formal-type optional-colon-formal-type ::= (* empty *) | colon-formal-type expression-list ::= expression | expression-list "," expression tuple-component ::= TUNDERSCORE | optional-try-expression tuple-expression-list ::= tuple-component "," tuple-component | tuple-expression-list "," tuple-component optional-actual-list ::= (* empty *) | actual-list actual-list ::= actual-expression | actual-list "," actual-expression actual-expression ::= ident-use "=" optional-try-expression | optional-try-expression ident-expression ::= ident-use | scalar-type sub-type-level-expression ::= nil-expression | lhs-expression | cond-expression | unary-operator-expression | binary-operator-expression | "single" expression | "index" "(" optional-actual-list ")" | "domain" "(" optional-actual-list ")" | "subdomain" "(" optional-actual-list ")" | "sparse" "subdomain" "(" actual-expression ")" | "atomic" expression | "sync" expression | "owned" | "owned" expression | "unmanaged" | "unmanaged" expression | "shared" | "shared" expression | "borrowed" | "borrowed" expression | "class" | "record" for-expression ::= "for" expression "in" expression "do" expression | "for" expression "in" zippered-iterator "do" expression | "for" expression "do" expression | "for" expression "in" expression "do" "if" expression "then" expression | "for" expression "in" zippered-iterator "do" "if" expression "then" expression | "for" expression "do" "if" expression "then" expression | "forall" expression "in" expression "do" expression | "forall" expression "in" zippered-iterator "do" expression | "forall" expression "do" expression | "forall" expression "in" expression "do" "if" expression "then" expression | "forall" expression "in" zippered-iterator "do" "if" expression "then" expression | "forall" expression "do" "if" expression "then" expression bracket-loop-expression ::= "[" "]" | "[" "]" expression | "[" expression-list "]" expression | "[" expression-list "in" expression "]" expression | "[" expression-list "in" zippered-iterator "]" expression | "[" expression-list "in" expression "]" "if" expression "then" expression | "[" expression-list "in" zippered-iterator "]" "if" expression "then" expression cond-expression ::= "if" expression "then" expression "else" expression nil-expression ::= "nil" statment-level-expression ::= nil-expression | ident-expression | dot-expression | call-expression | lambda-declaration-expression | new-expression | let-expression optional-task-intent-list ::= (* empty*) | task-intent-clause task-intent-clause ::= "with" "(" task-intent-list ")" task-intent-list ::= intent-expression | task-intent-list "," intent-expression forall-intent-clause ::= "with" "(" forall-intent-list ")" forall-intent-list ::= intent-expression | forall-intent-list "," intent-expression intent-expression ::= task-var-prefix ident-expression optional-type optional-init-expression | reduce-scan-operator-expression "reduce" ident-expression | expression "reduce" ident-expression task-var-prefix ::= "const" | "in" | "const" "in" | "ref" | "const" "ref" | "var" new-maybe-decorated ::= "new" | "new" "owned" | "new" "shared" | "new" "unmanaged" | "new" "borrowed" new-expression ::= new-maybe-decorated expression | "new" "owned" "(" expression ")" "(" optional-actual-list ")" | "new" "shared" "(" expression ")" "(" optional-actual-list ")" | "new" "owned" "(" expression ")" "(" optional-actual-list ")" "?" | "new" "shared" "(" expression ")" "(" optional-actual-list ")" "?" let-expression ::= "let" var-declaration-statment-inner-list "in" expression range-literal-expression ::= expression ".." expression | expression "..<" expression | expression ".." | ".." expression | "..<" expression | ".." | expression "<.." expression | expression "<..<" expression | expression "<.." cast-expression :: |expression ":" expression tuple-expand-expression ::= "(" "..." expression ")" super-expression ::= fn-expression | expression expression ::= for-expression | sub-type-level-expression | sub-type-level-expression "?" | "?" | bracket-loop-expression | query-expression | literal-expression | fn-type | reduce-expression | scan-expression | lambda-declaration-expression | new-expression | let-expression | ifc-constraint | tuple-expand-expression | cast-expression | range-literal-expression optional-expression ::= (* empty *) | expression optional-try-expression ::= "try" expression | "try!" expression | super-expression lhs-expression ::= ident-expression | call-expression | dot-expression | parenthesized-expression call-base-expression ::= lhs-expression | expression "!" | sub-type-level-expression "?" | lambda-declaration-expression | str-bytes-literal call-expression ::= call-base-expression "(" optional-actual-list ")" | call-base-expression "[" optional-actual-list "]" | TPRIMITIVE "(" optional-actual-list ")" dot-expression ::= expression "." ident-use | expression "." "type" | expression "." "domain" | expression "." "locale" | expression "." "bytes" "(" ")" | expression "." "bytes" "[" "]" parenthesized-expression ::= "(" tuple-component ")" | "(" tuple-component "," ")" | "(" tuple-expression-list ")" | "(" tuple-expression-list "," ")" bool-literal ::= "false" | "true" str-bytes-literal ::= STRINGLITERAL | BYTESLITERAL literal-expression ::= bool-literal | str-bytes-literal | INTLITERAL | REALLITERAL | IMAGLITERAL | CSTRINGLITERAL | "none" | "{" expression-list "}" | "{" expression-list "," "}" | "[" expression-list "]" | "[" expression-list "," "]" | "[" assoc-expression-list "]" | "[" assoc-expression-list "," "]" assoc-expression-list ::= expression "alias" expression | assoc-expression-list "," expression "alias" expression binary-operator-expression ::= expression "+" expression | expression "-" expression | expression "*" expression | expression "/" expression | expression "<<" expression | expression ">>" expression | expression "%" expression | expression "==" expression | expression "!=" expression | expression "<=" expression | expression ">=" expression | expression "<" expression | expression ">" expression | expression "&" expression | expression "|" expression | expression "^" expression | expression "&&" expression | expression "||" expression | expression "**" expression | expression "by" expression | expression "align" expression | expression "#" expression | expression "dmapped" expression unary-operator-expression ::= "+" expression | "-" expression | "--" expression | "++" expression | "!" expression | expression "!" | "~" expression reduce-expression ::= expression "reduce" expression | expression "reduce" zippered-iterator | reduce-scan-operator-expression "reduce" expression | reduce-scan-operator-expression "reduce" zippered-iterator scan-expression ::= expression "scan" expression | expression "scan" zippered-iterator | reduce-scan-operator-expression "scan" expression | reduce-scan-operator-expression "scan" zippered-iterator reduce-scan-operator-expression ::= "+" | "*" | "&&" | "||" | "&" | "|" | "^"
外部リソース
[編集]- 公式サイト:https://chapel-lang.org/
- GitHub:https://github.com/chapel-lang/chapel/
- オンラインコンパイル実行環境:Run – Attempt This Online // Chapel