Ruby变量作用域规则详解
Ruby的变量作用域规则
这里只考虑局部变量。
1.顶层范围看起来是全局范围,但它是main的局部作用域而不是全局作用域,所以方法内部、类内部、模块内部无法访问顶层变量。
2.局部变量有一个特性非常重要:必须先声明才能引用。
1 | a=3 |
3.方法内部不能访问方法外部的局部变量,因为方法内部是一个独立的作用域。
这种行为和其它语言可能不同。这种行为导致的结果是:
- 在方法内部只能访问在方法内部作用域中已经声明好的局部变量
- 要访问方法外部变量,只能通过参数传递的方式
这是一种”邻国相望,鸡犬之声相闻,民至老死,不相往来“的一种表现,内外部方法的变量作用域互不可见。
例如:
1 | a=5 # 这是局部变量 |
因内部函数无法访问外部变量,这产生了一系列的连锁反应:
- Ruby中的函数不是一等公民,无法返回一个函数、无法将函数作为参数传递
- Ruby中的函数无法直接实现闭包
- Ruby中的函数和匿名函数(严格地应该称为lambda)是不一样的,Ruby中的匿名函数是一种特殊的Proc对象,而非一种没有名称的函数
Ruby中的Proc或(方法式)语句块取代了函数作为一等公民,它可以用于实现闭包,Proc或语句块是一种更为抽象的代码封装结构。
4.方法式代码块内部能访问代码块外部的变量。
方法式代码块经常会应用于一些迭代方法中,比如each() {}、times() {}等,这意味着这部分方法式代码块会在每次迭代过程中被调用一次,相当于多次调用匿名函数,而每次函数调用对于函数内部也即代码块内部来说,作用域是独立的。所以方法式代码块的迭代控制变量、代码块内部赋值的变量都会在每次迭代调用过程中新建。
例如,下面方法式代码块中的控制变量x以及代码块内部的变量y,在每次迭代过程中都是新创建并初始化的新变量,所以y+=1
是多余的。
1 | 10.times {|x| y=0;puts x;y+=1} |
虽然代码块内部创建的变量是新的变量,也是代码块内部的局部变量(显然如此),但方法式代码块可以访问并修改外部变量,这和函数定义的代码块很不同。这是一种”你的就是我的,我的还是我的“的表现。
例如:
1 | a=5 |
更深入的,方法式语句块在迭代的时候,每次迭代都会创建新的语句块变量。这一点在了解了Proc对象和call()之后将理解的更清晰。例如:
1 | (1..10).each {|x| puts x} |
上面的代码中,其实内部声明了10次x变量,每次迭代过程中的x变量都不是同一个。
再例如,下面的两段代码中都通过循环将匿名函数加入到数组中,但是两种迭代方式会导致匿名函数的执行结果不同:
1 | def f() |
每次迭代执行代码块(Proc对象),等价于下面代码的执行方式。所以方法式语句块有自己的变量作用域,语句块变量也是独立的。
1 | p1 = Proc.new {|x| puts x} |