Ruby嵌套块方法调用顺序异常:如何控制meth_block执行时机?
运行原理:Ruby的参数求值顺序
你看到的现象是Ruby方法参数的求值规则导致的:Ruby在调用方法时,会先把所有实参(包括嵌套的方法调用)完整求值,再把求值结果传递给目标方法执行。
具体到你的代码里:
meth(meth_block, attr: 'third')
这行代码执行时,Ruby会先处理第一个位置参数meth_block——也就是直接调用meth_block方法。meth_block内部调用了meth attr: 'fourth' do 'Hello' end,这个调用会先完成并返回'Hello';之后Ruby才会把这个返回值作为content参数,连同attr: 'third'关键字参数一起,传给外层的meth方法执行。这就是为什么你会看到meth_block对应的meth调用先输出,然后才是带attr: 'third'的meth调用输出。
控制调用顺序的方法
如果想让外层带attr: 'third'的meth先执行,再触发meth_block的调用,核心是延迟meth_block的求值时机,不要让它作为直接参数被提前执行。常见实现方式有两种:
方式1:把meth_block的调用放到块里
修改run方法的内层调用:
def run meth attr: 'first' do meth attr: 'second' do # 把meth_block放到外层meth的块中,延迟到yield时执行 meth(attr: 'third') do meth_block end end end end
这样外层meth(attr: 'third')会先执行,当它走到yield时才会调用meth_block,实现顺序反转。
方式2:用Proc包装meth_block实现延迟求值
如果需要把meth_block的结果作为content参数,但要延迟到外层meth内部才求值,可以把meth_block包装成Proc,同时修改meth方法支持处理Proc类型的content:
首先修改meth方法:
def meth(content = nil, **kwargs, &block) p 'content: ' p content p 'kwargs: ' p kwargs p 'block: ' p block if content.is_a?(Proc) content.call # 延迟执行Proc包装的内容 elsif content proc { content }.call elsif block_given? yield end end
然后修改run里的调用:
def run meth attr: 'first' do meth attr: 'second' do # 用proc包装meth_block,避免提前求值 meth(proc { meth_block }, attr: 'third') end end end
这样外层meth(attr: 'third')会先执行,当它处理content参数时才会调用proc { meth_block }.call,触发meth_block的执行。
内容的提问来源于stack exchange,提问作者hahander

