Ruby中的函数不是第一类对象,但我希望能够将对函数的引用传递给另一个函数并让它被执行。怎么办呢?
示例:
def run_a_function_twice(my_function)
# Call the function once.
# Call the function again.
end
def say_hello
puts "HI!"
end
run_a_function_twice(say_hello)
我正在阅读文档,但不确定我是否应该尝试lambdas,procs或call(我只熟悉其他语言的调用概念)
答案 0 :(得分:8)
您可以通过两种不同的方式执行此操作:
传递方法的名称(通常作为符号,但字符串也可以):
def run_a_method_twice(method_name)
send(method_name)
send(method_name)
end
run_a_method_twice(:say_hello)
这取决于say_hello
在与run_a_method_twice
相同的范围内可用,例如如果它们都是同一类的实例方法。如果在另一个对象上定义say_hello
,您可以执行以下操作: some_obj.send(:say_hello)
。
您可以通过在方法名称之后将send
提供给方法来为方法提供参数,例如: jordan.send(:say_hello, "Donny")
。
使用块:
def yield_to_a_block_twice
yield
yield
end
yield_to_a_block_twice { say_hello }
此语法也适用:
def call_a_block_twice(&block)
block.call
block.call
end
您应尽可能使用yield
(速度更快),但有时需要能够按名称引用该块(例如,如果您需要将其传递给另一种方法,或从另一个块中调用它),在这种情况下,使其成为命名参数(即def meth(arg1, arg2, &block_name)
)是必要的。
block,Proc和lambda之间的区别对于Ruby新手来说是个挑战,而且很多人都写过它们 - 只是google" Ruby block proc lambda。"这是一篇非常好的文章,可以帮助您入门:http://awaxman11.github.io/blog/2013/08/05/what-is-the-difference-between-a-block/
答案 1 :(得分:4)
这是通过块传递方法完成的。有几种语法。
第一种语法使用yield,看起来像这样。
def method1
puts "This is from method 1"
yield
end
def method2
puts "This is from method 2"
end
method1(){method2}
以上将输出
这来自方法1
这是方法2
第二个选项使用以下语法
def method1(&block)
puts "This is from method 1"
block.call
end
def method2
puts "This is from method 2"
end
method1(){method2}
输出结果相同。通常情况下,yield
语法是首选,因为它更简洁,但也因为它的平均速度是block.call
表示法的5倍。
第三个选项是使用send
语法,如下所示。
def method1(method_name_string)
puts "This is from method 1"
send(method_name_string, 1, 2)
end
def method2(a,b)
puts "This is from method 2" + a.to_s + ' ' + b.to_s
end
method1("method2")
您还可以使用lambda
或Proc.new
def method1(lmb)
puts "This is from method 1"
block.call "A string"
end
foo = lambda do |x|
puts x
end
method1(foo)
在这种情况下,你会看到
这来自方法1
字符串
答案 2 :(得分:2)
通常在ruby中有块的概念,如果你愿意的话,它们是匿名方法(闭包)。
您可以将块传递给任何方法。然后,该方法可以使用yield
(与block_given?
结合使用)来调用方法/块,或者使用&
运算符将其引用到变量。后者可用于存储引用或将其传递给另一种方法。
def call_it_twice
2.times {|i| yield(i) }
end
call_it_twice { puts "hello" }
# hello
# hello
call_it_twice {|i| puts "hello #{i}" }
# hello 0
# hello 1
def call_it_thrice &block
call_it_twice(&block)
block.call(2)
end
call_it_thrice {|i| puts "hello #{i}" }
# hello 0
# hello 1
# hello 2
您也可以传递一个文字方法,但这种方法并不常见。
class Foo
def hello
puts "world"
end
end
Foo.instance_methods(:hello)
# #<UnboundMethod: Foo#hello>
Foo.instance_method(:hello).call
# NoMethodError: undefined method `call' for #<UnboundMethod: Foo#hello>
Foo.instance_method(:hello).bind(Foo.new).call
# world
Foo.new.method(:hello)
# #<Method: Foo#hello>
Foo.new.method(:hello).call
# world
常见的做法是将array.map{|x| x.downcase }
写为array.map(&:downcase)
(这是to_proc
的快捷方式,因此它是幕后的array.map(&:downcase.to_proc)
。
相对未知但是:array.each{|x| puts x }
与array.each(&method(:puts))
答案 3 :(得分:1)
Ruby中的函数不是第一类对象......
这不是真的。首先,Ruby不真正具有功能。定义“函数”时,Ruby实际上会向Object
添加一个实例方法。这与定义函数具有相同的效果,因为一切都是对象,因此始终可以调用该方法。
其次,方法是第一类对象,它们只是稍微难以访问,因为Ruby总是认为你想要调用方法而不是直接访问方法对象。 method
方法可以获得对象。
def run_a_function_twice(my_function)
2.times { my_function.() }
end
def say_hello
puts "HI!"
end
run_a_function_twice(method(:say_hello))