【问题标题】:Trouble yielding inside a block/lambda在块/lambda内产生问题
【发布时间】:2011-06-26 08:10:21
【问题描述】:

我有以下 Ruby 代码:

# func1 generates a sequence of items derived from x
# func2 does something with the items generated by func1
def test(x, func1, func2)
    func1.call(x) do | y |
        func2.call(y)
    end
end

func1 = lambda do | x |
    for i in 1 .. 5
        yield x * i
    end
end

func2 = lambda do | y |
    puts y
end


test(2, func1, func2) # Should print '2', '4', '6', '8', and '10'

这当然行不通。

test.rb:11: no block given (LocalJumpError)
    from test.rb:10:in `each'
    from test.rb:10
    from test.rb:4:in `call'
    from test.rb:4:in `test'
    from test.rb:20

【问题讨论】:

标签: ruby lambda block yield


【解决方案1】:

Lambda 不会像常规方法那样隐式接受块,因此您的 func1 无法产生。改为这样做:

func1 = lambda do |x, &blk|
  for i in 1 .. 5
    blk.call(x * i)
  end
end

具体来说,我认为这是因为 yield 会将控制权发送回caller 的块,其中不包括 lambda 调用。所以下面的代码就像你“期望”的那样工作:

def foo
  (lambda { |n| yield(n) }).call(5)
end
foo { |f| puts f }  # prints 5

【讨论】:

    【解决方案2】:

    仅在 Ruby 1.9 中:

    func1 = lambda do |x, &blk|
      for i in 1..5
        blk.call(x*i)
      end
    end
    

    【讨论】:

    • @wuputah:你是对的。我在 1.8.7 NEWS 文件中看到了这个特性的介绍。然而,它伴随着一个警告“当前形式的这个实现已知存在错误/损坏,尤其是在嵌套块调用时。将此作为实验性功能。”
    • @Ken Bloom:很好,我不知道!没有什么能比得上稳定 Ruby 版本中的实验性功能。
    • @wuputah:Ruby 1.8.7 中的大量更改是引起 Ruby 社区恐慌的一大原因。我不知道 1.8.7 中存在破坏性的实验性更改,但考虑到他们首先对 1.8.7 进行如此多的更改是不负责任的,我想他们也会加入一些破坏性的实验性更改也就不足为奇了. (我不得不说,看完这个之后,我同意 Debian Ruby 打包者的说法,即 Ruby 的开发实践与实际将代码分发到 Linux 发行版中的良好实践不符方式 .)
    • 由于这个功能在 v1.8.7 中是实验性的,我应该尽量避免它?例如,让func1 返回一个包含其生成值的数组可能会更好,我将在其中分别对数组值运行func2?你应该明白,这实际上是一个更复杂算法的简化版本。
    • @AJM:我强烈考虑返回一个生成值数组,除非存在严重的内存压力阻止这种情况。它不仅避免了实验性语言功能,而且可能更清楚正在发生的事情。也许您可以分享更复杂的算法,以便我们帮助您做出决定?
    【解决方案3】:
    def test(x, func1, func2)
        func1.call(x) do | y |
            func2.call(y)
        end
    end
    
    #change func1 to a method
    def func1 x
        for i in 1 .. 5
            yield x * i
        end
    end
    
    #func2 may be either a method or a lambda
    #I changed it for consistency, but you don't have to
    def func2 y
        puts y
    end
    
    
    test(2, method(:func1), method(:func2))
    

    【讨论】:

      【解决方案4】:

      根据 Nikita Misharin 的回答:[https://stackoverflow.com/a/45571976/2165560],我喜欢这个:

      def iterator(x)
        for i in 1 .. 5
          yield x * i
        end
      end
      
      
      iteratorWrapper = -> (m,&block) { iterator(m) {|n| block.call n}  }
      iteratorWrapper.call(2) { |y| puts y }
      

      它在这里回答了我的问题 [In Ruby, can you use the lambda or or Proc call method to invoke an iterator?

      通过包装迭代器,它可以任意传递给其他方法并在它们的块上进行迭代。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-01-01
        • 1970-01-01
        • 2015-02-23
        • 2021-07-08
        • 2017-07-20
        • 2018-06-10
        • 2021-06-29
        • 1970-01-01
        相关资源
        最近更新 更多