您不能像现在一样从lambda 直接将yield 从yield 传递给传递给它的非显式块(请参阅下面的cmets 以获取@cremno 提供的源代码,表明yield 只能用于方法和单例类定义)
来源提取:
if ((type != ISEQ_TYPE_METHOD && type != ISEQ_TYPE_CLASS) || block == 0) {
rb_vm_localjump_error("no block given (yield)", Qnil, 0);
}
主要解决方案:
你可以用这样的显式块来做到这一点
f = lambda do |data,&block|
data.select do |m|
block.call(m)
end.map do |m|
[m, block.call(m)]
end
end
d = ["string",1,2,3,4,"string2"]
f.call(d) { |n| n.is_a?(String) }
#=>[["string", true], ["string2", true]]
次要解决方案:
但是你也可以在lambda 中使用yield,如果块被传递给方法,例如
def some_method(data)
#extended syntax for readability but works fine as
#f = lambda{|data,&block| data.select{|m| block.call(m) }.map { |m| [m, block.call(m)]}}
f = lambda do |data|
data.select do |m|
yield(m)
end.map do |m|
[m, yield(m)]
end
end
f.call(data)
end
some_method(["string",1,2,3,4,"string2"]) { |s| s.is_a?(String) }
#=> [["string", true], ["string2", true]]
Tertiary Soultion:(与您的问题更接近的二级解决方案的产物)
定义辅助方法
def some_method(some_data)
x = filter(some_data) {|m| m.is_a?(String)}
y = filter(some_data) {|m| m.is_a?(Fixnum)}
[x, y]
end
def filter(data)
data.select do |m|
yield(m)
end.map do |m|
[m, yield(m)]
end
end
some_method(["string",1,2,3,4,"string2"])
#=>[
[["string", true], ["string2", true]],
[[1, true], [2, true], [3, true], [4, true]]
]
四元解:
从技术上讲,还有第四个选项,我发布它只是为了您,因为它尽可能地代表您的原始代码。这是迄今为止 4 中最奇怪的模式(几乎和 quaternary 这个词一样奇怪),但我喜欢彻底:
def some_method(some_data)
f = lambda{|data| data.select{|m| yield(m) }.map { |m| [m, yield(m)]}}
if block_given?
f.call(some_data)
else
x = some_method(some_data) {|m| m.is_a?(String)}
y = some_method(some_data) {|m| m.is_a?(Fixnum)}
[x,y]
end
end
some_method(["string",1,2,3,4,"string2"])
#=>[
[["string", true], ["string2", true]],
[[1, true], [2, true], [3, true], [4, true]]
]