Ben*_*ton 3 ruby lambda functional-programming
我正在编写一种方法,该方法将结构数组拆分为几个不同的数组,同时还消除了具有 nil 值的元素。
我想写的是:
def my_func(some_data)
f = lambda{|data| data.select{|m| yield(m).present? }.map { |m| [m.date, yield(m)]}}
x = f.call(some_data) {|m| m.first_var}
y = f.call(some_data) {|m| m.second_var}
return x, y
end
Run Code Online (Sandbox Code Playgroud)
这似乎不起作用,因为 Ruby 没有以我期望的方式处理 lambda 内部的产量。
LocalJumpError:没有给出块(产量)
有没有办法定义 f 以便 x 和 y 给出我正在寻找的结果?
您不能像现在一样直接yield从 alambda传递给它的非显式块(请参阅下面的评论以获取@cremno 提供的源代码,该源代码显示yield可能仅用于方法和单例类定义)
来源提取:
if ((type != ISEQ_TYPE_METHOD && type != ISEQ_TYPE_CLASS) || block == 0) {
rb_vm_localjump_error("no block given (yield)", Qnil, 0);
}
Run Code Online (Sandbox Code Playgroud)
主要解决方案:
你可以用一个像这样的显式块来做到这一点
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]]
Run Code Online (Sandbox Code Playgroud)
次要解决方案:
但是你也可以yield在lambdaif 块被传递给方法而不是例如
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]]
Run Code Online (Sandbox Code Playgroud)
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]]
]
Run Code Online (Sandbox Code Playgroud)
四元溶液:
从技术上讲,还有第四个选项,我发布它只是为了您,因为它尽可能地代表您的原始代码。这是迄今为止最奇怪的模式(几乎和四元这个词一样奇怪),但我喜欢彻底:
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]]
]
Run Code Online (Sandbox Code Playgroud)