【问题标题】:Declaring a Ruby lambda with a yield用 yield 声明一个 Ruby lambda
【发布时间】:2015-08-19 09:54:52
【问题描述】:

我正在编写一个方法,它将结构数组拆分为几个不同的数组,同时还消除具有 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

这似乎不起作用,因为 Ruby 没有按照我期望的方式处理 lambda 中的产量。

LocalJumpError: no block given(yield)

有没有办法定义 f 以便 x 和 y 给出我正在寻找的结果?

【问题讨论】:

  • 这将有助于查看一些示例输入和所需的输出。
  • 你怎么打电话给my_func
  • @engineersmnky 给出的例子很好地涵盖了它。

标签: ruby lambda functional-programming


【解决方案1】:

您不能像现在一样从lambda 直接将yieldyield 传递给传递给它的非显式块(请参阅下面的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]]
   ]

【讨论】:

  • 绝对不可能。 yield 仅在方法 (def) 和单例类 (class <<) 作用域 (see) 中直接有效。
  • @cremno 更新了我的帖子,如果将 lambda 的块传递给 yieldyield ,那么您显然可以在方法内从 lambda 中获得 yield。因此,您不能将 yield 直接传递给 lambda 的块。
  • 是的,你可以做到。抱歉,我的描述不够准确。
猜你喜欢
  • 2019-04-27
  • 2011-03-18
  • 1970-01-01
  • 2011-02-25
  • 1970-01-01
  • 2018-11-01
  • 2012-10-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多