理解Ruby中procs的返回

理解Ruby中procs的返回,ruby,metaprogramming,internals,Ruby,Metaprogramming,Internals,我想知道如何将一个块传递给一个方法,使该方法在上返回并产生 天真的套路行不通: def run(&block) block.call end run { return :foo } # => LocalJumpError def foo send(:return, 123) end foo #=> undefined method `return' 在另一个过程中包装具有相同的效果: def run(&block) proc { block.cal

我想知道如何将一个块传递给一个方法,使该方法
上返回
并产生

天真的套路行不通:

def run(&block)
  block.call
end

run { return :foo } # => LocalJumpError
def foo
  send(:return, 123)
end

foo #=> undefined method `return'
在另一个过程中包装具有相同的效果:

def run(&block)
  proc { block.call }.call
end

run { return :bar } # => LocalJumpError
因此我认为
return
语句绑定到当前
绑定的
接收者。然而,用
实例评估
尝试证明我错了:

class ProcTest
  def run(&block)
    puts "run: #{[binding.local_variables, binding.receiver]}"
    instance_eval(&block)
  end
end

pt = ProcTest.new
binding_inspector = proc { puts "proc: #{[binding.local_variables, binding.receiver]}" }
puts "main: #{[binding.local_variables, binding.receiver]}"
    # => main: [[:pt, :binding_inspector], main]
binding_inspector.call
    # => proc: [[:pt, :binding_inspector], main]
pt.run(&binding_inspector)
    # => run: [[:block], #<ProcTest:0x007f4987b06508>]
    # => proc: [[:pt, :binding_inspector], #<ProcTest:0x007f4987b06508>]
pt.run { return :baz }
    # => run: [[:block], #<ProcTest:0x007f4987b06508>]
    # => LocalJumpError
类测试
def运行(&block)
放置“运行:{[binding.local_变量,binding.receiver]}”
实例求值(&block)
结束
结束
pt=ProcTest.new
binding_inspector=proc{put“proc:{[binding.local_变量,binding.receiver]}”
放置“main:#{[binding.local_变量,binding.receiver]}”
#=>main:[:pt,:binding_inspector],main]
打电话
#=>proc:[:pt,:binding_inspector],main]
pt.运行(和装订检查)
#=>运行:[[:块],#]
#=>proc:[[:pt,:binding_inspector],#]
pt.run{return:baz}
#=>运行:[[:块],#]
#=>本地跳转错误
因此,问题是:

  • 如何做到这一点
  • 返回上下文如何与
    return
    语句相关联。这个连接可以通过语言的API访问吗
  • 这样做是不是有意的?如果是,为什么?如果没有-解决它的障碍是什么

  • return
    在块中,定义块时,从封闭方法返回(即创建块的闭包)。在您的示例中,没有可从中返回的封闭块,因此出现了异常

    这很容易证明:

    def foo(&block)
      puts yield
      puts "we won't get here"
    end
    
    def bar
      foo { return "hi from the block"; puts "we never get here" }
      puts "we never get here either"
    end
    
    puts bar # => "hi from the block" (only printed once; the puts in `foo` is not executed)
    
    proc中的Return将立即从proc中返回,而不是从proc下堆栈上的方法中返回:

    def foo(&block)
      puts yield
      puts "we will get here"
    end
    
    def bar
      foo &->{ return "hi from the proc"; puts "we never get here" }
      puts "we will get here too"
    end
    
    puts bar
    # hi from the proc      # puts from foo
    # we will get here      # puts from foo
    # we will get here too  # puts from bar
    
    由于这些行为,无法实现所需的行为,在这种情况下,给定块中的
    返回
    将在调用块的方法中执行
    返回
    ,除非该块在该范围内定义,因为这样做需要现有行为之一不起作用

    您可以使用throw…catch实现类似的功能,这对于从任意深度压缩堆栈有一定的帮助,但您不能使用它返回任意值:

    def foo(&block)
      yield
      puts "we won't get here"
    end
    
    catch(:escape) do
      foo &->{ throw :escape }
    end
    
    我认为
    return
    语句绑定到当前
    binding
    receiver

    只有方法有接收器<代码>返回
    不是一种方法:

    defined? return #=> "expression"
    
    尝试将其作为方法调用无效:

    def run(&block)
      block.call
    end
    
    run { return :foo } # => LocalJumpError
    
    def foo
      send(:return, 123)
    end
    
    foo #=> undefined method `return'
    
    instance\u eval
    进行测试证明我错了

    尽管
    instance\u eval
    在接收方上下文中计算块(因此您可以访问接收方实例方法和实例变量):

    。。。它不计算当前绑定中的块(因此您无权访问任何局部变量):

    如何做到这一点

    您可以使用
    eval
    ,但这需要一个字符串:

    def foo
      var = 123
      eval yield
      nil
    end
    
    foo { "return var * 2" }
    #=> 246
    
    或者通过将绑定传递到块(再次使用
    eval
    ):


    你一个问题都没回答。第一点-我知道我得到了异常,因为我试图从主范围
    返回
    ,并且
    返回
    与定义它的范围相关联。关于第二部分-我知道lambda与常规proc有不同的
    return
    语义,问题不在于它们。我也知道关于
    抛出
    -
    捕获
    ,问题是关于理解
    返回
    。这就像是在问为什么草是绿色的?看这片草地,你可以看到它是绿色的,水不是绿色的,你可以用非绿色的颜色来画假草。Ruby没有将块传递给方法的功能,而该方法可以从块内返回到块传递给的方法外,因为这种返回行为与现有的返回行为是互斥的。我不知道为什么它是这样设计的,但是“为什么Ruby没有这个”就是这样回答的,因此Q1得到了回答(你不能),Q3因此是不相关的(这不是一个需要修复的bug,这是一个故意选择的互斥替代方案),Q2将需要进入解析器和VM代码来回答,但答案可能是“上下文没有公开“。你所说的退货行为与现有退货行为以及故意选择相互排斥的备选方案相互排斥是什么意思?如果实现从调用方法返回所需的行为,会破坏哪些行为。调用时,进程执行不是添加到堆栈中吗?为什么Ruby没有这个功能,这意味着这是一个额外的功能,而不是。
    return
    from a block从定义块的帧返回(作为块结构上的帧副本存储)。你必须打破这个行为来获得你想要的行为,这将违反Ruby的块设计,使块能够访问它产生的方法的框架,而不是定义它的闭包。也许您可以详细说明您试图实现的目标?
    return
    在一个块中,非lambda
    Proc
    从封闭的方法返回,在一个方法中,以及lambda从方法/lambda本身返回。记住:
    Proc
    的行为类似于block,因为它们押韵,lambda的行为类似于method,因为它们都是希腊语-D(参数绑定也是如此,顺便说一句,
    Proc
    s绑定参数像块一样,lambdas绑定参数像方法一样。)eval可能是这里最好的解决方案,因为代码在屈服方法的范围内得到求值,但
    eval yield
    似乎非常危险<代码>屈服绑定
    可能是最好的方法。请注意,此代码将被解析、编译成iseq,然后在每次调用时执行,这可能比
    def foo
      var = 123
      yield binding
      nil
    end
    
    foo { |b| b.eval "return var * 2" }
    #=> 246