2014-10-17 162 views
0

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) 

我读的文档,但不知道我是否应该尝试lambda表达式,特效,或拨打电话(我只熟悉从其他语言调用的概念)

回答

7

您可以用两种不同的方式做到这一点:

  1. 传递方法的名称(通常为一个符号,而是一个字符串,将工作太):

    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")

  2. 使用块:

    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新手来说很具挑战性,并且大部分都是关于它们的 - 只是谷歌“Ruby block proc lambda”。这里有一个很好的文章,让您开始:http://awaxman11.github.io/blog/2013/08/05/what-is-the-difference-between-a-block/

3

这是通过块传递方法完成的。这有几种语法。

第一个语法使用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} 

输出t结果是一样的。典型地,yield语法是优选的,因为它更简洁,但也因为它的平均速度约为标记的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") 

您也可以使用lambdaProc.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

的字符串

1

一般在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 }是一样的Ruby array.each(&method(:puts))

1

函数不是第一类对象...

这只是是不正确的。首先,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)) 
+0

感谢您的回答最大!你如何使用'my_function。()'而不是'my_function.call()'? – 2014-10-17 20:42:33

+1

没有特别的理由。它做同样的事情,只是更短。 – Max 2014-10-17 20:45:09

相关问题