1

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

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

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 和 user 属于不同的类。


更新

感谢您的回答。正如 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

4 回答 4

4

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

def f
  yield 1
  yield 2
end

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

g :f
于 2010-10-20T22:30:59.220 回答
2

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

于 2010-10-20T22:31:19.583 回答
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

于 2010-10-21T02:37:24.387 回答
0

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

我喜欢之前提到的符号方法的简单性,但我害怕我未来的自己会忘记需要将迭代器作为符号传递才能使其工作。由于可读性是一个理想的特性,你可以将你的迭代器包装到一个对象中,你可以传递它而不用担心代码会被意外执行。

匿名对象作为迭代器

即:使用只有一个函数的匿名对象作为迭代器。非常容易阅读和理解。但是由于 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 对象作为迭代器

使用Proc对象作为迭代器可以让您轻松使用传递给迭代器构造函数的任何变量。阴暗面:这开始看起来很奇怪。对于未经训练的眼睛来说,阅读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')

类作为迭代器

最后是好的 ol' 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')
于 2018-09-27T16:00:08.427 回答