web-dev-qa-db-ja.com

Rubyでパラメーターとしてメソッドを渡す

Rubyを少し台無しにしようとしています。そのため、「Programming Collective Intelligence」Rubyのアルゴリズム(Pythonで指定)を実装しようとしています。

第8章では、著者はメソッドとしてパラメーターを渡します。これはPythonで動作しますが、Rubyでは動作しません。

ここに方法があります

def gaussian(dist, sigma=10.0)
  foo
end

別のメソッドでこれを呼び出したい

def weightedknn(data, vec1, k = 5, weightf = gaussian)
  foo
  weight = weightf(dist)
  foo
end

私が得たすべてはエラーです

ArgumentError: wrong number of arguments (0 for 1)

Procオブジェクトが必要です:

gaussian = Proc.new do |dist, *args|
  sigma = args.first || 10.0
  ...
end

def weightedknn(data, vec1, k = 5, weightf = gaussian)
  ...
  weight = weightf.call(dist)
  ...
end

そのようなブロック宣言ではデフォルト引数を設定できないことに注意してください。そのため、スプラットを使用して、procコード自体にデフォルトを設定する必要があります。


または、これらすべてのスコープに応じて、代わりにメソッド名を渡す方が簡単な場合があります。

def weightedknn(data, vec1, k = 5, weightf = :gaussian)
  ...
  weight = self.send(weightf)
  ...
end

この場合、コードの完全なチャンクを渡すのではなく、オブジェクトで定義されているメソッドを呼び出すだけです。これをどのように構成するかに応じて、_self.send with object_that_has_the_these_math_methods.send


最後になりましたが、メソッドからブロックを切ることができます。

def weightedknn(data, vec1, k = 5)
  ...
  weight = 
    if block_given?
      yield(dist)
    else
      gaussian.call(dist)
    end
  end
  ...
end

weightedknn(foo, bar) do |dist|
  # square the dist
  dist * dist
end

しかし、ここではもっと再利用可能なコードの塊が欲しいようです。

91
Alex Wayne

ブロックおよびProcsを参照するコメントは、Rubyでより一般的であるという点で正しいです。ただし、必要に応じてメソッドを渡すことができます。 methodを呼び出してメソッドを取得し、.callそれを呼び出すには:

def weightedknn( data, vec1, k = 5, weightf = method(:gaussian) )
  ...
  weight = weightf.call( dist )
  ...
end
94
Daniel Lucraft

method(:function) wayを使用してメソッドをパラメーターとして渡すことができます。以下は非常に簡単な例です。

 def double(a)
 return a * 2 
 end 
 => nil 
 
 def method_with_function_as_param(callback、number) 
 callback.call(number)
 end 
 => nil 
 
 method_with_function_as_param(method(:double)、10)
 => 20 
40
Patrick Wong

通常のRubyこれを行う方法は、ブロックを使用することです。

したがって、次のようになります。

def weightedknn( data, vec1, k = 5 )
  foo
  weight = yield( dist )
  foo
end

そして次のように使用されます:

weightenknn( data, vec1 ) { |dist| gaussian( dist ) }

このパターンは、Rubyで広く使用されています。

24
Chuck

メソッドのMethodインスタンスで&演算子を使用して、メソッドをブロックに変換することができます

例:

def foo(arg)
  p arg
end

def bar(&block)
  p 'bar'
  block.call('foo')
end

bar(&method(:foo))

詳細は http://weblog.raganwald.com/2008/06/what-does-do-when-used-as-unary.html

13

関数内の名前付きブロックにアクセスするには、アンパサンドを使用することをお勧めします。 この記事 に記載されている推奨事項に従うと、次のように書くことができます(これは私の作業プログラムからの本当のスクラップです):

  # Returns a valid hash for html form select element, combined of all entities
  # for the given +model+, where only id and name attributes are taken as
  # values and keys correspondingly. Provide block returning boolean if you
  # need to select only specific entities.
  #
  # * *Args*    :
  #   - +model+ -> ORM interface for specific entities'
  #   - +&cond+ -> block {|x| boolean}, filtering entities upon iterations
  # * *Returns* :
  #   - hash of {entity.id => entity.name}
  #
  def make_select_list( model, &cond )
    cond ||= proc { true } # cond defaults to proc { true }
    # Entities filtered by cond, followed by filtration by (id, name)
    model.all.map do |x|
      cond.( x ) ? { x.id => x.name } : {}
    end.reduce Hash.new do |memo, e| memo.merge( e ) end
  end

その後、この関数を次のように呼び出すことができます。

@contests = make_select_list Contest do |contest|
  logged_admin? or contest.organizer == @current_user
end

選択をフィルタリングする必要がない場合は、単にブロックを省略します。

@categories = make_select_list( Category ) # selects all categories

Rubyブロック。

1
vitrums

関数オブジェクトのメソッド「呼び出し」を呼び出す必要があります。

weight = weightf.call( dist )

編集:コメントで説明したように、このアプローチは間違っています。通常の関数の代わりにProcsを使用している場合に機能します。

1
Tiago