跳到内容

赋值

赋值表达式将一个值赋予一个命名标识符(通常是变量)。 赋值运算符 是等号 (=)。

赋值的目标可以是

# 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= 的调用。即使添加括号也不会强制方法调用,就像从局部变量读取时那样。

以下示例显示了用典型方法表示法和赋值运算符调用设置方法的两种方式。两种赋值表达式都是等价的。

class Thing
  def name=(value); end
end

thing = Thing.new

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

以下示例显示了用典型方法表示法和索引赋值运算符调用索引赋值方法的两种方式。两种赋值表达式都是等价的。

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

list = List.new

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

组合赋值

组合赋值 是赋值运算符和另一个运算符的组合。这适用于除常量外的任何目标类型。

有一些包含 = 字符的语法糖可用

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

这假设对应的目标 local 是可赋值的,要么作为变量,要么通过各自的 getter 和 setter 方法。

= 运算符语法糖也适用于 setter 和索引赋值方法。请注意,||&& 使用 []? 方法来检查键是否存在。

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

请注意,由于表达式被赋予临时变量,因此可以在一行代码中交换变量的内容

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

一对多赋值

如果右侧只包含一个表达式,则该类型将按以下方式为左侧的每个变量索引

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)

展开赋值

赋值的左侧可能包含一个展开符,它收集未分配给其他目标的任何值。如果右侧有一个表达式,则使用 范围 索引

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

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

对于展开符之后的 target,使用负索引

*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]

如果表达式没有足够的元素,并且展开符出现在 target 的中间,则会引发 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。即使没有 strict_multi_assign 标志(见上文 一对多赋值),也会进行大小检查和 Indexable 检查。

如果有多个值,则会形成一个 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]