POSTS
第四章、ruby变量
变量
变量是保存数据的地址。每个变量都有唯一的一个名字,变量命名存在着一些约定。变量保存着数据对象,更确切的说变量是对数据所在的计算机内存地址的引用。每一个对象都具有一定的数据类型,内置的类型或者自定义的类型。Ruby属于动态语言,与Java、C或者Pascal之类的强类型语言不同,动态语言不用为变量定义确切数据类型,而是解释器在分配变量时决定它的类型。在Ruby中程序运行过程变量可以包含不同类型的不同值。
命名约定
与其他语言一样,Ruby对变量标识符也有些命名约定。 Ruby是区分大小写的,这意味着age和Age是两个变量名。许多语言都是区分大小写的,但是BASIC例外。我们改变字符的大小写可以创建不同的变量,但是不推荐这种做法。
i = 5
p i
I = 7
p I
这个例子定义了两个变量I和i,它们保存了不同的值。
在Ruby中变量名可由字母数字和下划线组成。为了使解释器能简单的从字面上区分数字和变量,变量名不能以数字开头。变量名同样也不能以大写字母开头,在Ruby中以大写字母开头会被认为是常量。
变量名应该是有意义的。好的编程习惯是给变量取个具有描述性的名字,使得程序更加可读。
name = "Jane"
place_of_birth = "Bratislava"
occupation = "student"
i = 5
while i > 0 do
puts name
i -= 1
end
这个脚本展示三个具有描述性的变量名。对于程序员来说place_of_birth比其他的名字如pob更具有描述性。在循环的时候通常选择一个比较简单的变量名。
印章(Sigils)
变量标识符可以以一些特殊的印章(Sigils)符号开头。印章(Sigils)是附加在标识符上的符号。在Ruby中变量印章(Sigils)表示了变量的作用域范围。在Perl中它是表示数据的类型。Ruby的变量印章(Sigils)符号有$和@。
#!/usr/local/rvm/rubies/ruby-2.3.0/bin/ruby
tree_name = "pine"
$car_name = "Peugeot"
@sea_name = "Black sea"
@@species = "Cat"
p local_variables
p global_variables.include? :$car_name
p self.instance_variables
p Object.class_variables
我们定义了四个不同作用域范围的变量。作用域表示了该变量可以被引用到的范围。我们使用了内置的方法来决定变量的作用域。
tree_name = "pine"
不包含印章(Sigils)符号的变量是一个局部变量。局部变量只在局部有效,如在方法、代码块、模块内。
$car_name = "Peugeot"
全局变量以$符号开头。它们在所有地方都是有效的。在程序中不要滥用全局变量。
@sea_name = "Black sea"
实例变量是以@符号开头。它只有在实例对象中才有效。
@@species = "Cat"
最后我们定义了一个类变量。它在所有属于这个类的实例中都有效。
p local_variables
local_variables是保存了当前环境下所有定义的局部变量的数组。
p global_variables.include? :$car_name
类似的,global_variables是保存的全部全局变量的数组。由于全局变量很多,我们就不将它们全部在终端上打印了。每次Ruby脚本启动时都会预定义一些变量。我们使用数组的include?方法来检查我们全局变量是否定义了。同时请注意我们引用变量是用的符号。(符号是以一个冒号开头)
p self.instance_variables
self伪变量指向了instance_variables方法的接收对象。这个例子中的接收对象是main,Ruby的顶级执行区域。
p Object.class_variables
最后我们获取所有的类变量数组。main是一个Object类的实例。
➜ ~ ruby sigils.rb
sigils.rb:5: warning: class variable access from toplevel
[:tree_name]
true
[:@sea_name]
[:@@species]
以上是这个例子的输出结果,我们看到了变量的符号名。
局部变量
局部变量是只在Ruby源代码的局部区域有效。这个区域也称为局部作用域。局部变量是在Ruby的模块、方法、类中定义。
#!/usr/local/rvm/rubies/ruby-2.3.0/bin/ruby
def method1
x = 5
p x
end
method1
p x
我们定义了一个名为method1的方法,它有一个变量x。这个变量是局部变量。这意味着这个变量只在这个方法内有效。我们只能在这个方法名到end关键字之间访问x变量。
method1
方法被调用。
p x
我们试图在方法外部访问这个局部变量。这将导致NameError错误,Ruby解释器找不到这个标识符。
➜ ~ ruby locals.rb
5
locals.rb:9:in `<main>': undefined local variable or method `x' for main:Object (NameError)
以下例子是对前一个例子的简单修改。
#!/usr/local/rvm/rubies/ruby-2.3.0/bin/ruby
x = 5
def method1
x = 10
p x
end
method1
p x
我们定义了两个x变量。一个是在method1内部定义的,另一个是在外部定义的。他们是两个不同的局部变量,并不会相互冲突。
x = 5
我们创建了一个局部变量,它的值为5。这个变量的局部作用范围是main区域。它在method1内部是无效的。
def method1
x = 10
p x
end
在method1内部创建了一个新的x局部变量,它的值是10.它存在于method1方法内部,在end关键字之后就会失效。
➜ ~ ruby locals.rb
10
5
以上是输出结果。
如果一个方法接收了参数,那么就会创建与这些参数相对应的局部变量。
#!/usr/local/rvm/rubies/ruby-2.3.0/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方法传了两个值。这两个值将会分配给在方法内部创建的两个局部变量。
➜ ~ ruby parameters.rb
a
b
30
输出了三个结果。前两个是rectangle_area方法内部的局部变量名。第三个是面积的计算结果。
一个方法可以定义在另一个方法的内部。内嵌方法有它自己的局部变量。
#!/usr/local/rvm/rubies/ruby-2.3.0/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内部。每一个方法的局部变量仅在这个方法内是可访问的。
➜ ~ ruby lms.rb
Level 1
m1
m2
Level 2
m3
m4
Level 3
m5
m6
从输出结果我们可以知道method1有两个局部变量m1和m2。内嵌方法method2的局部变量有m3、m4。最内部的方法method3的局部变量是m5、m6。
这一节的最后一个例子将展示一些局部作用域的示范。
#!/usr/local/rvm/rubies/ruby-2.3.0/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是内核模块的一个方法,用于获取当前的所有局部变量。
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
在Some类中创建了两个局部变量x、y。
t1, t2 = 7
最后我们为Ruby顶级环境创建两个局部变量。
➜ ~ ruby locals3.rb
Inside module
m1
m2
Inside class
x
y
Inside method
v
w
Inside toplevel
t1
t2
输出结果展示了各个作用域的局部变量。模块、类和顶级环境是在执行脚本后按顺序加载的,谁在前面谁先加载执行,只有方法需要调用后才加载。
全局变量
全局变量可以在脚本的任何地方访问到。它们以$符号开头。 全局变量可能会引起很多程序错误,因此不鼓励使用全局变量。除非有原因要使用,否则建议使用局部变量。
#!/usr/local/rvm/rubies/ruby-2.3.0/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。
在顶级环境下打印全局变量的值,并检查这个变量是否为全局变量。
➜ ~ ruby globals.rb
Inside module
6
Inside class
6
Inside method
6
Inside toplevel
6
true
这个例子的输出结果确认了全局变量可以任何地方访问到。
当一个Ruby脚本启动时,它会访问多个预定义的全局变量。这些全局变量不认为有害并且能帮助完成一些常见的任务。
#!/usr/local/rvm/rubies/ruby-2.3.0/bin/ruby
p $LOAD_PATH
p $:
这个脚本显示了$LOAD_PATH这个全局变量。这个变量列出了require方法会搜索的所有目录。$:是$LOAD_PATH的缩写。
➜ ~ ruby globals.rb
["/usr/local/rvm/gems/ruby-2.3.0@global/gems/did_you_mean-1.0.0/lib", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/site_ruby/2.3.0", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/site_ruby/2.3.0/x86_64-linux", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/site_ruby", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/vendor_ruby/2.3.0", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/vendor_ruby/2.3.0/x86_64-linux", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/vendor_ruby", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/x86_64-linux"]
更多的全局变量会在本章的变量预定义这节中介绍。
实例变量、类变量
在这节将简要的介绍下实例变量和类变量。它们将会在面向对象那一章详细介绍。 实例变量是属于具体某个对象实例的变量。每个对象都有它自己的变量。实例变量以@符号开头。类变量属于特定某个类的。这个类所创建的对象实例共享这个类的类变量。类变量以@@符号开头。
#!/usr/local/rvm/rubies/ruby-2.3.0/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。
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
这个方法将刚创建的三个对象作为参数,它将调用每个对象的to_s方法。
p b1.does_exist?
p b2.does_exist?
p b3.does_exist?
最后我们调用每个实例对象的does_exist?方法。这三个方法会输出相同的结果,因为这个方法返回的是类变量。
➜ ~ ruby icvars.rb
This is Being 1
This is Being 2
This is Being 3
true
true
true
以上是这个例子的输出结果。前三条信息是唯一的,因为这个字符串是存储在实例变量中的。true值是类变量,它被调用的三次。
环境&命令行变量
可以用ENV常量来访问环境变量。它是一个Ruby的hash对象。每个环境变量都是ENV这个hash对象的键值。
ARGV常量存储了命令行的参数值。它们是在脚本启动时传递的。ARGV是一个数组,参数以字符串存储。$*是ARGV的别名。
ENV和ARGV都是全局常量。
#!/usr/local/rvm/rubies/ruby-2.3.0/bin/ruby
ARGV.each do |a|
puts "Argument: #{a}"
end
这个脚本我们通过循环遍历打印了ARGV的每个值。
➜ ~ ruby commandline.rb 1 2 3
Argument: 1
Argument: 2
Argument: 3
我们给了三个命令行参数。它们在终端上各打印了一行。
接下来的例子介绍了处理环境变量。
➜ ~ cat environment.rb
#!/usr/local/rvm/rubies/ruby-2.3.0/bin/ruby
puts ENV['SHELL']
puts ENV['LANG']
puts ENV['TERM']
这个脚本在终端上打印了三个环境变量的值。这些变量值的内容依赖于我们操作系统的系统设置。
➜ ~ ruby environment.rb
/bin/zsh
zh_CN.UTF-8
xterm-256color
伪变量
在Ruby中有一些变量被称作伪变量。它们不同于常规的变量,不能给它们设置值。 self是当前方法的接收者。nil是NilClass的唯一实例,它代表值不存在。true是TrueClass的唯一实例,它代表布尔真。flase是FalseClass是唯一实例,它代表布尔假。 true和false是布尔数据类型。从另一个角度来看他们是特殊的类实例,这是因为在Ruby中一切皆对象。
#!/usr/local/rvm/rubies/ruby-2.3.0/bin/ruby
p self
p nil
p true
p false
p self.class
p nil.class
p true.class
p false.class
这是一个伪变量的例子。我们打印所有的伪变量,然后再看它们的类名。
p self
在当前上下文self伪变量返回的是main执行的上下文。
➜ ~ ruby pseudo.rb
main
nil
true
false
Object
NilClass
TrueClass
FalseClass
在本节的第二个例子,我们将进一步分析self。
#!/usr/local/rvm/rubies/ruby-2.3.0/bin/ruby
class Some
puts self
end
class Other
puts self
end
puts self
之前我们说过,self是对当前方法接收者的引用。以上例子展示了三个不同的接收者。
➜ ~ ruby pseudoself.rb
Some
Other
main
本节的最后一个例子展示了另外三个伪变量。
#!/usr/local/rvm/rubies/ruby-2.3.0/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伪变量。
true用于布尔表达式中。这条消息总是会打印的。
p $name
p $age
如果全局变量没有初始化就引用,那么它们就会包含一个nil伪变量。这代表值不存在。
➜ ~ ruby pseudo2.rb
This message is shown
nil
nil
预定义变量
Ruby中有很多预定义的全局变量。这是继承到Perl,Ruby受Perl的影响很大。Ruby脚本启动之后就可以访问这些变量了。接下来有些例子展示预定义变量。
#!/usr/local/rvm/rubies/ruby-2.3.0/bin/ruby
print "Script name: ", $0, "\n"
print "Command line arguments: ", $*, "\n"
puts "Process number of this script: #{$$}"
以上使用了三个预定义变量。$0、$*和$$。$0存储了当前脚本的名字。$*存储了命令行参数。 $$存储了当前脚本程序的PID。
➜ ~ ruby predefined.rb 1 2 3
Script name: predefined.rb
Command line arguments: ["1", "2", "3"]
Process number of this script: 32473
$?全局变量存储了最后一个子进程的退出状态。
#!/usr/local/rvm/rubies/ruby-2.3.0/bin/ruby
system 'echo "Ruby"'
puts $?
%x[exit '1']
puts $?
我们执行两个子进程,然后使用$?查看它们的退出状态。
system 'echo "Ruby"'
puts $?
使用system方法启动一个子进程。它是一个bash的echo命令,用于在终端输出消息。
%x[exit '1']
puts $?
第二个情况是使用状态1执行bash的exit命令。这次我们使用%x操作符,用于执行一条被分隔符所选择的命令。
➜ ~ ruby predefined2.rb
Ruby
pid 507 exit 0
pid 508 exit 1
第一个子进程退出状态为0,第二为1。
$;变量存储了字符串split方法的默认分隔符。
#!/usr/local/rvm/rubies/ruby-2.3.0/bin/ruby
str = "1,2,3,4,5,6,7"
p str.split
$; = ","
p str.split
我们使用$;变量来控制字符串的split方法是如何分隔的。这个方法接收一个参数,用于确定字符串应该何处分隔。 如果这个参数省略了,那么将会使用$;的值。
$; = ","
p str.split
我们为$;分隔符指定个值。当split方法没有传递参数时,$;的值将会被使用。
➜ ~ ruby predefined3.rb
["1,2,3,4,5,6,7"]
["1", "2", "3", "4", "5", "6", "7"]
在第一种情况下字符串没有被分割,第二种情况下字符串正确的被分割了。
最后我们展示三个用于正则表达式的全局预定义变量。
#!/usr/local/rvm/rubies/ruby-2.3.0/bin/ruby
"Her name is Jane" =~ /name/
p $`
p $&
p $'
当我们对字符串使用=~操作符时,Ruby设置了一些变量。$&变量设为最后一个匹配该正则式的内容。$`设为$&之前的内容。$‘为$&之后的内容。
➜ ~ ruby predefined4.rb
"Her "
"name"
" is Jane"
在这一章的教程中我们深入的学习了Ruby的变量。zetcode原文地址