可能有一种简单的方法可以做到这一点。
我正在尝试重构以下内容
def foo(baz)
baz.update_first
if baz.has_condition?
yield baz.val if block_given?
baz.a
else
baz.b
end
end
称为
foo(baz) {|b| b.modify}
类似
def foo(baz)
baz.update_first
bar(baz) {|i| yield i if block_given? }
end
def bar(baz)
if baz.has_condition?
yield baz.val if block_given?
baz.a
else
baz.b
end
end
那会有用吗?怎么样?
我认为它会,但我很欣赏一个清楚解释如何让一个块内的让步工作...通过proc.c和vm.c以及relevant git commit in the ruby source code阅读,我认为当调用bar时foo它执行直到它产生,然后你走向帧堆栈到foo中定义的块的本地环境指针,这被调用,其中yield产生走向块foo被调用,执行它,然后你是回到酒吧。那是对的吗?有更好的方法吗?
这对我来说感觉有点怪异,比如反转控制,并且需要foo更多地了解baz,但我很遗憾不能简单地在此代码中传递proc或lambda。
答案 0 :(得分:3)
我认为,如果你看一个替代语法,将bloc转换为proc参数,那么yield
的概念可能会更清楚。
例如,以下示例是相同的
def my_each(arr)
arr.each { |x| yield x }
end
def my_each(arr, &blk)
arr.each { |x| blk.call(x) }
end
# Both are called the same way
my_each([1,2,3]) { |x| print x }
# => 123
使用yield
时,该变量在方法中可用,而不在参数列表中声明它。在参数前加&
符号会将其转换为proc,因此在方法中可以使用.call
运行。
这是一个向一个方法提供块然后在两个范围内执行它的示例:
def method_a(number, &blk)
method_b do
method_c do
blk.call(number)
end
end
end
def method_b(&blk)
blk.call
end
def method_c(&blk)
blk.call
end
method_a(1) { |num| puts num + 1 }
# => 2
请注意,blk
不是一个神奇的词 - 您可以根据需要为变量命名。
这与收益率相同:
def method_a(number)
method_b do
method_c do
yield number
end
end
end
def method_b
yield
end
def method_c
yield
end
method_a(1) { |num| puts num + 1 }
# => 2
我认为使用&blk
语法更清楚,因为它为proc分配了一个变量。仅仅因为在方法中使用了proc并不意味着你必须运行Proc.new
。该块自动转换为proc。