将函数传递给另一个函数并调用它

时间:2014-10-17 18:18:37

标签: ruby

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(我只熟悉其他语言的调用概念)

4 个答案:

答案 0 :(得分:8)

您可以通过两种不同的方式执行此操作:

  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新手来说是个挑战,而且很多人都写过它们 - 只是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")

您还可以使用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

字符串

答案 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))