ruby - 从 block 调用者的角度来看,next、break、redo 和 return 是如何工作的?

标签 ruby

因为 Ruby 非常依赖 block 的使用来进行迭代(whilefor 循环在 Ruby 中确实存在,但 Ruby 开发人员在练习)Ruby 允许在 block 内使用一些在其他语言中经常与循环相关的关键字,例如 breaknextredo . Ruby 中的 return 也从 block 所在的方法返回,这与循环的行为一致,而不是像函数式编程语言那样从 block 本身返回。 This answer很好地解释了这些行为。

现在我想知道这种行为是如何工作的从 block 调用者的角度(通过 block 调用者,我的意思是调用 block 的方法,而不是调用 block 的方法定义于)。对于其中一些关键字,它是不言自明的(例如 next 只是从 block 中返回,而 redo 使用相同的参数再次调用该 block ),但对于其他关键字则完全我不清楚行为。具体...

当我从 block 中调用 break 时,执行会立即转到定义 block 后的点吗?如果我要从迭代器方法中执行一些迭代后清理步骤,它们会被完全跳过吗?例如:

def iterate
  setup
  1.upto(5) do |x|
    yield x
  end
  cleanup # Does this get called? If not, how can I ensure that it does?
end

iterate do |x|
  puts x # Prints 1
  break # Break prevents further looping, somehow
end
# Execution continues here after break

return 怎么样?我假设它在这种情况下具有与 break 类似的行为(无论该行为可能是什么)。真的是这样吗?

但更令人困惑的是,Ruby block 可以作为方法参数捕获并转换为 Proc 对象。然后可以从任何地方调用这些对象,甚至可以在 block 最初定义的方法之外:

def get_proc(&block)
  block
end

def get_iterator_proc
  p = get_proc do |x| # Block is defined here...
    puts x
    break
  end
  # ...so `break` makes execution continue here...

  do_other_stuff

  return p
end

p = get_iterator_proc

p.call # ...even though the block isn't actually called until here?

同样,return 也是如此。在这种情况下,它实际上返回了什么?这些关键字真正是如何工作的?

最佳答案

def iterate
  setup
  1.upto(5) do |x|
    yield x
  end
  cleanup # Does this get called? If not, how can I ensure that it does?
end

iterate do |x|
  puts x # Prints 1
  break # Break prevents further looping, somehow
end
# Execution continues here after break

cleanup # Does this get called?

是的。但是,如果有异常,则优先处理异常。

cleanup # If not, how can I ensure that it does?

您可以使用 ensure没有抢救,因为有异常(exception)。

break # Break prevents further looping, somehow

怀疑它使用throw,因为这就是Enumerable 的工作方式。

What about with return? I would assume it has similar behavior to that of break in this context (whatever that behavior may be). Is that indeed the case?

这取决于上下文。您可能会得到一个 LocalJumpError: unexpected return 或者它可能作为一个 break

def get_proc(&block)
  block
end

def get_iterator_proc
  p = get_proc do |x| # Block is defined here...
    puts x
    break
  end
  # ...so `break` makes execution continue here...

有效,但有细微差别。它更像是你的第一个例子。

def get_proc(&block)
  block
  # ...so `break` makes execution continue here...
end

def get_iterator_proc
  p = get_proc do |x| # Block is defined here...
    puts x
    break
  end
  # get_proc returned here (after assigning to p)

请记住,get_proc 只是一个具有特殊语法的函数调用,用于传递函数。

如果你有

def get_proc(&block)
  block
  7
end

然后在 get_iterator_procp 将是 7(我们很遗憾 7 没有调用 方法)

关于ruby - 从 block 调用者的角度来看,next、break、redo 和 return 是如何工作的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24022894/

相关文章:

ruby-on-rails - 如何安全地检索可能不存在的 Ruby 方法的值?

ruby - 通过浏览器进行音频聊天和通话

ruby - 使用正则表达式检查字符串是否以辅音开头

ruby - 在几个类中的一个方法中捕获异常的好方法

ruby-on-rails - 从没有 Ruby 的 Zurb 基础开始?

ruby - 如何检测未经测试的 ruby​​ 文件?

Ruby block 到字符串而不是执行

Ruby - 按值或按引用传递

Ruby/RSpec 随机化似乎不是那么随机

ruby-on-rails - FactoryGirl 创建多条记录