Ruby 元编程代码的解释

runup · 2016年04月27日 · 最后由 runup 回复于 2016年04月28日 · 2959 次阅读

摘自元编程第二版 101 页,第二版。 完整代码如下:

@setups = [proc{puts "one"}, proc{puts "two"}]

def each_setup(&block)
  @setups.each do |setup|
    block.call setup
  end
end

each_setup do |setup|
  setup.call
end

如何理解:

each_setup do |setup|
  setup.call
end

中的 setup 和

def each_setup(&block)
  @setups.each do |setup|
    block.call setup
  end
end

中的&block 的关系?

对这段代码产生迷糊是因为本身对块的理解不够,下面根据楼下的意见进行解释: 下面的两段代码是等价的:

each_setup do |setup|
  setup.call
end

each_setup { |setup| setup.call }

参数&block 其实就是 { |setup| setup.call },那么 block 就是 proc { |setup| setup.call } 那么上面的 each_setup 的代码可以改成如下形式:

def each_setup(&block)
  @setups.each do |setup|
    proc{|setup| setup.call}.call setup
  end
end

相等关系。setup 将你那两个 block,put one 和 put two,塞到@setups中。each_setup 再迭代@setups,将两个 block 传给{ |setup| setup.call},这才执行那两个 block。

#1 楼 @theblock24block 问题进行了简化和修改,能不能再帮忙看看

each_setup方法接收一个代码块,你可以啥都不做嘛,所以不一定非得和 setup 扯上关系。比如

each_setup do |_setup|
  p 'Hello world'
end

不过感觉就你这里的方法定义和调用可以简化成下面这样,希望有助于理解:

# 方法定义
def each_setup(&block)
  @setups.each do |setup|
    block.call setup
  end
end

# 方法调用
each_setup do |setup|
  setup.call
end

# ===================================
# => 基本等价于下面这样
# 方法定义
def each_setup
  @setups.each do |setup|
    setup.call
  end
end

# 方法调用
each_setup

来人肉运行一下脚本:

each_setup do |setup|
  setup.call
end

调用方法each_setup,参数 (即def each_setup(&block)中的block) 是proc { |setup| setup.call }

然后方法实际运行等同于

block.call proc {puts "one"}
block.call proc {puts "two"}

再写直白一点

(proc {|setup| setup.call}).call(proc {puts "one"})
(proc {|setup| setup.call}).call(proc {puts "two"})

再往后就看 ruby block 相关的文章吧

我认为 each_setup 这个方法难理解的就在于它接收一个代码块,在代码块中又有代码块执行,分两步就可以搞清楚关系了。 举例说明:

lambda_1 = ->(setup) {setup.call}  #这是一个可调用对象,它接收另外一个可调用对象作为参数
lambda_1.call("hehe") # 报错,因为参数不是可调用对象
lambda_1.call(Proc.new {puts "hehe"}) #=> hehe

回到书中的实例代码,在调用 each_setup 的时候挂载了一个代码块,

each_setup do |setup|
  setup.call
end
#这里的setup.call不就相当于上面例子中的setup.call 嘛
#回到定义中,block.call(setup) 等价于
#proc_1 = ->(hehe) {hehe.call}
#proc_1.call(setup) 这里的setup会替换上一行中的hehe,也就是最后执行的是setup可调用对象
def each_setup(&block)
  @setups.each do |setup|
    block.call setup
  end
end
# 这里的setup是数组中的元素,也就是Proc对象,block.call setup 则将这个Proc对象传递给运行each_setup方法时的setup.call
# 基本上就是proc{puts "one"}.call

#3 楼 @qinfanpeng #4 楼 @piecehealth #5 楼 @nju520 #6 楼 @killernova 谢谢楼上各位的指点,这个问题我觉得自己终于搞明白了,已经在原问题中添加了解释。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号