コンテンツにスキップ

English

このページでは、Graphcal のすべての式形式、すなわち演算子、優先順位、条件式について説明します。

演算子の優先順位

優先順位の低いものから高いものの順に示します。

優先順位 演算子 説明 結合性
0 -> 単位変換 なし
1 if/else 条件式
2 \|\| 論理 OR
3 && 論理 AND
4 == != < > <= >= 比較 連鎖不可
5 + - 加算、減算
6 * / % 乗算、除算、剰余
7 - ! 単項の符号反転、論理 NOT 前置
8 ^ べき乗
9 . [...] フィールドアクセス、インデックスアクセス

括弧 () を使って優先順位を上書きできます。-> は最も低いレベルで結合するため、末尾形式の expr -> unit はその左側のすべてを包み込みます。

算術演算子

演算子 量に対する振る舞い Int に対する振る舞い 次元の規則
a + b 加算 加算 次元が一致しなければならない
a - b 減算 減算 次元が一致しなければならない
a * b 乗算 乗算 次元を乗算
a / b 除算 整数除算 次元を除算
a % b 非対応 剰余 Int オペランドのみ
a ^ n べき乗 整数のべき乗(非負の指数) 次元をべき乗
-a 符号反転 符号反転 次元を保持

%Int % Int に対してのみ定義されます。Graphcal は実数の量に IEEE 浮動小数点の剰余を暗黙に適用しません。結果は切り捨て整数剰余の意味論に従い、除数がゼロの場合は評価エラーになります。

複素数の算術

複素数の量は Complex<D> 型を持ち、D は実部と虚部が共有する次元です。加算や減算のために実数の量が暗黙に昇格されることはありません。明示的に to_complex(x) と書いてください。

結果 要件
z1 + z2, z1 - z2 Complex<D> 両オペランドが Complex<D> であること
z1 * z2 Complex<D1 * D2> 複素数のオペランドは異なる次元を持ってよい
z1 / z2 Complex<D1 / D2> 除数が非ゼロであること
q * z, z * q Complex<Dq * Dz> q は実数の量
z / q Complex<Dz / Dq> q は非ゼロの実数の量
q / z Complex<Dq / Dz> z が非ゼロであること
-z Complex<D>

例を示します。

node displacement: Complex<Length> = complex(3.0 m, 4.0 m);
node gain: Complex<Dimensionless> = complex(0.5, -0.25);
node transformed: Complex<Length> = @displacement * @gain;
node with_offset: Complex<Length> = @displacement + to_complex(1.0 m);

%^ は複素数のオペランドに対しては定義されていません。==!= は実部と虚部を正確に比較します。複素数の値には順序がないため、<><=>= は拒否されます。成分や極形式の操作には reimabsphaseconj を使ってください。

べき乗の指数

量に対するすべての指数は無次元でなければなりません。底が Dimensionless であれば、指数は任意の実行時 Dimensionless 量でかまいません。@ratio ^ @runtime_exponent は有効です。

底が物理的な次元を持つ場合、その指数は代わりに正確な静的構文、すなわち整数(2-20)または括弧で囲まれた有理数((3/2)(-1/3))を使わなければなりません。結果の次元はその正確な有理数だけべき乗されます。次元付きの底に対しては小数/指数表記の構文は受け付けられません。0.25 ではなく (1/4)2.0 ではなく 2 と書いてください(D020)。

正確な有理数のメタデータは実行時にも使われます。例えば、(-8.0) ^ (1/3)-2.0 に評価されます。負の底に対する偶数分母の冪根は実数値ではなく、評価エラーを生成します。次元や単位の宣言ではゼロ乗が省略されますが、値レベルの指数ゼロは有効です。

Int ^ Int は非負の正確な整数の指数を必要とします。定数畳み込みによってそのような指数になる右結合の式も受け付けられます。2 ^ 3 ^ 22 ^ (3 ^ 2) としてパースされ、2 ^ 9 として評価されます。

有限な量

浮動小数点リテラルは有限でなければなりません。NaNinf を生み出すことになる 実数または複素数の演算は、実行時の値を生成する代わりにエラーとして表面化します。 非ゼロを保存しない実数演算、すなわち乗算、除算、べき乗、要素ごとの積は、 非ゼロの入力が完全にゼロにアンダーフローした場合にもエラーになります。 表現可能な非正規化数の結果は引き続き有効であり、打ち消しや明示的なゼロの オペランドによって生じたゼロも引き続き有効です。

比較演算子

演算子 説明 オペランドの要件
== 等しい 同じ型と次元
!= 等しくない 同じ型と次元
< より小さい 同じ型と次元
> より大きい 同じ型と次元
<= 以下 同じ型と次元
>= 以上 同じ型と次元

すべての比較演算子は、インデックスなしのオペランドに対して Bool を返します。複素数の量は、次元が一致する場合に正確な ==!= をサポートしますが、順序比較はサポートしません。インデックスキーも同様に、同じ軸上でのみ ==!= をサポートし、順序を持ちません。順序が必要な場合は、取り出した中身(coord(t)to_int(i))を比較してください。構造体とユニオンの等値比較は再帰的です。インデックス付きのフィールドや座標インデックスキー(その軸と位置の両方が同一性の一部です)を含め、すべてのフィールドが等しくなければなりません。

Datetime の算術は「時点対期間」の規則に従います。Datetime + TimeTime + DatetimeDatetime - TimeDatetime を生成し、Datetime - DatetimeTime の期間を生成します。Time - DatetimeDatetime + Datetime はエラーです。

比較演算子はブロードキャストしません。すべてのオペランドはインデックスなしでなければなりません。インデックス付きの値を ==!=<><=>= に渡すとコンパイルエラー(D019)になります。要素ごとの比較は for を使って明示的に書き、本体の中で各コレクションをインデックスしてください。

index Case = { A, B };
node values: Length[Case] = {
    Case#A: 1.0 m,
    Case#B: 2.0 m,
};
node below_limit: Bool[Case] = for case: Case {
    @values[case] < 3.0 m
};

これにより、コレクションの形状を黙って解決するのではなく、選択された軸と要素の対応付けがソース上で目に見えるようになります。

論理演算子

演算子 説明
a \|\| b 論理 OR
a && b 論理 AND
!a 論理 NOT

オペランドは Bool でなければなりません。

&&|| は常に両方のオペランドを評価します(短絡評価はありません)。 リアクティブ計算グラフでは、制御フローにかかわらずすべての部分式が有効であるべきなので、 Graphcal はエラーを短絡評価の裏に隠すのではなく積極的に表面化させます。 条件付きの評価が必要な場合は if cond { a } else { b } を使ってください。

単位変換(->

値を同じ次元の別の単位に変換します。

node alt_m: Length = @altitude -> m;
node time_h: Time = @duration -> h;
node displacement_cm: Complex<Length> = @displacement -> cm;

Complex<D> の場合、変換先は両方の成分をスケールし、表示メタデータとなります。保存されている SI の成分と型は変わりません。それ以外の点では、変換先は単位のスコープ規則に従います。ローカル、選択的にインポートされた、あるいはプレリュードの単位には修飾なしの名前を、エイリアス付きでインポートされたモジュールの単位には alias::unit を使います(単位のスコープを参照)。

-> は連鎖しません。1 つの式が持てる変換先は最大 1 つです。@x -> km -> m はパースエラーであり、括弧付きの (@x -> km) -> m は次元検査器によって拒否されます(D012)。

変換は表示位置、すなわちその変換先が宣言の描画される値に到達できる場所(宣言のトップレベル、if/match の分岐、コンストラクターのフィールド、マップの要素、for 内包表記の本体、scan/unfold の init)に置かなければなりません。それ以外の位置(算術のオペランド、関数の引数、比較、条件、assert の本体)にある変換は効果を持たず、拒否されます(D013)。

ファントム型の変更(明示的な再構築)

ファントム型のキャスト演算子はありません。ファントム型パラメーターを変更するには、新しいインスタンスを構築して各フィールドを明示的に代入します。

node pos_body: Vec3<Length, Body> = Vec3<Length, Body>(
    x: @pos_eci.x,
    y: @pos_eci.y,
    z: @pos_eci.z,
);

この冗長さは意図的なものです。ラベルの付け替え(例えば座標系の変更)は、フィールドごとの意図的な行為であり、黙った再解釈ではありません。

フィールドアクセス(.

レコード状の単一コンストラクター代数的値のフィールドにアクセスします。

node dv: Velocity = @transfer.total_dv;

インデックスアクセス([...]

インデックス付きの値の要素にアクセスします。

node first: Velocity = @delta_v[Maneuver#Departure];

角括弧には少なくとも 1 つの引数を含めなければなりません。expr[] は恒等操作ではなく無効です。各引数はその軸に対するインデックスキーであり、修飾付きラベル(定数キー)、ループ変数、または argmax の結果のような任意の計算された Key<I> 値です。

if/else 式

条件式です。

node clamped: Int = if @a > @seven { @seven } else { @a };

両方の分岐は同じ型と次元を持たなければなりません。else 分岐は必須です。

DAG 呼び出し式(@dag(args)::out

任意の DAG モジュール(ファイルルートまたは dag ブロック)は式として呼び出すことができ、構文上の各呼び出し箇所で新しい実行時サブグラフを生成します。

node doubled: Length = @scale(factor: 2.0, v: @src)::result;

. の後の射影される出力は必須です。引数は周囲のスコープで評価されるため、外側の for 内包表記のループ変数やその他のローカルな束縛子を参照できます。

@ の直後に来るものは、ローカルの DAG またはインポートされたモジュールエイリアスです。module としてインポートされたファイルルートまたはインライン DAG のターゲットは @module(args)::out として直接呼び出され、@module.dag(args)::out は子 DAG へ下降します。射影が必須であることにより、この式はグラフ参照になります。射影は明示的にエクスポートされたノードまたはパラメーター入力ポートのいずれかを指定でき、パラメーターを射影するとその実効的な束縛値/デフォルト値が得られます。

DAG 呼び出しは匿名の include に対する構文糖衣であり、コンパイル時の関数呼び出しではありません。そのため、選択された出力がたまたま定数のみに依存する場合であっても、const node の本体とすべての定義域の境界では拒否されます。

完全な意味論についてはマルチファイル: DAG 呼び出し式を参照してください。

数値リテラル

形式
整数 42, 1_000 Int
浮動小数点 3.14, 1.0e-3 Dimensionless
単位付き浮動小数点 200.0 km, 9.8 m/s^2 単位の次元
真偽値 true, false Bool