Ruby 变量
最后修改于 2023 年 10 月 18 日
在本 Ruby 教程中,我们将更详细地研究变量。
一个 变量 是存储数据的地方。 每个变量都有一个唯一的名称。 变量名有一些命名约定。 变量保存对象。 更确切地说,它们引用位于计算机内存中的特定对象。 每个对象都具有特定数据类型。 有内置的数据类型,也有自定义构建的数据类型。
Ruby 属于动态语言家族。 与 Java、C 或 Pascal 等强类型语言不同,动态语言不会声明变量具有特定数据类型。 相反,解释器在赋值时确定数据类型。 Ruby 中的变量可以随时间包含不同的值和不同类型的值。
#!/usr/bin/ruby i = 5 puts i i = 7 puts i
术语“变量”源于一个事实,即变量,与常量不同,可以随时间取不同的值。 在此示例中,有一个名为 i
的变量。 首先,它被赋值为 5,后来又被赋值为另一个值 7。
Ruby 变量命名约定
与其他任何编程语言一样,Ruby 对变量标识符有一些命名约定。
Ruby 是一种 大小写敏感 的语言。 这意味着 age 和 Age 是两个不同的变量名。 大多数语言都是大小写敏感的。 BASIC 是一个例外; 它是一种大小写不敏感的语言。 虽然我们可以通过更改字符的大小写来创建不同的名称,但不建议这样做。
#!/usr/bin/ruby i = 5 p i I = 7 p I
代码示例定义了两个变量:I
和 i
。 它们包含不同的值。
$ ./case.rb 5 7
Ruby 中的变量名可以由字母数字字符和下划线 _
字符组成。 变量不能以数字开头。 这使得解释器更容易将文字数字与变量区分开来。 变量名不能以大写字母开头。 如果标识符以大写字母开头,则在 Ruby 中将其视为常量。
#!/usr/bin/ruby name = "Jane" placeOfBirth = "Bratislava" placeOfBirth = "Kosice" favorite_season = "autumn" n1 = 2 n2 = 4 n3 = 7 p name, placeOfBirth, favorite_season p n1, n2, n3
在本脚本中,我们展示了一些有效的变量名。
变量名应该 有意义。 选择描述性名称作为变量是一个好的编程习惯。 这样程序会更具可读性。
#!/usr/bin/ruby name = "Jane" place_of_birth = "Bratislava" occupation = "student" i = 5 while i > 0 do puts name i -= 1 end
该脚本显示了三个描述性变量名。 与例如 pob 相比,place_of_birth 对程序员来说更具描述性。 通常认为在循环中选择简单的变量名是可以接受的。
Ruby 符
变量标识符也可以以特殊字符开头,也称为 符。 符是附加到标识符的符号。 Ruby 中的变量符表示变量作用域。 这与 Perl 形成对比,Perl 中的符表示数据类型。 Ruby 变量符是 $
和 @
。
#!/usr/bin/ruby tree_name = "pine" $car_name = "Peugeot" @sea_name = "Black sea" class Animal @@species = "Cat" end p local_variables p global_variables.include? :$car_name p self.instance_variables p Animal.class_variables
我们有四个具有不同作用域的变量。 作用域是引用变量的范围。 我们使用特殊的内置方法来确定变量的作用域。
tree_name = "pine"
没有符的变量是局部变量。 局部变量仅在局部有效:例如,在方法、块或模块内部。
$car_name = "Peugeot"
全局变量以 $
字符开头。 它们在任何地方都有效。 应限制在程序中使用全局变量。
@sea_name = "Black sea"
以 @
符开头的变量名是实例变量。 该变量在对象内部有效。
class Animal @@species = "Cat" end
最后,我们有一个类变量。 该变量对特定类的所有实例都有效。
p local_variables
local_variables
给出一个在特定上下文中定义的所有局部变量的数组。 我们的上下文是 Ruby 顶层。
p global_variables.include? :$car_name
同样,global_variables
产生一个全局变量数组。 我们不会将所有全局变量打印到终端,因为它们有很多。 每个 Ruby 脚本都从一堆预定义的变量开始。 相反,我们调用数组的 include?
方法来检查我们的全局变量是否在数组中定义。 还要注意,我们使用它们的符号来引用变量。 (符号以冒号字符开头。)
p self.instance_variables
self
伪变量指向 instance_variables
方法的接收者。 在我们的例子中,接收者是 main
,即 Ruby 顶层执行区域。
p Animal.class_variables
最后,我们有一个类变量数组。 main
是 Animal
类的一个实例。
$ ./sigils.rb [:tree_name] true [:@sea_name] [:@@species]
我们看到了变量的符号名称。
Ruby 局部变量
局部变量是在 Ruby 源代码的局部区域内有效的变量。 该区域也称为 局部作用域。 局部变量存在于 Ruby 模块、方法、类的定义中。
#!/usr/bin/ruby def method1 x = 5 p x end method1 p x
我们有一个名为 method1
的方法,它有一个变量。 该变量是局部的。 这意味着它仅在方法定义中有效。 我们只能在方法名称和 end
关键字之间引用 x
变量。
def method1 x = 5 p x end
这是 method1
方法的定义。 在方法内部,我们创建一个局部 x
变量。 我们将变量的值打印到终端。
method1
该方法被调用。
p x
我们尝试在方法定义之外引用局部变量。 这将导致 NameError
。 Ruby 解释器找不到这样的标识符。
$ ./locals.rb 5 Traceback (most recent call last): ./locals.rb:10:in `<main>': undefined local variable or method `x' for main:Object (NameError)
运行该示例会给出上述输出。
以下示例是前一个示例的稍作修改。
#!/usr/bin/ruby x = 5 def method1 x = 10 p x end method1 p x
我们有两个 x
变量。 一个在 method1
内部定义,另一个在外部定义。 它们是两个不同的局部变量。 它们彼此之间不会发生冲突。
x = 5
我们创建了一个局部 x
变量,它保存值 5。 该变量在主执行区域的局部作用域中有效。 它在 method1
内部无效。
def method1 x = 10 p x end
在 method1
的定义内部,定义了一个新的局部变量 x
。 它的值为 10。它存在于 method1
方法的主体中。 在 end
关键字之后,它不再存在。
$ ./locals2.rb 10 5
如果一个方法带参数,则会为每个参数创建一个局部变量。
#!/usr/bin/ruby def rectangle_area a, b puts local_variables return a * b end puts rectangle_area 5, 6
我们有一个方法定义,它接受两个值。 该方法返回一个矩形的面积。
def rectangle_area a, b puts local_variables return a * b end
rectangle_area 方法有两个参数。 它们是一个矩形的边,我们为其计算面积。 将自动为标识符 a 和 b 创建两个局部变量。 我们调用 local_variables
方法来查看我们在该方法中使用了哪些局部变量。
puts rectangle_area 5, 6
在这里,我们传递两个值给方法 rectangle_area
。 这些值将被分配给在该方法内部创建的两个局部变量。
$ ./parameters.rb a b 30
输出显示了三件事。 前两个是 rectangle_area 方法中局部变量的名称。 第三个是给定矩形的计算面积。
一个方法可以在另一个方法内部定义。 内部方法有它们自己的局部变量。
#!/usr/bin/ruby def method1 def method2 def method3 m5, m6 = 3 puts "Level 3" puts local_variables end m3, m4 = 3 puts "Level 2" puts local_variables method3 end m1, m2 = 3 puts "Level 1" puts local_variables method2 end method1
在这个 Ruby 脚本中,我们创建了三个方法。 method2
和 method3
是内部方法。 method2
在 method1
内部定义,method3
在 method2
内部定义。 每个方法的局部变量只能在其定义的方法中访问。
$ ./locals2.rb Level 1 m1 m2 Level 2 m3 m4 Level 3 m5 m6
从输出中我们可以看到 method1 有两个局部变量 m1
和 m2
。 内部的 method2
有局部变量 m3
和 m4
。 method3
,最内部的方法,有局部变量 m5
和 m6
。
本节的最后一个示例将展示几个局部作用域的演示。
#!/usr/bin/ruby module ModuleM m1, m2 = 4 puts "Inside module" puts local_variables end def method1 v, w = 3 puts "Inside method" puts local_variables end class Some x, y = 2 puts "Inside class" puts local_variables end method1 t1, t2 = 7 puts "Inside toplevel" puts local_variables
在代码示例中,我们在模块、方法、类和顶层内部创建局部变量。 local_variables
是 Kernel
模块的一个方法,它返回所有当前的局部变量。
module ModuleM m1, m2 = 4 puts "Inside module" puts local_variables end
模块是方法和常量的集合。 我们创建了两个局部变量 m1
和 m2
。
def method1 v, w = 3 puts "Inside method" puts local_variables end
在 method1
中创建了两个局部变量 v
和 w
。
class Some x, y = 2 puts "Inside class" puts local_variables end
x
和 y
局部变量是在 Some
类的定义中创建的。
t1, t2 = 7
最后,创建了两个属于 Ruby 顶层局部作用域的局部变量。
$ ./locals3.rb Inside module m1 m2 Inside class x y Inside method v w Inside toplevel t1 t2
输出显示了每个局部作用域的局部变量。
Ruby 全局变量
全局变量在脚本的任何地方都有效。 它们在 Ruby 中以 $
符开头。
不鼓励使用全局变量。 全局变量很容易导致许多编程错误。 仅当有理由这样做时才应使用全局变量。 建议程序员尽可能使用局部变量,而不是全局变量。
#!/usr/bin/ruby $gb = 6 module ModuleM puts "Inside module" puts $gb end def method1 puts "Inside method" puts $gb end class Some puts "Inside class" puts $gb end method1 puts "Inside toplevel" puts $gb puts global_variables.include? :$gb
在示例中,我们有一个全局变量 $gb
。 我们显示该变量可以在模块、方法、类和顶层中引用。 全局变量 $gb
在所有这些实体中都有效。
$gb = 6
创建了一个全局变量 $gb
; 它的值为 6。
module ModuleM puts "Inside module" puts $gb end
在模块的定义内部,我们打印全局变量的值。
def method1 puts "Inside method" puts $gb end
在方法的定义内部,我们打印全局变量的值。
class Some puts "Inside class" puts $gb end
在类的定义内部,我们打印全局变量的值。
puts $gb puts global_variables.include? :$gb
最后,在顶层执行区域中,我们打印全局变量的值,以及该变量是否在由 global_variables
方法生成的数组中。
$ ./globals.rb Inside module 6 Inside class 6 Inside method 6 Inside toplevel 6 true
该示例的输出确认全局变量在任何地方都是可访问的。
当 Ruby 脚本启动时,它可以访问多个预定义的全局变量。 这些全局变量被认为无害,有助于解决常见的编程作业。
#!/usr/bin/ruby p $LOAD_PATH p $:
该脚本显示了一个 $LOAD_PATH
全局变量。 该变量列出了 load
和 require
方法搜索的目录。 $:
是 $LOAD_PATH
名称的短同义词。
本章的预定义变量部分将介绍更多全局变量。
Ruby 实例变量、类变量
在本节中,我们将简要介绍实例和类变量。 它们将在面向对象编程章节中更详细地描述。
实例变量是属于特定对象实例的变量。 每个对象都有自己的对象变量。 实例变量以 @
符开头。 类变量 属于特定类。 从特定类创建的所有对象共享类变量。 类变量以 @@
字符开头。
#!/usr/bin/ruby class Being @@is = true def initialize nm @name = nm end def to_s "This is #{@name}" end def does_exist? @@is end end b1 = Being.new "Being 1" b2 = Being.new "Being 2" b3 = Being.new "Being 3" puts b1, b2, b3 p b1.does_exist? p b2.does_exist? p b3.does_exist?
我们创建了一个自定义的 Being
类。 Being
类有一个类变量和一个实例变量。
class Being @@is = true
@@is
是一个类变量。 该变量由 Being
类的所有实例共享。 此示例的逻辑是 Being
是,而 NotBeing 不是。
def initialize nm @name = nm end
initialize
方法是一个构造函数。 当创建对象时调用该方法。 创建了一个 @name
实例变量。 该变量特定于具体对象。
def to_s "This is #{@name}" end
当对象是打印方法(例如 p
或 puts
)的参数时,调用 to_s
方法。 在我们的例子中,该方法给出了对象的简短的、人类可读的描述。
def does_exist? @@is end
does_exist?
方法返回类变量。
b1 = Being.new "Being 1" b2 = Being.new "Being 2" b3 = Being.new "Being 3"
从 Being
类创建了三个对象。 每个对象都有一个不同的名称。 对象的名称将存储在实例方法中,该方法是每个对象实例独有的。 这将在 to_s
方法中使用,该方法给出一个对象的简短描述。
puts b1, b2, b3
puts
方法将创建的对象作为三个参数。 它在每个对象上调用 to_s
方法。
p b1.does_exist? p b2.does_exist? p b3.does_exist?
最后,我们调用每个实例的 does_exist?
方法并打印它们的返回值。 这三个方法的输出相同,因为每个方法都返回类变量。
$ ./icvars.rb This is Being 1 This is Being 2 This is Being 3 true true true
该示例的输出。 前三个消息是唯一的。 字符串存储在对象的实例变量中。 true 值是类变量的值,被调用了三次。
Ruby 环境和命令行变量
ENV
常量用于访问环境变量。 它是一个 Ruby 哈希。 每个环境变量都是 ENV
哈希的键。
ARGV
常量保存命令行参数值。 它们由程序员在启动脚本时传递。 ARGV
是一个数组,它将参数存储为字符串。 $*
是 ARGV
的别名。
ENV
和 ARGV
都是全局常量。
#!/usr/bin/ruby ARGV.each do |a| puts "Argument: #{a}" end
在脚本中,我们循环遍历 ARGV
数组并打印其每个值。
$ ./command_line.rb 1 2 3 Argument: 1 Argument: 2 Argument: 3
我们给出了三个命令行参数。 它们被打印到控制台上,每行一个。
以下示例将处理环境变量。
#!/usr/bin/ruby puts ENV['SHELL'] puts ENV['LANG'] puts ENV['TERM']
该脚本将向终端打印三个环境变量的值。 这些值取决于我们操作系统的操作系统设置。
$ ./environment.rb /bin/bash en_US.UTF-8 xterm-256color
这是一个示例输出。
Ruby 伪变量
Ruby 有一些变量,称为 伪变量。 它们与常规变量不同。 我们不能为伪变量赋值。
self
是当前方法的接收者。 nil
是 NilClass
的唯一实例。 它表示值的缺失。 true
是 TrueClass
的唯一实例。 它表示布尔真。 false
是 FalseClass
的唯一实例。 它表示布尔假。
true 和 false 是布尔数据类型的值。 从另一个角度来看,它们是特定类的实例。 这是因为 Ruby 中的所有内容都是对象。 这看起来不必要地复杂。 但这是前面提到的 Ruby 习惯用法的后果。
#!/usr/bin/ruby p self p nil p true p false p self.class p nil.class p true.class p false.class
这是伪变量的一个例子。 我们使用 p
方法打印所有四个伪变量。 然后我们找出所有变量的类名。
p self
在这种情况下,self
伪变量返回主执行上下文。
$ ./pseudo.rb main nil true false Object NilClass TrueClass FalseClass
示例输出。
在本节的第二个示例中,我们进一步查看 self
。
#!/usr/bin/ruby class Some puts self end class Other puts self end puts self
正如我们所说,self
引用当前方法的接收者。 上面的示例显示了三个不同接收者的示例。
class Some puts self end
接收者是名为 Some
的类。
class Other puts self end
这是另一个接收者:名为 Other
的类。
puts self
第三个接收者是 Ruby 顶层。
$ ./pseudo_self.rb Some Other main
本节的最后一个示例将介绍另外三个伪变量。
#!/usr/bin/ruby if true puts "This message is shown" end if false puts "This message is not shown" end p $name p $age
上面的示例显示了 true
、false
和 nil
伪变量正在工作。
if true puts "This message is shown" end
true
用于布尔表达式。 消息总是被打印出来。
if false puts "This message is not shown" end
此消息永远不会被打印。 条件不满足。 在布尔表达式中,我们总是得到一个负值。
p $name p $age
如果引用了全局值但未初始化,它们将包含 nil
伪变量。 它代表值的缺失。
$ ./pseudo2.rb This message is shown nil nil
Ruby 预定义的变量
Ruby 有很多预定义的全局变量。 这是 Perl 语言的遗产。 Ruby 受到 Perl 的强烈影响。 它们在 Ruby 脚本启动时是可访问的。 我们有一些预定义的 Ruby 变量的例子。
#!/usr/bin/ruby print "Script name: ", $0, "\n" print "Command line arguments: ", $*, "\n" puts "Process number of this script: #{$$}"
使用了三个预定义的变量:$0
、$*
和$$
。$0
存储当前脚本的名称。$*
变量存储命令行参数。而$$
存储脚本的 PID(进程 ID)。
$ ./predefined.rb 1 2 3 Script name: ./predefined.rb Command line arguments: ["1", "2", "3"] Process number of this script: 20909
这是一个示例输出。
全局变量$?
存储最后一个已执行子进程的退出状态。
#!/usr/bin/ruby system 'echo "Ruby"' puts $? %x[exit '1'] puts $?
我们运行两个外部子进程,并使用$?
变量检查它们的退出状态。
system 'echo "Ruby"' puts $?
通过使用system
方法,我们启动一个子进程。它是一个 echo bash 命令,用于将消息打印到终端。
%x[exit '1'] puts $?
在第二个例子中,我们使用状态 1 执行 bash exit
命令。这次我们使用%x
运算符,它在两个选定的分隔符之间执行一个命令。我们选择了 []
字符。
$ ./predefined2.rb Ruby pid 20952 exit 0 pid 20953 exit 1
第一个子进程以状态 0 终止,第二个子进程以退出状态 1 终止。
在最后一个例子中,我们展示了三个与正则表达式一起使用的全局预定义变量。
#!/usr/bin/ruby "Her name is Jane" =~ /name/ p $` p $& p $'
当我们对一个字符串应用=~
运算符时,Ruby 会设置一些变量。$&
变量包含与最后一个正则表达式匹配的字符串。$`
包含在 $&
之前的字符串,而 $'
包含在 $&
之后的字符串。
$ ./predefined3.rb "Her " "name" " is Jane"
在本 Ruby 教程中,我们更深入地研究了 Ruby 变量。