34

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>

ただし、裸でfoo実際にfooを呼び出すため、Rubyでは異なるようです。

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に割り当てて、それを呼び出すにはどうすればよいですか?または、これを行うためのより慣用的な方法はありますか?

4

4 に答える 4

59

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

Procsはメソッドとは異なる方法で呼び出されることに注意してください。

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

構文は、 (sおよびsの場合も)のfoo.(bar)構文糖衣です。オブジェクトにメソッドを実装してからそれを呼び出すことは、Pythonの機能に最も近いものです。foo.call(bar)ProcMethodfoo[bar]call.()__call__

RubyとPythonラムダの重要な違いProcは、制限がないことです。Pythonでは、ラムダに含めることができるステートメントは1つだけですが、Rubyにはステートメントと式の区別がありません(すべて式です)。したがって、この制限は単に存在しません。したがって、単一のステートメントでロジックを表現できないためにPythonで名前付き関数を引数として渡す必要がある多くの場合、RubyProcでは代わりにまたはブロックを渡すだけです。 、メソッドを参照するための醜い構文の問題が発生しないようにします。

オブジェクト(基本的にはduck-types )でメソッドをラップするには、オブジェクトでメソッドを呼び出します(これにより、その特定のオブジェクトにバインドされているが得られます)。MethodProcObject#methodMethodself

foo_bound = method(:foo)

foo_bound.('Hello')
# Hello

Module#instance_methodファミリ内のメソッドの1つを使用してUnboundMethod、モジュール(または、クラスはモジュールであるため、明らかにクラス)から取得することもできます。これUnboundMethod#bindにより、特定のオブジェクトを呼び出して呼び出すことができます。(Pythonの概念は同じだと思いますが、実装は異なります。バインドされていないメソッドは、宣言された方法と同じように、自己引数を明示的に取ります。)

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

ただし、とは両方ともMethodメソッドUnboundMethodのラッパーであり、メソッド自体でないことに注意してください。メソッドはRubyのオブジェクトではありません。(私が他の回答で書いたこととは反対に、ところで。私は本当に戻ってそれらを修正する必要があります。)それらをオブジェクトでラップすることはできますが、それらはオブジェクトではありません。ラッパーで常に発生する問題:IDと状態。method同じメソッドを複数回呼び出すと、Method毎回異なるオブジェクトが取得されます。そのオブジェクトに状態を保存しようとしたMethod場合(Pythonスタイルなど)__doc__文字列など)、その状態はその特定のインスタンスに対してプライベートになり、を介してdocstringを再度取得しようとするとmethod、それがなくなっていることがわかります。

メソッド参照演算子.:の形式の構文糖衣もあります。

bound_method = obj.:foo

これはと同じです

bound_method = obj.method(:foo)
于 2010-11-28T00:22:36.040 に答える
10

methodから継承されたインスタンスメソッドを使用してObject、オブジェクトを取得できます。オブジェクトMethodは、基本的にはProc呼び出すことができるオブジェクトですcall

コンソールでは、次のようにします。

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

fooMethod.call #invokes fooMethod

代替テキスト

于 2010-11-27T23:36:15.777 に答える
1

Rubyはサポートprocしてlambdaおり、他の言語では、使用方法に応じて無名関数またはクロージャと呼ばれる場合があります。彼らはあなたが探しているものに近いかもしれません。

于 2010-11-28T00:09:14.417 に答える
1

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

概念的には、特定のタイプのオブジェクトで機能する「関数」が必要な場合は、それをメソッドにする必要があり、コードをそのように編成する必要があります。特定のコンテキストで「関数」のみが必要で、それを渡したい場合は、ラムダを使用します。

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)
于 2014-11-19T17:05:40.117 に答える