笔记:Ruby控制作用域相关实验

导航关联

Ruby特性和实验分析汇总

默认的情况

Class、Module、def 分别产生作用域门,他们自己的变量仅仅对自己可见。 符合预期。


class A
  ka_a = 'ka a'
  ka_b = 'ka b'

  puts '--Class A'
  pp local_variables

  module B
    mb_a = 'module b var a'
    puts '--Class B'
    pp local_variables
  end

  def c
    puts '--method c'
    pp local_variables
  end
end


A.new.c

# --Class A
# [:ka_a, :ka_b]
# --Class B
# [:mb_a]
# --method c
# []

块和扁平作用域

我们只要把例子简单的修改定义 def的部分换成块模式定义:

top_a = "top_a"
top_b = "top_b"

class A
  ka_a = "ka a"
  ka_b = "ka b"

  puts "--Class A"
  pp local_variables

  module B
    mb_a = "module b var a"
    puts "--Class B"
    pp local_variables
  end

  define_method :c do
    puts "--method c"
    pp local_variables
  end
end

A.new.c

puts "--top-level--"
pp local_variables

# output >>>>>
# --Class A
# [:ka_a, :ka_b]
# --Class B
# [:mb_a]
# --method c
# [:ka_a, :ka_b]
# --top-level--
# [:top_a, :top_b]

块实际上是函数+所在环境的绑定(也叫做闭包)。

参考《Ruby原理剖析》,实际上在Ruby内部块是一个结构体,保存了 所在环境的EP(环境指针Environment Pointer),那么在解析变量的时候,可以看到外面一层。

这个实验中,c实例看不到 top level(最外层称为 top level)足够说明问题,块只能绑定他所在的这一层作作用域。

块的这种能力在《Ruby元编程》里称为 打破作用域门(Scope Gate)。也叫扁平作用域,看起来就像 块和外部的作用域压平在一起了。

嵌套块 传递作用域

还是把刚才的例子简单变化一下,A类用 Class.new 和块的方式生存。这时候 A和c是嵌套的。

A 携带了环境绑定,c 也绑定到了所定义的A里。作用域可见性实现了一种传递。

词法作⽤域: 词法作⽤域就是指作⽤域是由代码中函数声明的位置来决定的,所以词法作⽤域是静态的作 ⽤域,通过它就能够预测代码在执⾏过程中如何查找标识符。

这个方式可以理解为词法作用域,块的作用域就是代码位置所在之处的作用域。

在执行的底层,块的结构体中 EP指针,指向的是当前执行栈栈帧。结果是一样的。(词法和栈帧的结果一致性的判断我不知道对不对。结果上来看是这样。)


top_a = "top_a"
top_b = "top_b"

A = Class.new do
  ka_a = "ka a"
  ka_b = "ka b"

  puts "--Class A"
  pp local_variables

  module B
    mb_a = "module b var a"
    puts "--Class B"
    pp local_variables
  end

  define_method :c do
    puts "--method c"
    pp local_variables
  end
end

A.new.c

puts "--top-level--"
pp local_variables

共享作用域

这段例子展示的是 一个def 里面有两个 块在一个作用域内部,他们之间可以共享变量,从结果上可以看出来。

而且,由于最外层 def的存在,建立了一个作用域门,这意味着外面也无法访问内部。

所以这里起到了一个效果就是两个块在一个私有空间里共享作用域。


def counter_job
  shared_counter = 0

  define_method :inc do
    shared_counter += 1
  end

  define_method :display do
    puts "[Result]: #{shared_counter}"
  end

  Kernel.send :inc
  Kernel.send :inc
  Kernel.send :inc

  Kernel.send :display

  puts "--in counter_job"
  pp local_variables
end

counter_job

puts "--top level--"
pp local_variables

# output >>>>>>
# [Result]: 3
# --in counter_job
# [:shared_counter]
# --top level--
# []

总结

我们知道了 块可以扁平作用域、嵌套的块可以传递作用域、作用域门内部两个块可以共享作用域。顺着这个思路,我们可以通过写法控制代码的作用域了。

Mark24

Everything can Mix.