Skip to content

Latest commit

 

History

History
806 lines (531 loc) · 20.8 KB

4.variables.md

File metadata and controls

806 lines (531 loc) · 20.8 KB

变量

在这章的教程中我们将详细介绍变量。

变量是保存数据的地址。每个变量都有唯一的一个名字,变量命名存在着一些约定。变量保存着数据对象,更确切的说变量是对数据所在的计算机内存地址的引用。每一个对象都具有一定的数据类型,内置的类型或者自定义的类型。Ruby属于动态语言,与Java、C或者Pascal之类的强类型语言不同,动态语言不用为变量定义确切数据类型,而是解释器在分配变量时决定它的类型。在Ruby中程序运行过程变量可以包含不同类型的不同值。

#!/usr/bin/ruby

i = 5
puts i
i = 7
puts i

变量是可变的,与常量不同,在程序运行过程中它们可以保存不同的值。在上面这个例子中有一个名为i的变量,首先赋值为5,然后又改为7。

命名约定

与其他语言一样,Ruby对变量标识符也有些命名约定。
Ruby是区分大小写的,这意味着ageAge是两个变量名。许多语言都是区分大小写的,但是BASIC例外。我们改变字符的大小写可以创建不同的变量,但是不推荐这种做法。

#!/usr/bin/ruby

i = 5
p i
I = 7
p I

这个例子定义了两个变量Ii,它们保存了不同的值。

./case.rb
5
7

以上是case.rb脚本的输出结果。


在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

这个脚本展示三个具有描述性的变量名。对于程序员来说place_of_birth比其他的名字如pob更具有描述性。在循环的时候通常选择一个比较简单的变量名。

印章(Sigils)

变量标识符可以以一些特殊的印章(Sigils)符号开头。印章(Sigils)是附加在标识符上的符号。在Ruby中变量印章(Sigils)表示了变量的作用域范围。在Perl中它是表示数据的类型。Ruby的变量印章(Sigils)符号有*$@*。

#!/usr/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类的实例。

$ ./sigils.rb
[:tree_name]
true
[:@sea_name]
[:@@species]

以上是这个例子的输出结果,我们看到了变量的符号名。

局部变量

局部变量是只在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
./locals.rb:11: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

我们创建了一个局部变量,它的值为5。这个变量的局部作用范围是main区域。它在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方法接收两个参数。它们是矩形的边长,然后我们计算它的面积。对应于标识符ab的两个局部变量将自动创建了。我们调用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脚本中我们创建了三个方法。method2method3是内嵌方法。method2定义在method1内部,method3又定义在method2内部。每一个方法的局部变量仅在这个方法内是可访问的。

$ ./lms.rb
Level 1
m1
m2
Level 2
m3
m4
Level 3
m5
m6

从输出结果我们可以知道method1有两个局部变量m1m2。内嵌方法method2的局部变量有m3m4。最内部的方法method3的局部变量是m5m6


这一节的最后一个例子将展示一些局部作用域的示范。

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

模块是一个方法和常量的集合。我们创建了两个局部变量m1m2

def method1
    v, w = 3
    puts "Inside method"
    puts local_variables
end

method1方法中创建了两个局部变量vw

class Some
    x, y = 2
    puts "Inside class"
    puts local_variables
end 

Some类中创建了两个局部变量xy

t1, t2 = 7

最后我们为Ruby顶级环境创建两个局部变量。

$ ./locals3.rb
Inside module
m1
m2
Inside class
x
y
Inside method
v
w
Inside toplevel
t1
t2

输出结果展示了各个作用域的局部变量。

全局变量

全局变量可以在脚本的任何地方访问到。它们以*$*符号开头。
全局变量可能会引起很多程序错误,因此不鼓励使用全局变量。除非有原因要使用,否则建议使用局部变量。

#!/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

在顶级环境下打印全局变量的值,并检查这个变量是否为全局变量。

$ ./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这个全局变量。这个变量列出了require方法会搜索的所有目录。$:$LOAD_PATH*的缩写。
更多的全局变量会在本章的变量预定义这节中介绍。

实例变量、类变量

在这节将简要的介绍下实例变量和类变量。它们将会在面向对象那一章详细介绍。
实例变量是属于具体某个对象实例的变量。每个对象都有它自己的变量。实例变量以*@符号开头。类变量属于特定某个类的。这个类所创建的对象实例共享这个类的类变量。类变量以@@*符号开头。

#!/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"

p 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 method方法会被调用。这个方法返回这个对象便于人类阅读的描述内容。

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方法中使用,用于返回一个对该对象的简短描述。

p b1, b2, b3

这个方法将刚创建的三个对象作为参数,它将调用每个对象的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值是类变量,它被调用的三次。

环境&命令行变量

可以用ENV常量来访问环境变量。它是一个Ruby的hash对象。每个环境变量都是ENV这个hash对象的键值。

ARGV常量存储了命令行的参数值。它们是在脚本启动时传递的。ARGV是一个数组,参数以字符串存储。S*ARGV的别名。

ENVARGV都是全局常量。

#!/usr/bin/ruby

ARGV.each do |a|
  puts "Argument: #{a}"
end

这个脚本我们通过循环遍历打印了ARGV的每个值。

$ ./commandline.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.utf8
xterm

以上是一个输出例子。

伪变量

在Ruby中有一些变量被称作伪变量。它们不同于常规的变量,不能给它们设置值。
self是当前方法的接收者。nilNilClass的唯一实例,它代表值不存在。trueTrueClass的唯一实例,它代表布尔真。flaseFalseClass是唯一实例,它代表布尔假。
truefalse是布尔数据类型。从另一个角度来看他们是特殊的类实例,这是因为在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 self

在当前上下文self伪变量返回的是main执行的上下文。

$ ./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顶级环境。

$ ./pseudoself.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

上面的例子展示了truefalsenil伪变量。

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

以上是pseudo2.rb脚本的输出结果。

预定义变量

Ruby中有很多预定义的全局变量。这是继承到Perl,Ruby受Perl的影响很大。Ruby脚本启动之后就可以访问这些变量了。接下来有些例子展示预定义变量。

#!/usr/bin/ruby

print "Script name: ", $0, "\n"
print "Command line arguments: ", $*, "\n"

puts "Process number of this script: #{$$}"

以上使用了三个预定义变量。$0$*$$$0存储了当前脚本的名字。*$**存储了命令行参数。 *$$*存储了当前脚本程序的PID。

$ ./predefined.rb 1 2 3
Script name: ./predefined.rb
Command line arguments: ["1", "2", "3"]
Process number of this script: 3122

以上是一个输出例子。


*$?*全局变量存储了最后一个子进程的退出状态。

#!/usr/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* 操作符,用于执行一条被分隔符所选择的命令。

$ ./predefined2.rb
Ruby
pid 3131 exit 0
pid 3133 exit 1

第一个子进程退出状态为0,第二为1。


$;变量存储了字符串split方法的默认分隔符。

#!/usr/bin/ruby

str = "1,2,3,4,5,6,7"
p str.split

$; = ","
p str.split

我们使用*$;变量来控制字符串的split方法是如何分隔的。这个方法接收一个参数,用于确定字符串应该何处分隔。 如果这个参数省略了,那么将会使用$;*的值。

$; = ","
p str.split

我们为*$;分隔符指定个值。当split方法没有传递参数时,$;*的值将会被使用。

$ ./predefined3.rb
["1,2,3,4,5,6,7"]
["1", "2", "3", "4", "5", "6", "7"]

在第一种情况下字符串没有被分割,第二种情况下字符串正确的被分割了。


最后我们展示三个用于正则表达式的全局预定义变量。

#!/usr/bin/ruby

"Her name is Jane" =~ /name/

p $`
p $&
p $'

当我们对字符串使用*=~保用符时,Ruby设置了一些变量。$&变量设为最后一个匹配该正则式的内容。$`设为$&之前的内容.$’$&*之后的内容。

$ ./predefined4.rb
"Her "
"name"
" is Jane"

以上是这个例子的输出结果。

在这一章的教程中我们深入的学习了Ruby的变量。


原文地址: http://zetcode.com/lang/rubytutorial/variables/
翻译:龙昌 admin@longchangjin.cn
完整教程:https://github.com/wusuopu/Ruby-tutorial