我想编写一个方法,在一个地方产生值,并将其作为参数传递给另一个将使用块调用它的方法。我确信它可以完成,但不知怎的,我无法找到正确的语法。
这里有一些示例(非工作)代码来说明我想要实现的目标:
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))
答案 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')