Chapel

出典: フリー教科書『ウィキブックス(Wikibooks)』
Wikipedia
Wikipedia
ウィキペディア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では代入は式ではなく文
Cファミリーでは代入は式なので
int a, b, c;
a = b = c = 0;

のように書くと

int a, b, c;
a = ( b = ( c = 0 ) );

という意味になります(代入演算子が右結合なのはこれが理由です)。

しかし、Chapelでは代入は演算子ではなく文なので連結することも、条件式に書くことも出来ません。

このため、結合方向を気にする必要はなくなりました(文は式と結合しないので)。

同様な方針を取った言語としては、最近では Go、古くは FortranAlgolPascalがあります。


定数[編集]

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のキーワード一覧
キーワード 分類 説明
_ 特殊文字
  1. タプルの要素
  2. 識別子 as _
  3. 数値リテラルの可読性のための区切り
align 型修飾子 アライメントを指定する
  1. 範囲式 align 式
  2. ドメイン式 align 式
as 型変換 値を別の型に変換する
  1. 式 as 変数種 識別子
  2. 式 as 識別子
  3. 識別子 as 識別子
  4. 識別子 as _
atomic 並行処理 アトミック操作を行う
  1. atomic 型
begin タスク begin [ インテント ] 文
bool データ型 真理値を表す型
borrowed クラス型 借用された参照を表す型
break 制御構造 繰り返しループから脱出する
  1. break [ ラベル ] ;
by 制御構造 ストライドを指定する
  1. 範囲リテラル by 整数リテラル
  2. ドメイン式 by 式
  3. 範囲式 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:分類と説明の充実]

予約済キーワード[編集]

以下の識別子は、将来の使用のために予約されているキーワードです。

lambda pragma primitive

データ型[編集]

プリミティブ型[編集]

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とtrueを2つのリテラル値としてサポートします:

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
  • 上記のコードでは、真理値型の変数 ab を宣言し、それぞれに truefalse の値を代入しています。
  • 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)
  • 上記のコードでは、整数型の変数 abcd を宣言し、それぞれに整数値を代入しています。
  • writeln 関数を使用して、それぞれの変数の値を出力しています。結果として、a の値は 10、b の値は -5、c の値は 32767、d の値は 255 であることが示されます。
  • writeln 関数を使用して、それぞれの変数の型情報も出力しています。結果として、変数 ab の型はデフォルトの整数型 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
  • まず、abcという名前の3つの変数を宣言しています。aは64ビットの実数型、bは32ビットの実数型、cはアーキテクチャによって決まるデフォルトの実数型です。
  • 次に、各変数の型情報を出力しています。結果として、aの型はreal(64)bの型はreal(32)cの型はデフォルトのアーキテクチャに基づくreal(64)であることが示されます。
  • その後、各変数に値を代入しています。aには小数表現の3.14bには指数表現の2.5e-12cには16進数表現の0x1.7p-1が代入されています。
  • 最後に、各変数の値を出力しています。結果として、aの値は3.14bの値は2.5e-12cの値は0.71875であることが示されます。
  • これにより、異なるビット幅の実数型を使用して値を表現し、それぞれのビット幅に基づく演算や計算を行うことができることがわかります。また、Chapelはさまざまな数値表現方法をサポートしており、小数表現、指数表現、16進数表現などを使用して実数値を代入することができます。

10進数形式では、整数リテラルと区別するために.を使用する必要があります。10.は、Chapelでは有効な浮動小数点リテラル値ではないことに注意してください。 これは、整数値に対してメソッド呼び出しを行う際の構文の曖昧さのためです。このため、代わりに10.0が使用されなければならない。 また、.7070.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つの異なる虚数型の変数abcを宣言しています。aは64ビットの実数型の虚数を表し、bは32ビットの実数型の虚数を表します。cはアーキテクチャに自然な虚数型を表します。
  • また、writeln文を使用して各変数のデータ型を表示しています。
  • 次に、虚数型の変数に値を代入する例です。aには虚数リテラル3.0iを代入しています。bには2.5imagキーワードを使用して強制的に虚数型に変換して代入しています。一方、cには実数型の値1.7を代入しようとするとエラーが発生します。
  • 最後の部分では、各変数の値を表示しています。abはそれぞれ代入された値がそのまま表示されますが、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はこれらのメモリ管理戦略の一つです。

メモリ管理戦略[編集]

クラスのタイプには、メモリ管理戦略が含まれています。現在サポートされている戦略は、ownedsharedunmanagedborrowedの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}
配列の添字の開始は 0 か? 1 か?
Chapelの配列の添字は、宣言時に明示したりドメインで指定できるので、どちらにすることも出来ます。また0/1以外の数(例えば 2; エラトステネスの篩の実装例では実際に2にしています)。

ですが、配列リテラルのように配列のインデックスの開始値を明示しない配列もあります。このうような場合は、バージョン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

条件式、並列イテレータ式、シリアルイテレータ式
, 式リスト
註)「 (右)」と特記なき限り左結合
インクリメント・デクリメント演算子はありません
C言語ファミリー言語とは異なり、Chapelには前置/後置インクリメント/デクリメント演算子はありません。

n += 1およびn += 1はあります。

Rubyも、インクリメント・デクリメント演算子はありませんが、Rubyの場合はNumericはイミュータブルなのが理由で、若干事情が異なります。


制御構造[編集]

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 未満である間、次の処理を繰り返します。
    1. 変数 i の値を出力します。
    2. 変数 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 を使って定義します。


関数宣言キーワード
Chapel-1.4(2011年10月)より前では、プロシージャとイテレータの両方を定義するためにdefを使用していました。

コンパイラは、関数本体にyieldステートメントが存在するかどうかを検査することで、プロシージャかイテレータかを判断していました。

1.4の変更により、これらの2つのケースを明確に区別するために、キーワードprociterを導入しました。 この変更により、誤りを防ぎ、それぞれのケースを明確に区別するセマンティクスをサポートし、インターフェースの指定をより良くサポートすることができるようになりました。


イテレータの定義と実行[編集]

// イテレータの定義
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>は演算子を表すシンボル(例: +, -, *, /)を指定します。lhsrhsは同じ型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 オブジェクトの xy の値をそれぞれ足し合わせた新しい Point オブジェクトを返します。同様に、- 演算子メソッドでは、2つの Point オブジェクトの xy の値をそれぞれ引き算した新しい 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プロシージャは、itemitemsの次の位置に追加し、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]が特徴的で、配列を渡すと、ドメイン(添字範囲)も暗黙に渡ります。
これは再帰でも応用されquicksort(ary[dom.low..j]);のように部分配列を意味どおりに表現しています。

最大公約数と最小公倍数[編集]

最大公約数と最小公倍数を、若干 Chapel らしく書いてみました。

最大公約数と最小公倍数
// 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 ::= "+"
                                  | "*"
                                  | "&&"
                                  | "||"
                                  | "&"
                                  | "|"
                                  | "^"

外部リソース[編集]