演算子は、算術演算や論理演算を行うためのものです。
Zenは単項演算子と二項演算子を持ちます。Zenの演算子の多くは、2つのオペランドを持つ二項演算子です。a / b
の例を見てみると、 /
は、 a
と b
の2つのオペランドを持つ二項演算子です。a
を左辺オペランド、b
を右辺オペランドと本書では呼びます。
Zenの単項演算子は、オペランドの前に置くものと、オペランドの後ろに置くものがあります。!a
の!
はオペランドの前に置く単項演算子です。一方、a.*
の.*
はオペランドの後ろに置く単項演算子です。
ノート: Zen言語では演算子のオーバーロードはありません。
次の分類に従って、説明します。
比較演算子は2つのオペランド間で特定の関連性が成立するかどうかを確認し、その結果を論理値 (true
/ false
) で返します。この結果は主に(if
、while
、for
といった)制御フローを決定するために使用されます。それ以外にも、変数に保存される場合があります。
構文 | 対象の型 |
---|---|
a == b | - 整数型 - 浮動小数点型 - ブール型 - 型 ( type ) |
a != b | - 整数型 - 浮動小数点型 - ブール型 - 型 ( type ) |
a > b | - 整数型 - 浮動小数点型 |
a < b | - 整数型 - 浮動小数点型 |
a >= b | - 整数型 - 浮動小数点型 |
a <= b | - 整数型 - 浮動小数点型 |
a == null | - オプション型 |
左辺オペランドと右辺オペランドが等しいかどうかを確認します。左辺オペランドと右辺オペランドが等しければブール型のtrue
を返します。それ以外はfalse
を返します。
1 == 1 // true
1 == 2 // false
注意: 等価演算子は代入演算子(
=
)とは異なるものです!
左辺オペランドと右辺オペランドが等しくないかどうかを確認します。
1 != 2 // true
1 != 1 // false
この演算子は、等価演算子(==
)とは逆の結果を返します。
左辺オペランドが右辺オペランドよりも大きな値を持つかどうかを確認します。
1 > 0 // true
1 > 2 // false
左辺オペランドが右辺オペランドよりも小さな値を持つかどうかを確認します。
1 < 2 // true
1 < 0 // false
左辺オペランドが右辺オペランド以上の値を持つかどうかを確認します。
1 >= 0 // true
1 >= 1 // true
1 >= 2 // false
左辺オペランドが右辺オペランド以下の値を持つかどうかを確認します。
1 <= 2 // true
1 <= 1 // true
1 <= 0 // false
左辺オペランドがnull
かどうかを確認します。
examples/ch04-basic-syntax/src/operators_comparison.zen:72:75
test "==null" {
const value: ?u32 = null;
ok(value == null);
}
ビット演算子は整数値に対して、ビット単位の演算を行います。
シンボル | 演算子 |
---|---|
& | ビット単位論理積 (AND) |
| | ビット単位論理和 (OR) |
^ | ビット単位排他的論理和 (XOR) |
~ | ビット単位否定 (1の補数) |
<< | 論理左シフト |
>> | 論理右シフト |
以下のプログラムは全てのビット演算子の使用方法を示しています。
ノート:
0b
から始まる数値は、2進数表記です。
左辺オペランドと右辺オペランドのビット単位の論理積を行います。
0b1010 & 0b1100 == 0b1000
左辺オペランドと右辺オペランドのビット単位の論理和を行います。
0b1010 | 0b1100 == 0b1110
左辺オペランドと右辺オペランドのビット単位の排他的論理和を行います。
0b1010 ^ 0b1100 == 0b0110
右辺オペランドの論理否定を行います。
~@to(u4, 0b1010) == @to(u4, 0b0101)
左辺オペランドを右辺オペランドで指定したビット数だけ左シフトします。右辺オペランドは、左辺オペランドのビット幅のlog2でなければなりません。例えば、左辺オペランドがu32
であれば、右辺オペランドはu5
です。
@to(u32, 0b0001) << @to(u5, 3) == @to(u32, 0b1000)
左辺オペランドを右辺オペランドで指定したビット数だけ右シフトします。右辺オペランドは、左辺オペランドのビット幅のlog2でなければなりません。例えば、左辺オペランドがu32
であれば、右辺オペランドはu5
です。
@to(u32, 0b1000) >> @to(u5, 3) == @to(u32, 0b0001)
符号付き整数へのビット演算は避けるべきです。なぜなら、符号付き整数の符号ビット (最上位ビット) は特別な意味をもつため、符号付き整数に対してビット演算子を適用すると、結果が予期せぬ値になる可能性が高いためです。
特にシフト演算では注意が必要です。
ビット演算子は主に、マスキングで利用されます。マスキングとは、任意のビットを変数から抽出することです。マスキングに使われるオペランド(定数または変数)は マスク(mask) と呼ばれます。
マスキングを行う方法は多岐にわたります。以下のコードは変数のビットパターンを抽出するためにマスクを使用します。
// マスク`mask`とマスクを適用する対象の整数`x`
const mask: u32 = 0x0000_ffff;
var x:u32 = 0x1234_5678;
新しい変数の残りの部分は0で埋め、与えられたビットパターンの一部を新しい変数にコピーします。ビット単位論理積 (&
) を使用します。
// `u32`の上位16ビットは`0`で埋め、下位16ビットをコピー
(x & mask) == 0x0000_5678
新しい変数の残りの部分に1を埋め、与えられたビットパターンの一部を新しい変数にコピーします。ビット単位論理和 (|
) を使用します。
// `u32`の上位16ビットをコピー、下位16ビットは`1`で埋める
(x | mask) == 0x1234_ffff
元のビットパターンの残りの部分を新しい変数内で反転させながら、与えられたビットパターンの一部を新しい変数にコピーします。ビット単位排他的論理和 (^
) を使用します。
// `u32`の上位16ビットをコピー、下位16ビットは反転させる
(x ^ mask) == 0x1234_a987
算術演算子は、左辺オペランドに右辺オペランドを算術演算した結果を返します。交換法則を満たす演算子もあります。例えば加算や乗算は交換法則を満たしますが、減算や除算は満たしません。
構文 | 対象の型 |
---|---|
a + b | - 整数型 - 浮動小数点型 |
a - b | - 整数型 - 浮動小数点型 |
a * b | - 整数型 - 浮動小数点型 |
a / b | - 整数型 - 浮動小数点型 |
a % b | - 整数型 - 浮動小数点型 |
-a | - 整数型 |
a +% b | - 整数型 |
a -% b | - 整数型 |
a *% b | - 整数型 |
-%a | - 整数型 |
a ++ b | - 配列 |
a ** b | - 配列 |
左辺オペランドと右辺オペランドを加算します。
2 + 5 == 7 // true
2 + 5 == 8 // false
左辺オペランドから右辺オペランドを減算します。
5 - 2 == 3 // true
5 - 2 == 4 // false
左辺オペランドと右辺オペランドを乗算します。
2 * 5 == 10 // true
2 * 5 == 12 // false
除算演算子 (/
) は左辺オペランドを右辺オペランドで除算します。除算のオペランドが両方とも整数型の場合、整数値を返し、余りは破棄されます。 (剰余演算子を用いれば、余りを取得できます。) オペランドのいずれか一つでも浮動小数点型の場合、結果は小数の近似値になります。
10 / 5 == 2 // true
10 / 5 == 3 // false
剰余演算子 (%
) は整数型のオペランドにのみ適用できます。これは左辺オペランドを右辺オペランドで除算した時の余りを計算します。
10 % 3 == 1 // true
10 % 3 == 0 // false
単項マイナス演算 (-a
) はa
の符号を逆転します。
-1 == 0 - 1
左辺オペランドと右辺オペランドを加算します。オーバーフロー発生時、2の補数表現のラップアラウンド動作を保証します。
@to(u32, std.math.maxInt(u32)) +% 1 == 0
左辺オペランドから右辺オペランドを減算します。オーバーフロー発生時、2の補数表現のラップアラウンド動作を保証します。
@to(u32, 0) -% 1 == std.math.maxInt(u32)
左辺オペランドから右辺オペランドを乗算します。オーバーフロー発生時、2の補数表現のラップアラウンド動作を保証します。
@to(u8, 200) *% 2 == 144
右辺オペランドの符号を逆転します。オーバーフロー発生時、2の補数表現のラップアラウンド動作を保証します。
-%@to(i32, std.math.minInt(i32)) == std.math.minInt(i32)
配列の連結 (a ++ b
) は配列のオペランドにのみ適用できます。これは左辺の配列と右辺の配列を連結します。
examples/ch04-basic-syntax/src/operators_arithmetic.zen:54:61
test "array concatenation" {
const array1 = [_]u32{ 1, 2 };
const array2 = [_]u32{ 3, 4 };
const together = array1 ++ array2;
equalSlices(u32, &together, &[_]u32{ 1, 2, 3, 4 });
}
配列の積 (a ** b
) は、左辺の配列のオペランドa
がb
回連続する配列を生成します。
examples/ch04-basic-syntax/src/operators_arithmetic.zen:63:66
test "array product" {
const pattern = "ab" ** 3;
equalSlices(u8, pattern, "ababab");
}
構文 | 対象の型 |
---|---|
. | - 構造体 - 列挙型 - 共用体 - インタフェース |
&a | - 全ての型 |
a.* | - ポインタ |
構造体インスタンス、列挙型インスタンスまたはインターフェースインスタンスのメンバにアクセスします。アクセスされたインスタンスのメンバの値を返します。
examples/ch04-basic-syntax/src/operators_access.zen:4:11
const Int32 = struct {
value: i32,
};
test "." {
var x = Int32{ .value = 42 };
ok(x.value == 42);
}
与えられた式のアドレスを返します。ここで、アドレス演算子に与える式は、変数など、アドレスが割り当てられているものでなければなりません。
const x: u32 = 1234;
const ptr = &x;
ポインタを間接参照します。
ptr.* == 1234
右辺オペランドの値を、左辺オペランドの変数に代入します。
いくつかの算術演算は複合代入演算子を持ちます。
a += b // equal to: a = a + b
a +%= b // equal to: a = a +% b
a -= b // equal to: a = a - b
a -%= b // equal to: a = a -% b
a *= b // equal to: a = a * b
a *%= b // equal to: a = a *% b
a /= b // equal to: a = a / b
a %= b // equal to: a = a % b
a &= b // equal to: a = a & b
a |= b // equal to: a = a | b
a ^= b // equal to: a = a ^ b
a <<= b // equal to: a = a << b
a >>= b // equal to: a = a >> b
複合代入の重要な特徴は、左辺オペランド (a
) が1度しか評価されないことです。
次のコードにおいて、ptr.* = ptr.* + 2;
とptr.* += 2;
とは、少なくともデバッグビルドでは異なるアセンブリを出力します。
var x: u64 = 0;
const ptr = &mut x;
ptr.* = ptr.* + 2;
ptr.* += 2;
ptr.* = ptr.* + 2;
は、次のようにptr.*
が2回評価されています (rdi
とrax
とにptr
のアドレスがロードされる) 。
mov rdi, qword ptr [rbp - 24]
mov rax, qword ptr [rbp - 24]
mov rax, qword ptr [rax]
add rax, 2
mov qword ptr [rdi], rax
対して、ptr.* += 2;
は、ptr.*
が1回しか評価されません。
mov rax, qword ptr [rbp - 24]
mov rdi, qword ptr [rax]
add rdi, 2
mov qword ptr [rax], rdi
構文 | 対象の型 |
---|---|
a and b | - ブール型 |
a or b | - ブール型 |
!a | - ブール型 |
両方のオペランドが false
でなければ true
返す、論理演算の AND を実行します。
false and false // `false`が返されます.
false and true // `false`が返されます.
true and false // `false`が返されます.
true and true // `true`が返されます.
いずれかのオペランドが true
であれば true
を返す、論理演算の OR を実行します。
false or false // `false`が返されます.
false or true // `true`が返されます.
true or false // `true`が返されます.
true or true // `true`が返されます.
オペランドの論理否定を返します。論理否定演算子はオペランドが true
かどうかを確認し、もしそうなら false
を返します。それ以外は true
を返します:
!true // `false`が返されます.
!false // `true`が返されます.
!(1 == 1) // `false`が返されます.
可能な時は条件式(if
/ while
/ ...)の評価を省略します。例えば、a and b
を演算する場合、a
がfalse
と評価された時点で、式全体がfalse
になることが確定します。そのような場合に、b
の評価は省略されます。
examples/ch04-basic-syntax/src/shortcircuit_evaluation.zen:2:10
pub fn main() void {
var a: i8 = 5;
var b: i8 = -3;
// ここでは `a == 42` は `false` なので、
// `b == -3` の式は評価されません。
if (a == 42 and b == -3) {
std.debug.warn("ここはプリントされない!\n", .{});
}
}
and
と or
に共通する重要な特性は以下の通りです。
これは次のことを意味します。
true
となる場合、or
の右辺オペランドの値は評価されません。false
となる場合、and
の右辺オペランドは評価されません。
構文 | 対象の型 |
---|---|
a orelse b | - オプション型 |
a .? | - オプション型 |
a catch b | - エラー共用体 |
a || b | - エラー型 |
左辺オペランドが null
ならば右辺オペランドを返します。null
でなければ、左辺オペランドを返します。
examples/ch04-basic-syntax/src/operators_others.zen:4:8
test "orelse" {
const value: ?u32 = null;
const unwrapped = value orelse 1234;
ok(unwrapped == 1234);
}
a.?
は a orelse unreachable
と同じ意味です。
examples/ch04-basic-syntax/src/operators_others.zen:10:13
test ".?" {
const value: ?u32 = 5678;
ok(value.? == 5678);
}
左辺オペランドがエラーならば右辺オペランドを返します。
a catch |err| b
の時、 err
はエラーのことで式 b
のスコープ内となります。
examples/ch04-basic-syntax/src/operators_others.zen:15:19
test "catch" {
const value: anyerror!u32 = error.Broken;
const unwrapped = value catch 1234;
ok(unwrapped == 1234);
}
エラーをマージする演算子です。
examples/ch04-basic-syntax/src/operators_others.zen:21:35
const A = error{One};
const B = error{Two};
const C = A || B;
fn handlerMergedError(b: bool) C!void {
return switch (b) {
true => error.One,
false => error.Two,
};
}
test "||" {
std.testing.err(C.One, handlerMergedError(true));
std.testing.err(C.Two, handlerMergedError(false));
}
演算子の優先順位は下表の通りです。上に行くほど優先度が高くなります。
演算子 | 説明 |
---|---|
x() x[] x.y | 関数呼び出し、要素アクセス、メンバアクセス |
a!b | エラー共用体 |
!x -x -%x ~x &x ?x | 前置の単項演算 |
x{} x.* x.? | 初期化、デリファレンス、アンラップ |
* / % ** *% || | 乗算、除算、エラーの合成 |
+ - ++ +% -% | 加減算 |
<< >> | シフト |
& ^ | orelse catch | ビット演算、null判定、エラー判定 |
== != < > <= >= | 比較演算 |
and or | 論理演算 |
= *= /= %= += -= <<= >>= &= ^= |= | 代入 |
☰ 人の生きた証は永遠に残るよう ☰
Copyright © 2018-2020 connectFree Corporation. All rights reserved. | 特定商取引法に基づく表示
Zen, the Zen three-circles logo and The Zen Programming Language are trademarks of connectFree corporation in Japan and other countries.