如何在 Ruby 中引用函数?

发布于 2024-10-04 13:29:13 字数 720 浏览 4 评论 0原文

在 python 中,引用函数相当简单:

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

但是,在 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 

How do I 实际上分配 function foo到 x 然后调用它?或者有更惯用的方法来做到这一点?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

╰◇生如夏花灿烂 2024-10-11 13:29:13

Ruby 没有函数。它只有方法(不是一流的)和一流的 Proc,但不与任何对象关联。

所以,这是一个方法:

def foo(bar) puts bar end

foo('Hello')
# Hello

哦,是的,这一个真正的方法,而不是顶级函数或过程或其他东西。在顶层定义的方法最终作为 Object 类中的 private(!) 实例方法:

Object.private_instance_methods(false) # => [:foo]

这是一个 Proc

foo = -> bar { puts bar }

foo.('Hello')
# Hello

请注意 Proc s 的调用方式与方法不同:

foo('Hello')  # method
foo.('Hello') # Proc

foo.(bar) 语法只是 foo.call(bar) 的语法糖(对于 Proc s 和 Methods 也别名为 foo[bar])。在对象上实现 call 方法,然后使用 .() 调用它是最接近 Python 的 __call__ 功能的方法。

请注意,Ruby Proc 和 Python lambda 之间的一个重要区别是没有任何限制:在 Python 中,lambda 只能包含单个语句,但 Ruby 没有 > 语句和表达式之间的区别(一切都是表达式),因此这种限制根本不存在,因此在很多情况下您需要传递一个在 Python 中将命名函数作为参数,因为您无法在单个语句中表达逻辑,而在 Ruby 中您只需传递一个 Proc 或一个块即可,这样引用方法的丑陋语法问题就不会出现甚至都不会出现。

您可以通过调用 Object#method 将方法包装在 Method 对象(本质上是鸭子类型 Proc)中> 对象上的方法(这将为您提供一个 Method,其 self 绑定到该特定对象):

foo_bound = method(:foo)

foo_bound.('Hello')
# Hello

您还可以使用 Module 中的方法之一#instance_method 系列从模块(或类,显然,因为类是一个模块)获取 UnboundMethod,然后您可以 UnboundMethod#bind到一个特定的对象并调用。 (我认为 Python 具有相同的概念,尽管实现不同:未绑定方法只是显式地接受 self 参数,就像它的声明方式一样。)

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

请注意,您只能将 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

但是请注意,MethodUnboundMethod 都是包装器。 围绕方法,而不是方法本身。 Ruby 中的方法不是对象。 (顺便说一句,与我在其他答案中所写的相反。我真的需要回去修复这些问题。)您可以将它们包裹在对象中,但它们不是对象,您可以看到,因为您本质上会遇到与包装器总是相同的问题:身份和状态。如果多次调用同一个方法的 method ,每次都会得到不同的 Method 对象。如果您尝试在该 Method 对象上存储某些状态(例如 Python 风格的 __doc__ 字符串),则该状态将是该特定私有的< /em> 实例,如果您尝试通过 method 再次检索您的文档字符串,您会发现它已经消失了。

还有方法引用运算符.:形式的语法糖:

bound_method = obj.:foo

这与

bound_method = obj.method(:foo)

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)
不忘初心 2024-10-11 13:29:13

您可以使用从 Object< 继承的 method 实例方法/code>检索 Method 对象,本质上是一个 Proc 对象,您可以调用call

在控制台中,您可以执行以下操作:

fooMethod = self.method(:foo) #fooMethod is a Method object

fooMethod.call #invokes fooMethod

alt text

You can use the method instance method inherited from Object to retrieve a Method object, which essentially is a Proc object which you can invoke call on.

In the console, you'd do this:

fooMethod = self.method(:foo) #fooMethod is a Method object

fooMethod.call #invokes fooMethod

alt text

花期渐远 2024-10-11 13:29:13

Ruby 支持 proclambda 在其他语言中可能称为匿名函数或闭包,具体取决于关于如何使用它们。他们可能更接近您正在寻找的东西。

Ruby supports proc and lambda which in other languages might be called anonymous functions or closures, depending on how they are used. They might be closer to what you are looking for.

丑疤怪 2024-10-11 13:29:13

https://stackoverflow.com/a/26620095/226255 复制的函数和方法之间的(主要)区别

函数是在类外部定义的,而方法是在类外部定义的
课堂内部和课堂的一部分。

Ruby 没有函数,您的 def foo 最终成为 Object 类的方法。

如果您坚持像上面那样定义 foo ,您可以通过执行以下操作提取其“功能”:

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: Class(Object)#foo>

method(:foo).to_proc
# a Proc that can be called without the original object the method was bound to
=> #<Proc:0x007f97845f35e8 (lambda)>

重要说明:

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

从概念上讲,如果您想要一个可以在某种类型的对象上工作的“函数”,它应该是一个方法,并且您应该这样组织您的代码。如果您只需要在特定上下文中使用“函数”并希望传递它,请使用 lambda:

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)

The (main) difference between functions and methods as copied from https://stackoverflow.com/a/26620095/226255

Functions are defined outside of classes, while methods are defined
inside of and part of classes.

Ruby does not have functions and your def foo ends up being a method for the Object class.

If you insist on defining foo like you're doing above, you can extract its "functionality" by doing this:

def foo(a,b)
 a+b
end

x = method(:foo).to_proc
x.call(1,2)
=> 3

Explanation:

> method(:foo) # this is Object.method(:foo), returns a Method object bound to 
# object of type 'Class(Object)'
=> #<Method: Class(Object)#foo>

method(:foo).to_proc
# a Proc that can be called without the original object the method was bound to
=> #<Proc:0x007f97845f35e8 (lambda)>

Important note:

to_proc "copies" the method's object's associated instance variables if any. Consider this:

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

Conceptually, if you want a "function" that would work on a certain type of objects, it should be a method and you should organize your code as such. If you only need your "function" in a certain context and wish to pass it around, use 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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文