如何在Ruby中引用函数?
在python中,引用函数非常简单:
>>> def foo(): ... print "foo called" ... return 1 ... >>> x = foo >>> foo() foo called 1 >>> x() foo called 1 >>> x >>> foo
但是,它似乎在Ruby中有所不同,因为裸foo
实际上调用了foo:
ruby-1.9.2-p0 > def foo ruby-1.9.2-p0 ?> print "foo called" ruby-1.9.2-p0 ?> 1 ruby-1.9.2-p0 ?> end => nil ruby-1.9.2-p0 > x = foo foo called => 1 ruby-1.9.2-p0 > foo foo called => 1 ruby-1.9.2-p0 > x => 1
我如何实际将函数 foo赋值给x然后调用它? 或者有更惯用的方法吗?
Ruby没有function。 它只有方法(不是一流的)和Proc
,它们是一流的,但不与任何对象相关联。
所以,这是一种方法:
def foo(bar) puts bar end foo('Hello') # Hello
哦,是的,这是一个真正的方法,而不是顶级function或程序或其他东西。 在顶级定义的方法最终作为Object
类中的私有(!)实例方法:
Object.private_instance_methods(false) # => [:foo]
这是一个Proc
:
foo = -> bar { puts bar } foo.('Hello') # Hello
请注意, Proc
的调用与方法不同:
foo('Hello') # method foo.('Hello') # Proc
foo.(bar)
语法只是foo.call(bar)
语法糖(对于Proc
和Method
s,它也别名为foo[bar]
)。 在你的对象上实现一个call
方法然后用.()
调用它是你最接近Python的__call__
ables。
请注意,Ruby Proc
和Python lambdas之间的一个重要区别是没有限制:在Python中,lambda只能包含一个语句,但Ruby没有语句和表达式之间的区别( 一切都是表达式),所以这个限制根本就不存在,因此在很多情况下你需要在Python中传递一个命名函数作为参数,因为你不能在一个语句中表达逻辑,你会在Ruby中简单地传递一个Proc
或者而是阻塞,因此甚至不会出现引用方法的丑陋语法问题。
您可以通过在Object#method
上调用Object#method
方法将方法包装在Method
对象(实际上是鸭子类型Proc
)中(这将为您提供一个其self
绑定到该特定对象的方法):
foo_bound = self.method(:foo) foo_bound.('Hello') # Hello
您还可以使用Module#instance_method
系列中的一种UnboundMethod
从模块(或类,显然,因为类是一个模块) UnboundMethod#bind
,然后您可以将UnboundMethod#bind
到特定对象并进行调用。 (我认为Python具有相同的概念,尽管有不同的实现:未绑定的方法只是明确地接受self参数,就像声明它的方式一样。)
foo_unbound = Object.instance_method(:foo) # this is an UnboundMethod foo_unbound.('Hello') # NoMethodError: undefined method `call' for # foo_rebound = foo_unbound.bind(self) # this is a Method foo_rebound.('Hello') # Hello
请注意,您只能将UnboundMethod
绑定到一个对象,该对象是您从该方法获取的模块的实例。 您不能使用UnboundMethods
在不相关的模块之间“移植”行为:
bar = module Foo; def bar; puts 'Bye' end; self end.instance_method(:bar) module Foo; def bar; puts 'Hello' end end obj = Object.new bar.bind(obj) # TypeError: bind argument must be an instance of Foo obj.extend(Foo) bar.bind(obj).() # Bye obj.bar # Hello
但请注意, Method
和UnboundMethod
都是方法的包装器 , 而不是方法本身。 方法不是 Ruby中的对象。 (与我在其他答案中所写的相反,BTW。我真的需要回去修复它们。)你可以将它们包装在对象中,但它们不是对象,你可以看到,因为你基本上都是这样的你总是得到包装的问题:身份和国家。 如果为同一方法多次调用method
,则每次都会得到一个不同的Method
对象。 如果您尝试在该Method
对象上存储某些状态(例如Python样式的__doc__
字符串),那么该状态将对该特定实例是私有的,如果您尝试通过method
再次检索文档字符串,您会发现它消失了。
过去有几个建议使用Ruby的作用域解析运算符::
来访问对象的绑定Method
,如下所示:
bound_method = obj::foo
这应该是相同的
bound_method = obj.method(:foo)
问题是目前实际上支持使用范围解析运算符来调用方法:
obj.bar obj::bar # Hello
更糟糕的是,这实际上是使用的 ,所以这将是一个向后不兼容的变化。
您可以使用从Object
inheritance的method
实例方法来检索Method
对象,该对象本质上是一个可以调用call
的Proc
对象。
在控制台中,你这样做:
fooMethod = self.method(:foo) #fooMethod is a Method object fooMethod.call #invokes fooMethod
Ruby支持proc
和lambda
,在其他语言中可能被称为匿名函数或闭包,具体取决于它们的使用方式。 他们可能更接近你想要的。
从https://stackoverflow.com/a/26620095/226255复制的function和方法之间的(主要)差异
函数在类之外定义,而方法在类的内部和部分类中定义。
Ruby没有函数,你的def foo
最终成为Object
类的一个方法。
如果你坚持像上面那样定义foo
,你可以通过这样做来提取它的“function”:
def foo(a,b) a+b end x = method(:foo).to_proc x.call(1,2) => 3
说明:
> method(:foo) # this is Object.method(:foo), returns a Method object bound to # object of type 'Class(Object)' => # method(:foo).to_proc # a Proc that can be called without the original object the method was bound to => #
重要的提示:
to_proc
“复制”方法的对象的关联实例变量(如果有)。 考虑一下:
class Person def initialize(name) @name = name end def greet puts "hello #{@name}" end end greet = Person.new('Abdo').method(:greet) # note that Person.method(:greet) returns an UnboundMethod and cannot be called # unless you bind it to an object > greet.call hello Abdo => nil
从概念上讲,如果你想要一个可以在某种类型的对象上工作的“函数”,它应该是一个方法,你应该这样组织你的代码。 如果您只需要在某个上下文中使用“function”并希望传递它,请使用lambdas:
greet = lambda { |person| "hello #{person}" } yell_at = lambda { |person| "HELLO #{person.upcase}" } def do_to_person(person, m) m.call(person) end do_to_person('Abdo', greet)