使用 yield 声明 Ruby lambda

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 给出我正在寻找的结果?

eng*_*nky 5

您不能像现在一样直接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)

次要解决方案

但是你也可以yieldlambdaif 块被传递给方法而不是例如

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)