コンテンツにスキップ

代入

代入式は、名前付き識別子(通常は変数)に値を割り当てます。 代入演算子は等号 (=) です。

代入のターゲットは次のいずれかになります。

# Assigns to a local variable
local = 1

# Assigns to an instance variable
@instance = 2

# Assigns to a class variable
@@class = 3

# Assigns to a constant
CONST = 4

# Assigns to a setter method
foo.method = 5
foo[0] = 6

代入先としてのメソッド

等号 (=) で終わるメソッドは、セッターメソッドと呼ばれます。これは代入のターゲットとして使用できます。代入演算子のセマンティクスは、メソッド呼び出しへのシンタックスシュガーの形式として適用されます。

セッターメソッドの呼び出しには、明示的なレシーバーが必要です。レシーバーのない構文 x = y は、常にローカル変数への代入として解析され、メソッド x= の呼び出しとして解析されることはありません。括弧を追加しても、ローカル変数から読み込む場合のようにメソッド呼び出しを強制することはありません。

次の例は、標準的なメソッド表記と代入演算子を使用した、セッターメソッドへの2つの呼び出しを示しています。どちらの代入式も同等です。

class Thing
  def name=(value); end
end

thing = Thing.new

thing.name=("John")
thing.name = "John"

次の例は、標準的なメソッド表記とインデックス代入演算子を使用した、インデックス付き代入メソッドへの2つの呼び出しを示しています。どちらの代入式も同等です。

class List
  def []=(key, value); end
end

list = List.new

list.[]=(2, 3)
list[2] = 3

複合代入

複合代入は、代入演算子と別の演算子の組み合わせです。これは、定数を除くすべてのターゲットタイプで機能します。

= 文字を含むシンタックスシュガーがいくつか利用可能です。

local += 1  # same as: local = local + 1

これは、対応するターゲット local が、変数として、またはそれぞれのゲッターおよびセッターメソッドを介して代入可能であることを前提としています。

= 演算子のシンタックスシュガーは、セッターメソッドおよびインデックス代入メソッドでも利用可能です。 || および && は、キーの存在を確認するために []? メソッドを使用することに注意してください。

person.age += 1 # same as: person.age = person.age + 1

person.name ||= "John" # same as: person.name || (person.name = "John")
person.name &&= "John" # same as: person.name && (person.name = "John")

objects[1] += 2 # same as: objects[1] = objects[1] + 2

objects[1] ||= 2 # same as: objects[1]? || (objects[1] = 2)
objects[1] &&= 2 # same as: objects[1]? && (objects[1] = 2)

連鎖代入

同じ値を連鎖代入を使用して複数のターゲットに割り当てることができます。これは、定数を除くすべてのターゲットタイプで機能します。

a = b = c = 123

# Now a, b and c have the same value:
a # => 123
b # => 123
c # => 123

多重代入

式をコンマ (,) で区切ることにより、複数の変数を同時に宣言/代入できます。これは、定数を除くすべてのターゲットタイプで機能します。

name, age = "Crystal", 1

# The above is the same as this:
temp1 = "Crystal"
temp2 = 1
name = temp1
age = temp2

式は一時変数に代入されるため、1行で変数の内容を交換できることに注意してください。

a = 1
b = 2
a, b = b, a
a # => 2
b # => 1

多重代入は、= で終わるメソッドでも利用できます。

person.name, person.age = "John", 32

# Same as:
temp1 = "John"
temp2 = 32
person.name = temp1
person.age = temp2

また、インデックス代入 ([]=) でも利用できます。

objects[1], objects[2] = 3, 4

# Same as:
temp1 = 3
temp2 = 4
objects[1] = temp1
objects[2] = temp2

1対多の代入

右辺に式が1つだけ含まれている場合、左辺の各変数に対して次のように型がインデックス付けされます。

name, age, source = "Crystal, 123, GitHub".split(", ")

# The above is the same as this:
temp = "Crystal, 123, GitHub".split(", ")
name = temp[0]
age = temp[1]
source = temp[2]

さらに、strict_multi_assign フラグが指定されている場合、要素の数はターゲットの数と一致する必要があり、右辺は Indexable である必要があります。

name, age, source = "Crystal, 123, GitHub".split(", ")

# The above is the same as this:
temp = "Crystal, 123, GitHub".split(", ")
if temp.size != 3 # number of targets
  raise IndexError.new("Multiple assignment count mismatch")
end
name = temp[0]
age = temp[1]
source = temp[2]

a, b = {0 => "x", 1 => "y"} # Error: right-hand side of one-to-many assignment must be an Indexable, not Hash(Int32, String)

スプラット代入

代入の左辺には、他のターゲットに割り当てられていない値を収集するスプラットを1つ含めることができます。右辺に式が1つある場合は、範囲インデックスが使用されます。

head, *rest = [1, 2, 3, 4, 5]

# Same as:
temp = [1, 2, 3, 4, 5]
head = temp[0]
rest = temp[1..]

負のインデックスは、スプラット後のターゲットに使用されます。

*rest, tail1, tail2 = [1, 2, 3, 4, 5]

# Same as:
temp = [1, 2, 3, 4, 5]
rest = temp[..-3]
tail1 = temp[-2]
tail2 = temp[-1]

式に十分な要素がなく、スプラットがターゲットの途中に表示される場合、IndexError が発生します。

a, b, *c, d, e, f = [1, 2, 3, 4]

# Same as:
temp = [1, 2, 3, 4]
if temp.size < 5 # number of non-splat assignment targets
  raise IndexError.new("Multiple assignment count mismatch")
end
# note that the following assignments would incorrectly not raise if the above check is absent
a = temp[0]
b = temp[1]
c = temp[2..-4]
d = temp[-3]
e = temp[-2]
f = temp[-1]

右辺の式は、Indexable である必要があります。サイズチェックと Indexable チェックの両方は、strict_multi_assign フラグがない場合でも発生します(上記の 1対多の代入を参照してください)。

複数の値がある場合、Tuple が形成されます。

*a, b, c = 3, 4, 5, 6, 7

# Same as:
temp1 = {3, 4, 5}
temp2 = 6
temp3 = 7
a = temp1
b = temp2
c = temp3

アンダースコア

アンダースコアは、任意の代入の左辺に表示できます。値を割り当てても効果はなく、アンダースコアから読み込むことはできません。

_ = 1     # no effect
_ = "123" # no effect
puts _    # Error: can't read from _

右辺から返される値の一部が重要でない場合、多重代入で役立ちます。

before, _, after = "main.cr".partition(".")

# The above is the same as this:
temp = "main.cr".partition(".")
before = temp[0]
_ = temp[1] # this line has no effect
after = temp[2]

*_ への代入は完全に削除されるため、多重代入を使用して、中間オブジェクトを作成せずに、値の最初と最後の要素を効率的に抽出できます。

first, *_, last = "127.0.0.1".split(".")

# Same as:
temp = "127.0.0.1".split(".")
if temp.size < 2
  raise IndexError.new("Multiple assignment count mismatch")
end
first = temp[0]
last = temp[-1]