Assignment
分配
在 Ruby 中,赋值使用=
(等号)字符。本例将数字5分配给本地变量v
:
v = 5
如果变量先前未被引用,则赋值会创建一个局部变量。
局部变量名称
局部变量名称必须以小写的 US-ASCII 字母或设置了8位的字符开头。通常,本地变量与 US-ASCII 兼容,因为键入它们的键存在于所有键盘上。
(Ruby 程序必须使用 US-ASCII 兼容的字符集编写,在这种字符集中,如果设置了8位,则表示扩展字符,Ruby 允许本地变量包含这些字符。)
局部变量名称可能包含字母,数字,_
(下划线或下划线)或第八位设置的字符。
局部变量范围
一旦分配了本地变量名称 - 对该范围其余部分的所有名称使用都被认为是局部变量。
这里是一个例子:
1.times do
a = 1
puts "local variables in the block: #{local_variables.join ", "}"
end
puts "no local variables outside the block" if local_variables.empty?
这打印:
local variables in the block: a
no local variables outside the block
由于该块创建一个新的作用域,因此它内部创建的任何局部变量都不会泄漏到周围的作用域。
外部范围中定义的变量显示在内部范围内:
a = 0
1.times do
puts "local variables: #{local_variables.join ", "}"
end
这打印:
local variables: a
您可以通过;
在块的参数中列出它们来将块中的变量与外部范围隔离。有关示例,请参阅调用方法文档中的块局部变量文档。
另请参阅内核#local_variables,但请注意,for
循环不会像块一样创建新的范围。
局部变量和方法
在 Ruby 中,局部变量名称和方法名称几乎完全相同。如果你还没有分配到其中一个不明确的名字,ruby 会假设你想调用一个方法。一旦你分配了名字,ruby 会假设你想引用一个局部变量。
分析器遇到赋值时创建局部变量,而不是在赋值时发生:
a = 0 if false # does not assign to a
p local_variables # prints [:a]
p a # prints nil
方法与局部变量名称之间的相似性会导致代码混淆,例如:
def big_calculation
42 # pretend this takes a long time
end
big_calculation = big_calculation()
现在任何引用都big_calculation
被认为是局部变量,并且会被缓存。要调用该方法,请使用self.big_calculation
。
您可以通过使用上面显示的空参数括号强制进行方法调用,或者使用类似的明确接收方来强制执行方法调用self.
。如果方法的可见性不公开,则使用明确的接收方可能会引发 NameError。
另一个通常令人困惑的情况是使用修饰符时if
:
p a if a = 0.zero?
而非打印的“真”,你收到一个 NameError,“未定义的局部变量或方法a'”. Since ruby parses the bare
一left of the
,如果first and has not yet seen an assignment to
一个it assumes you wish to call a method. Ruby then sees the assignment to
了`,并会假设你引用一个本地方法。
这种混淆来自表达的乱序执行。首先分配局部变量 - 然后尝试调用不存在的方法。
变量实例
实例变量在同一对象的所有方法中共享。
实例变量必须以@
(“at” 符号或商业地址)开头。否则,实例变量名称将按照规则作为局部变量名称。由于实例变量以@
第二个字符开头,可能是一个大写字母。
这是一个实例变量用法的例子:
class C
def initialize(value)
@instance_variable = value
end
def value
@instance_variable
end
end
object1 = C.new "some value"
object2 = C.new "other value"
p object1.value # prints "some value"
p object2.value # prints "other value"
未初始化的实例变量的值为nil
。如果您在启用警告的情况下运行 Ruby,您将在访问未初始化的实例变量时收到警告。
value
方法可以访问该方法设置的值initialize
,但只能访问同一个对象。
类变量
类变量在类,它的子类和它的实例之间共享。
类变量必须以@@
(两个 “at” 符号)开头。名称的其余部分遵循与实例变量相同的规则。
这里是一个例子:
class A
@@class_variable = 0
def value
@@class_variable
end
def update
@@class_variable = @@class_variable + 1
end
end
class B < A
def update
@@class_variable = @@class_variable + 2
end
end
a = A.new
b = B.new
puts "A value: #{a.value}"
puts "B value: #{b.value}"
这打印:
A value: 0
B value: 0
继续相同的例子,我们可以使用任何一个类中的对象进行更新,并且该值是共享的:
puts "update A"
a.update
puts "A value: #{a.value}"
puts "B value: #{b.value}"
puts "update B"
b.update
puts "A value: #{a.value}"
puts "B value: #{b.value}"
puts "update A"
a.update
puts "A value: #{a.value}"
puts "B value: #{b.value}"
这打印:
update A
A value: 1
B value: 1
update B
A value: 3
B value: 3
update A
A value: 4
B value: 4
访问未初始化的类变量将引发 NameError 异常。
请注意,类有实例变量,因为类是对象,所以尽量不要混淆类和实例变量。
全局变量
全局变量无处不在。
全局变量以$
(美元符号)开头。名称的其余部分遵循与实例变量相同的规则。
这里是一个例子:
$global = 0
class C
puts "in a class: #{$global}"
def my_method
puts "in a method: #{$global}"
$global = $global + 1
$other_global = 3
end
end
C.new.my_method
puts "at top-level, $global: #{$global}, $other_global: #{$other_global}"
这打印:
in a class: 0
in a method: 0
at top-level, $global: 1, $other_global: 3
未初始化的全局变量的值为nil
。
Ruby 有一些特殊的全局变量,其取决于上下文的行为有所不同,例如正则表达式匹配变量或分配给它时有副作用。详细信息请参阅全局变量文档。
分配方法
您可以定义像分配一样的方法,例如:
class C
def value=(value)
@value = value
end
end
c = C.new
c.value = 42
使用赋值方法可以让程序看起来更漂亮。当分配给实例变量时,大多数人使用 Module#attr_accessor:
class C
attr_accessor :value
end
使用方法分配时,您必须始终有一个接收器。如果你没有接收器,Ruby假定你正在分配一个局部变量:
class C
attr_accessor :value
def my_method
value = 42
puts "local_variables: #{local_variables.join ", "}"
puts "@value: #{@value.inspect}"
end
end
C.new.my_method
这打印:
local_variables: value
@value: nil
要使用分配方法,您必须设置接收器:
class C
attr_accessor :value
def my_method
self.value = 42
puts "local_variables: #{local_variables.join ", "}"
puts "@value: #{@value.inspect}"
end
end
C.new.my_method
这打印:
local_variables:
@value: 42
缩写赋值
您可以混合几个操作员和任务。要将1添加到您可以编写的对象:
a = 1
a += 2
p a # prints 3
这相当于:
a = 1
a = a + 2
p a # prints 3
您可以使用以下运算符是这样的:+,-,*,/,%,**,&,|,^,<<,>>
还有||=
和&&=
。如果值是nil
或false
当后者进行赋值时,前者进行赋值,如果该值不是nil
或false
。
这里是一个例子:
a ||= 0
a &&= 1
p a # prints 1
注意,这两个运营商更像a || a = 0
比a = a || 0
。
隐式数组赋值
分配时可以通过列出多个值来隐式创建一个数组:
a = 1, 2, 3
p a # prints [1, 2, 3]
这隐含地创建一个数组。
您可以*
在分配时使用或 “splat” 运算符或解压缩数组。这与多次分配类似:
a = *[1, 2, 3]
p a # prints [1, 2, 3]
您可以在任务右侧的任何地方摔打:
a = 1, *[2, 3]
p a # prints [1, 2, 3]
多重分配
您可以将右侧的多个值分配给多个变量:
a, b = 1, 2
p a: a, b: b # prints {:a=>1, :b=>2}
在下面的章节中,任何使用“变量”的地方都可以使用赋值方法,实例,类或全局:
def value=(value)
p assigned: value
end
self.value, $global = 1, 2 # prints {:assigned=>1}
p $global # prints 2
您可以使用多个赋值就地交换两个值:
old_value = 1
new_value, old_value = old_value, 2
p new_value: new_value, old_value: old_value
# prints {:new_value=>1, :old_value=>2}
如果赋值右侧的值比左侧的变量多,则会忽略额外的值:
a, b = 1, 2, 3
p a: a, b: b # prints {:a=>1, :b=>2}
您可以使用*
它在作业的右侧收集额外的值。
a, *b = 1, 2, 3
p a: a, b: b # prints {:a=>1, :b=>[2, 3]}
本*
可以在左侧的任意位置出现:
*a, b = 1, 2, 3
p a: a, b: b # prints {:a=>[1, 2], :b=>3}
但你只能*
在任务中使用一个。
数组分解
像方法参数中的数组分解一样,您可以在使用括号进行赋值时分解数组:
(a, b) = [1, 2]
p a: a, b: b # prints {:a=>1, :b=>2}
您可以将数组分解为更大的多个赋值的一部分:
a, (b, c) = 1, [2, 3]
p a: a, b: b, c: c # prints {:a=>1, :b=>2, :c=>3}
由于每个分解都被认为是它自己的多重赋值,因此可以用它*
来收集分解中的参数:
a, (b, *c), *d = 1, [2, 3, 4], 5, 6
p a: a, b: b, c: c, d: d
# prints {:a=>1, :b=>2, :c=>[3, 4], :d=>[5, 6]}