如何在 Ruby 中引用函数? [英] How do I reference a function in Ruby?

查看:61
本文介绍了如何在 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) 的语法糖(对于 Procs 和 Methods 也是 foo[bar] 的别名).在您的对象上实现一个 call 方法,然后使用 .() 调用它是最接近 Python 的 __call__ables 的方法.>

请注意,Ruby Procs 和 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).()# 再见对象栏# 你好

但是请注意,MethodUnboundMethod 都是围绕方法的包装器不是方法本身.方法是不是 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 Procs 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 Procs 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 Procs and Methods 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 Procs 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆