Ruby 2.4

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 bareleft 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

您可以使用以下运算符是这样的:+,-,*,/,%,**,&,|,^,<<,>>

还有||=&&=。如果值是nilfalse当后者进行赋值时,前者进行赋值,如果该值不是nilfalse

这里是一个例子:

a ||= 0 a &&= 1 p a # prints 1

注意,这两个运营商更像a || a = 0a = 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]}