如何在 Ruby 中引用函数? [英] How do I reference a function in Ruby?
问题描述
在python中,引用一个函数相当简单:
<预><代码>>>>定义 foo():...打印foo叫"...返回 1...>>>x = foo>>>富()foo 叫1>>>X()foo 叫1>>>X<函数 foo 在 0x1004ba5f0>>>>富<函数 foo 在 0x1004ba5f0>然而,它在 Ruby 中似乎有所不同,因为裸 foo
实际上调用了 foo:
ruby-1.9.2-p0 >定义 fooruby-1.9.2-p0 ?>打印foo叫"ruby-1.9.2-p0 ?>1ruby-1.9.2-p0 ?>结尾=>零ruby-1.9.2-p0 >x = foofoo 被称为 =>1ruby-1.9.2-p0 >富foo 被称为 =>1ruby-1.9.2-p0 >X=>1
我如何实际将 function foo 分配给 x 然后调用它?或者有没有更惯用的方法来做到这一点?
Ruby 没有函数.它只有方法(不是一流的)和 Proc
是一流的,但不与任何对象相关联.
所以,这是一个方法:
def foo(bar) 放置 bar 结束foo('你好')# 你好
哦,是的,这是一个真正的方法,而不是顶级函数或过程或其他东西.在顶层定义的方法最终成为 Object
类中的私有(!)实例方法:
Object.private_instance_methods(false) # =>[:foo]
这是一个Proc
:
foo = ->栏 { 放置栏 }foo.('你好')# 你好
请注意,Proc
与方法的调用方式不同:
foo('Hello') # 方法foo.('Hello') # 进程
foo.(bar)
语法只是 foo.call(bar)
的语法糖(对于 Proc
s 和 Method
s 也是 foo[bar]
的别名).在您的对象上实现一个 call
方法,然后使用 .()
调用它是最接近 Python 的 __call__
ables 的方法.>
请注意,Ruby Proc
s 和 Python lambdas 之间的一个重要区别是没有限制:在 Python 中,lambda 只能包含一个语句,但 Ruby 没有 语句和表达式的区别(一切都是一个表达式),所以这个限制根本不存在,因此在很多情况下你需要要在 Python 中将命名函数作为参数传递,因为您无法在单个语句中表达逻辑,您将在 Ruby 中简单地传递一个 Proc
或一个块,这样难看的语法问题引用方法甚至不会出现.
您可以通过调用 Object#method 在
对象上的方法(它将为您提供一个 Method
对象(本质上是鸭子类型的 Proc
)中包装一个方法Method
,其 self
绑定到该特定对象):
foo_bound = method(:foo)foo_bound.('你好')# 你好
您还可以使用 Module#instance_method
系列中的方法之一从模块(或类,显然,因为类是-a模块),然后您可以UnboundMethod#bind
到特定对象并调用.(我认为 Python 具有相同的概念,尽管实现方式不同:未绑定方法只是显式地采用 self 参数,就像它的声明方式一样.)
foo_unbound = Object.instance_method(:foo) # 这是一个 UnboundMethodfoo_unbound.('你好')# NoMethodError: undefined method `call' for #<UnboundMethod: Object#foo>foo_rebound = foo_unbound.bind(self) # 这是一个方法foo_rebound.('你好')# 你好
请注意,您只能将 UnboundMethod
绑定到一个对象,该对象是您从中获取该方法的模块的实例.您不能使用 UnboundMethods
在不相关的模块之间移植"行为:
bar = 模块 Foo;定义栏;把再见"结束;self end.instance_method(:bar)模块 Foo;定义栏;puts 'Hello' 结束obj = Object.newbar.bind(obj)# TypeError: 绑定参数必须是 Foo 的一个实例obj.extend(Foo)bar.bind(obj).()# 再见对象栏# 你好
但是请注意,Method
和 UnboundMethod
都是围绕方法的包装器,不是方法本身.方法是不是 Ruby 中的对象.(与我在其他答案中所写的相反,顺便说一句.我真的需要回去修复它们.)您可以将它们包装在对象中,但它们不是对象,并且您可以看到这一点,因为您基本上会遇到总是使用包装器时遇到的所有相同问题:身份和状态.如果你为同一个方法多次调用method
,你每次都会得到不同的Method
对象.如果您尝试在该 Method
对象上存储一些状态(例如 Python 样式的 __doc__
字符串),则该状态对于该特定 实例,如果您尝试通过 method
再次检索您的文档字符串,您会发现它已经消失了.
还有方法引用操作符.:
:
bound_method = obj.:foo
相同于
bound_method = obj.method(:foo)
In python, it's fairly straightforward to reference a function:
>>> def foo():
... print "foo called"
... return 1
...
>>> x = foo
>>> foo()
foo called
1
>>> x()
foo called
1
>>> x
<function foo at 0x1004ba5f0>
>>> foo
<function foo at 0x1004ba5f0>
However, it seems to be different in Ruby since a naked foo
actually calls 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
How do I actually assign the function foo to x and then call it? Or is there a more idiomatic way to do this?
Ruby doesn't have functions. It only has methods (which aren't first-class) and Proc
s which are first-class, but are not associated with any object.
So, this is a method:
def foo(bar) puts bar end
foo('Hello')
# Hello
Oh, and, yes, this is a real method, not a top-level function or procedure or something. Methods defined at the top-level end up as private(!) instance methods in the Object
class:
Object.private_instance_methods(false) # => [:foo]
This is a Proc
:
foo = -> bar { puts bar }
foo.('Hello')
# Hello
Notice that Proc
s are called differently from methods:
foo('Hello') # method
foo.('Hello') # Proc
The foo.(bar)
syntax is just syntactic sugar for foo.call(bar)
(which for Proc
s and Method
s is also aliased to foo[bar]
). Implementing a call
method on your object and then calling it with .()
is the closest thing you will get to Python's __call__
ables.
Note that an important distinction between Ruby Proc
s and Python lambdas is that there are no restrictions: in Python, a lambda can only contain a single statement, but Ruby doesn't have the distinction between statements and expressions (everything is an expression), and so this limitation simply doesn't exist, therefore in a lot of cases where you need to pass a named function as an argument in Python because you cannot express the logic in a single statement, you would in Ruby simply pass a Proc
or a block instead, so that the problem of the ugly syntax for referencing methods doesn't even arise.
You can wrap a method in a Method
object (which essentially duck-types Proc
) by calling the Object#method
method on an object (which will give you a Method
whose self
is bound to that particular object):
foo_bound = method(:foo)
foo_bound.('Hello')
# Hello
You can also use one of the methods in the Module#instance_method
family to get an UnboundMethod
from a module (or class, obviously, since a class is-a module), which you can then UnboundMethod#bind
to a particular object and call. (I think Python has the same concepts, albeit with a different implementation: an unbound method simply takes the self argument explicitly, just like the way it is declared.)
foo_unbound = Object.instance_method(:foo) # this is an UnboundMethod
foo_unbound.('Hello')
# NoMethodError: undefined method `call' for #<UnboundMethod: Object#foo>
foo_rebound = foo_unbound.bind(self) # this is a Method
foo_rebound.('Hello')
# Hello
Note that you can only bind an UnboundMethod
to an object which is an instance of the module you took the method from. You cannot use UnboundMethods
to "transplant" behavior between unrelated modules:
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
Note, however, that both the Method
and the UnboundMethod
are wrappers around the method, not the method itself. Methods are not objects in Ruby. (Contrary to what I have written in other answers, BTW. I really need to go back and fix those.) You can wrap them in objects, but they aren't objects, and you can see that because you essentially get all the same problems you always get with wrappers: identity and state. If you call method
multiple times for the same method, you will get a different Method
object every time. If you try to store some state on that Method
object (such as Python-style __doc__
strings, for example), that state will be private to that particular instance, and if you try to retrieve your docstring again via method
, you will find that it is gone.
There is also syntactic sugar in the form of the method reference operator .:
:
bound_method = obj.:foo
Which is identical to
bound_method = obj.method(:foo)
这篇关于如何在 Ruby 中引用函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!