コンテンツへスキップ

演算子

Crystalは、1つ、2つ、または3つのオペランドを持つ多くの演算子をサポートしています。

演算子式は実際にはメソッド呼び出しとして解析されます。たとえば、a + bは意味的にa.+(b)と同等であり、引数baに対するメソッド+の呼び出しです。

ただし、演算子の構文に関するいくつかの特別な規則があります。

  • 受信者とメソッド名(つまり、演算子)の間に通常配置されるドット(.)は省略できます。
  • 演算子呼び出しの連結されたシーケンスは、コンパイラによって再構築され、演算子の優先順位が実装されます。演算子の優先順位を強制することで、1 * 2 + 3 * 4のような式が(1 * 2) + (2 * 3)として解析され、通常の数学規則が尊重されます。
  • 通常のメソッド名は、文字またはアンダースコアで始める必要がありますが、演算子は特殊文字のみで構成されます。文字またはアンダースコアで始まらないメソッドは、演算子メソッドです。
  • 使用可能な演算子はコンパイラでホワイトリストに登録されています(下記の演算子のリストを参照)。これにより、シンボルのみのメソッド名が許可され、優先順位規則を含む演算子として扱われます。

演算子は通常のメソッドと同じように実装され、標準ライブラリは、たとえば数学式に対して多くの実装を提供しています。

演算子メソッドの定義

ほとんどの演算子は、通常のメソッドとして実装できます。

演算子には任意の意味を割り当てることができますが、わかりにくいコードや予期しない動作をするコードを避けるために、一般的な演算子の意味と同様の意味にとどめることをお勧めします。

いくつかの演算子はコンパイラによって直接定義されており、ユーザーコードで再定義することはできません。これの例としては、反転演算子!、代入演算子=複合代入演算子||=など)、および範囲演算子などがあります。メソッドを再定義できるかどうかは、以下の演算子表の「オーバーロード可能」列で示されています。

単項演算子

単項演算子はプレフィックス記法で記述され、オペランドは1つだけです。したがって、メソッドの実装は引数を何も受け取らず、selfに対してのみ動作します。

次の例は、単項演算子メソッド-を使用してベクトル反転を行う2次元ベクトルとしてVector2型を示しています。

struct Vector2
  getter x, y

  def initialize(@x : Int32, @y : Int32)
  end

  # Unary operator. Returns the inverted vector to `self`.
  def - : self
    Vector2.new(-x, -y)
  end
end

v1 = Vector2.new(1, 2)
-v1 # => Vector2(@x=-1, @y=-2)

二項演算子

二項演算子は2つのオペランドを持ちます。したがって、メソッドの実装は、2番目のオペランドを表す正確に1つの引数を受け取ります。最初のオペランドは受信者selfです。

次の例は、二項演算子メソッド+を使用してベクトル加算を行う2次元ベクトルとしてVector2型を示しています。

struct Vector2
  getter x, y

  def initialize(@x : Int32, @y : Int32)
  end

  # Binary operator. Returns *other* added to `self`.
  def +(other : self) : self
    Vector2.new(x + other.x, y + other.y)
  end
end

v1 = Vector2.new(1, 2)
v2 = Vector2.new(3, 4)
v1 + v2 # => Vector2(@x=4, @y=6)

慣例により、二項演算子の戻り値の型は最初のオペランド(受信者)の型である必要があります。そうすることで、typeof(a <op> b) == typeof(a)となります。そうでないと、代入演算子(a <op>= b)によってaの型が意図せず変更される可能性があります。ただし、合理的な例外もあります。たとえば、標準ライブラリでは、整数型の浮動小数点除算演算子/は常にFloat64を返します。これは、商が整数の値範囲に制限されない必要があるためです。

三項演算子

条件演算子(? :は唯一の三項演算子です。これはメソッドとして解析されず、その意味を変更することはできません。コンパイラはこれをif式に変換します。

演算子の優先順位

このリストは優先順位順にソートされているため、上のエントリは下のエントリよりも強くバインドされます。

カテゴリ 演算子
インデックスアクセサ [], []?
単項 +, &+, -, &-, !, ~
指数 **, &**
乗算 *, &*, /, //, %
加算 +, &+, -, &-
シフト <<, >>
二項AND &
二項OR/XOR |,^
等価性と包含関係 ==, !=, =~, !~, ===
比較 <, <=, >, >=, <=>
論理AND &&
論理OR ||
範囲 .., ...
条件演算子 ?:
代入 =, []=, +=, &+=, -=, &-=, *=, &*=, /=, //=, %=, |=, &=,^=,**=,<<=,>>=, ||=, &&=
スプラット *, **

演算子のリスト

算術演算子

単項

演算子 説明 オーバーロード可能 結合性
+ +1 はい
&+ ラップアラウンド正 &+1 はい
- -1 はい
&- ラップアラウンド負 &-1 はい

乗算

演算子 説明 オーバーロード可能 結合性
** べき乗 1 ** 2 はい
&** ラップアラウンドべき乗 1 &** 2 はい
* 乗算 1 * 2 はい
&* ラップアラウンド乗算 1 &* 2 はい
/ 除算 1 / 2 はい
// 床除算 1 // 2 はい
% 剰余 1 % 2 はい

加算

演算子 説明 オーバーロード可能 結合性
+ 加算 1 + 2 はい
&+ ラップアラウンド加算 1 &+ 2 はい
- 減算 1 - 2 はい
&- ラップアラウンド減算 1 &- 2 はい

その他の単項演算子

演算子 説明 オーバーロード可能 結合性
! 反転 !true いいえ
~ 二の補数 ~1 はい

シフト

演算子 説明 オーバーロード可能 結合性
<< 左シフト、追加 1 << 2STDOUT << "foo" はい
>> 右シフト 1 >> 2 はい

二項

演算子 説明 オーバーロード可能 結合性
& 二項AND 1 & 2 はい
| 二項OR 1 | 2 はい
^ 二項XOR 1 ^ 2 はい

関係演算子

関係演算子は、2つの値間の関係をテストします。これには、等価性不等号包含関係が含まれます。

等価性

等価演算子 == は、オペランドの値が等しいかどうかを検査します。

非等価演算子 != は、反転を表すためのショートカットです。a != b!(a == b) と等価であるとみなされます。

非等価演算子を実装する型は、この規則に従う必要があります。不等式は等式よりも高速に証明できることが多いため、パフォーマンス上の理由から特別な実装が有用な場合があります。

両方の演算子は可換であることが期待されます。つまり、a == bb == a と同値です。これはコンパイラによって強制されるものではなく、実装する型自身で注意する必要があります。

演算子 説明 オーバーロード可能 結合性
== 等しい 1 == 2 はい
!= 等しくない 1 != 2 はい

情報

標準ライブラリは、演算子ではない別の等価性テストとしてReference#same?を定義しています。これは、2つの値がメモリの同じ場所を参照しているかどうかを判断する参照同一性を検査します。

不等式

不等式演算子は、値間の順序を記述します。

3項比較演算子 <=>スペースシップ演算子とも呼ばれる)は、戻り値の符号によって表される2つの要素間の順序を表します。

演算子 説明 オーバーロード可能 結合性
< 小さい 1 < 2 はい
<= 小さいか等しい 1 <= 2 はい
> 大きい 1 > 2 はい
>= 大きいか等しい 1 >= 2 はい
<=> 3項比較 1 <=> 2 はい

情報

標準ライブラリは、Comparableモジュールを定義しており、これは3項比較演算子から他のすべて的不等式演算子と等価演算子を導出します。

包含

パターンマッチ演算子 =~ は、最初のオペランドの値がパターンマッチングによって2番目のオペランドの値と一致するかどうかを検査します。

パターン不一致演算子 !~ は、その逆を表します。

ケース包含演算子 ===ケース等価演算子またはトリプルイコールとも不正確に呼ばれる)は、右辺のオペランドが左辺の演算子によって記述される集合のメンバーかどうかを検査します。正確な解釈は、関連するデータ型によって異なります。

コンパイラは、case ... when条件にこの演算子を挿入します。

逆演算子はありません。

演算子 説明 オーバーロード可能 結合性
=~ パターンマッチ "foo" =~ /fo/ はい
!~ パターン不一致 "foo" !~ /fo/ はい
=== ケース包含 /foo/ === "foo" はい

関係演算子の連結

関係演算子==!====<><=>=は連結でき、複合式として解釈されます。たとえば、a <= b <= ca <= b && b <= cとして扱われます。異なる演算子を混合することも可能です。a >= b <= c > da >= b && b <= c && c > dと同等です。

予期せぬバインディング動作を避けるために、同じ優先順位クラスの演算子のみを組み合わせることをお勧めします。たとえば、a == b <= ca == b && b <= cと同等ですが、a <= b == ca <= (b == c)と同等です。

論理

演算子 説明 オーバーロード可能 結合性
&& 論理AND true && false いいえ
|| 論理OR true || false いいえ

範囲

範囲演算子は、範囲リテラルで使用されます。

演算子 説明 オーバーロード可能
.. 包含範囲 1..10 いいえ
... 排他的範囲 1...10 いいえ

スプラット

スプラット演算子は、メソッド引数でタプルをデストラクトする場合にのみ使用できます。詳細はスプラットとタプルを参照してください。

演算子 説明 オーバーロード可能
* スプラット *foo いいえ
** ダブルスプラット **foo いいえ

条件式

条件演算子(? :)は、コンパイラによって内部的にif式に書き換えられます。

演算子 説明 オーバーロード可能 結合性
? : 条件式 a == b ? c : d いいえ

代入

代入演算子=は、2番目のオペランドの値を最初のオペランドに代入します。最初のオペランドは変数(この場合、演算子は再定義できません)または呼び出し(この場合、演算子は再定義できます)のいずれかです。詳細は代入を参照してください。

演算子 説明 オーバーロード可能 結合性
= 変数への代入 a = 1 いいえ
= 呼び出しへの代入 a.b = 1 はい
[]= インデックスへの代入 a[0] = 1 はい

複合代入

代入演算子=は、演算子と代入を組み合わせたすべての演算子の基礎です。一般的な形式はa <op>= bであり、コンパイラはそれをa = a <op> bに変換します。

一般的な展開式に対する例外は、論理演算子です。

  • a ||= ba || (a = b)に変換されます。
  • a &&= ba && (a = b)に変換されます。

aがインデックスアクセサ([])である場合、別の特別なケースがあります。これは、nullableなバリアント(右辺の[]?)に変更されます。

  • a[i] ||= ba[i] = (a[i]? || b)に変換されます。
  • a[i] &&= ba[i] = (a[i]? && b)に変換されます。

すべての変換は、受信者(a)が変数であることを前提としています。呼び出しである場合、置換は意味的に同等ですが、実装は少し複雑になり(匿名の一時変数を導入)、a=が呼び出し可能であることを期待します。

受信者は変数または呼び出し以外にすることはできません。

演算子 説明 オーバーロード可能 結合性
+= 加算と代入 i += 1 いいえ
&+= ラップアラウンド加算と代入 i &+= 1 いいえ
-= 減算と代入 i -= 1 いいえ
&-= ラップアラウンド減算と代入 i &-= 1 いいえ
*= 乗算と代入 i *= 1 いいえ
&*= ラップアラウンド乗算と代入 i &*= 1 いいえ
/= 除算と代入 i /= 1 いいえ
//= 床除算と代入 i //= 1 いいえ
%= 剰余と代入 i %= 1 はい
|= ビットごとのORと代入 i |= 1 いいえ
&= ビットごとのANDと代入 i &= 1 いいえ
^= ビットごとのXORと代入 i ^= 1 いいえ
**= べき乗と代入 i **= 1 いいえ
<<= 左シフトと代入 i <<= 1 いいえ
>>= 右シフトと代入 i >>= 1 いいえ
||= 論理ORと代入 i ||= true いいえ
&&= 論理ANDと代入 i &&= true いいえ

インデックスアクセサ

インデックスアクセサは、インデックスまたはキーによって値を照会するために使用されます(たとえば、配列のアイテムまたはマップのエントリ)。nullableなバリアント[]?は、インデックスが見つからない場合にnilを返すことが期待されますが、non-nullableなバリアントはそのような場合は例外を発生させます。標準ライブラリの実装では通常、KeyErrorまたはIndexErrorを発生させます。

演算子 説明 オーバーロード可能
[] インデックスアクセサ ary[i] はい
[]? nullableインデックスアクセサ ary[i]? はい