如何将Ruby迭代器作为参数传递?

时间:2010-10-20 21:56:08

标签: ruby block yield

我想编写一个方法,在一个地方产生值,并将其作为参数传递给另一个将使用块调用它的方法。我确信它可以完成,但不知怎的,我无法找到正确的语法。

这里有一些示例(非工作)代码来说明我想要实现的目标:

def yielder
  yield 1
  yield 2
  yield 3
end

def user(block)
  block.call { |x| puts x }
end

# later...
user(&yielder)

$ ruby x.rb
x.rb:2:in `yielder': no block given (yield) (LocalJumpError)
from x.rb:12:in `<main>'

FWIW,在我的真实代码中,yielder和用户属于不同的类。


更新

感谢您的回答。正如Andrew Grimm所提到的,我希望迭代器方法获取参数。我的原始例子留下了这个细节。此代码段提供了一个最多可以计算给定数量的迭代器。为了使它工作,我使内部块明确。它做我想要的,但它有点难看。如果有人能够改进这一点,我会非常有兴趣看到它。

def make_iter(upto)
  def iter(upto, block)
    (1 .. upto).each do |v|
      block.call(v)
    end
  end
  lambda { |block| iter(upto, block) }
end

def user(obj)
  obj.call Proc.new { |x| puts x }
end

# later...
user(make_iter(3))

4 个答案:

答案 0 :(得分:4)

这不使用lambda或unbound方法,但它是最简单的方法......

def f
  yield 1
  yield 2
end

def g x
  send x do |n|
    p n
  end
end

g :f

答案 1 :(得分:2)

当您编写&yielder时,您正在调用yielder,然后尝试对结果应用&(convert-to-Proc)运算符。当然,在没有阻止的情况下调用yielder是不行的。你想要的是获得方法本身的引用。只需将该行更改为user(method :yielder)即可。

答案 2 :(得分:1)

我认为这可能与你想做的事情有关:

def yielder
  yield 1
  yield 2
  yield 3
end

def user(meth)
  meth.call { |x| puts x }
end

# later...
user( Object.method(:yielder) )

此处提供了一些相关信息:http://blog.sidu.in/2007/11/ruby-blocks-gotchas.html

答案 3 :(得分:0)

正如已经指出的,基线问题是,当您尝试将函数作为参数传递给Ruby时,Ruby会执行该函数–括号中的副作用是可选的。

我喜欢前面提到的symbol方法的简单性,但是我担心自己将来会忘记,需要将迭代器作为一种符号来使之起作用。为了使可读性成为所需的功能,您可以将迭代器包装到一个对象中,从而可以传递该对象,而不必担心意外执行代码。

作为迭代器的匿名对象

也就是说:使用仅具有一个功能的匿名对象作为迭代器。立即阅读和理解。但是由于Ruby处理范围的方式的限制,迭代器无法轻松接收参数:在函数iterator中接收的任何参数在each中不会自动可用。

def iterator
    def each
        yield("Value 1")
        yield("Value 2")
        yield("Value 3")
    end
end

def iterate(my_iterator)
    my_iterator.each do |value|
        puts value
    end
end

iterate iterator

将对象作为迭代器

使用Proc对象作为迭代器,可以轻松使用传递给 iterator构造函数的所有变量。阴暗面:这开始看起来很奇怪。对于未经训练的眼睛来说,读取Proc.new块不是立即的。另外:无法使用yield会使恕我直言有点难看。

def iterator(prefix:)
    Proc.new { |&block|
        block.call("#{prefix} Value 1")
        block.call("#{prefix} Value 2")
        block.call("#{prefix} Value 3")
    }
end

def iterate(my_iterator)
    my_iterator.call do |value|
        puts value
    end
end

iterate iterator(prefix: 'The')

Lambda作为迭代器

这是理想的选择,以使您难以对代码进行混淆,以至于除了您之外,其他人都无法阅读它。

def iterator(prefix:)
    -> (&block) {
        block.call("#{prefix} Value 1")
        block.call("#{prefix} Value 2")
        block.call("#{prefix} Value 3")
    }
end

def iterate(my_iterator)
    my_iterator.call do |value|
        puts value
    end
end

iterate iterator(prefix: 'The')

作为迭代器的类

最后是好的OOP方法。有点冗长,无法根据我的口味进行初始化,但是效果很少或没有。

class Iterator
    def initialize(prefix:)
        @prefix = prefix
    end

    def each
        yield("#{@prefix} Value 1")
        yield("#{@prefix} Value 2")
        yield("#{@prefix} Value 3")
    end
end

def iterate(my_iterator)
    my_iterator.each do |value|
        puts value
    end
end

iterate Iterator.new(prefix: 'The')