[ruby] Ruby에서 매개 변수로 메소드 전달

루비를 조금 엉망으로 만들려고합니다. 그래서 나는 책 “Programming Collective Intelligence”Ruby의 알고리즘 (Python으로 제공)을 구현하려고합니다.

8 장에서 저자는 메소드 a를 매개 변수로 전달합니다. 이것은 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

이와 같은 블록 선언에서 기본 인수를 설정할 수 없습니다. 따라서 splat을 사용하고 proc 코드 자체에 기본값을 설정해야합니다.


또는이 모든 범위에 따라 메서드 이름을 대신 전달하는 것이 더 쉬울 수 있습니다.

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

이 경우 전체 코드 청크를 전달하는 대신 객체에 정의 된 메서드를 호출하는 것입니다. 당신은 당신이 대체 할 필요가 수도 구조화 방법에 따라 self.send과를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

그러나 여기에 더 많은 재사용 가능한 코드 덩어리를 원하시는 것 같습니다.


답변

블록과 Procs를 언급하는 주석은 Ruby에서 더 일반적이라는 점에서 정확합니다. 그러나 원하는 경우 메서드를 전달할 수 있습니다. 당신은 전화 method방법을 얻을하고 .call그것을 전화 :

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


답변

방법으로 매개 변수로 메소드를 전달할 수 있습니다 method(:function). 다음은 매우 간단한 예입니다.

def double (a)
  * 2 반환
종료
=> 없음

def method_with_function_as_param (콜백, 번호)
  callback.call (번호)
종료
=> 없음

method_with_function_as_param (method (: double), 10)
=> 20


답변

이를 수행하는 일반적인 Ruby 방법은 블록을 사용하는 것입니다.

따라서 다음과 같습니다.

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

그리고 다음과 같이 사용됩니다.

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

이 패턴은 Ruby에서 광범위하게 사용됩니다.


답변

메서드 인스턴스 에서 &연산자를 사용하여 메서드를 블록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에서


답변

함수 객체의 “call”메소드를 호출해야합니다.

weight = weightf.call( dist )

편집 : 의견에 설명 된대로이 접근 방식은 잘못되었습니다. 정상적인 기능 대신 Procs를 사용하는 경우 작동합니다.


답변

함수 내에서 명명 된 블록에 액세스하려면 앰퍼샌드를 사용하는 것이 좋습니다. 이 기사에 제공된 권장 사항에 따라 다음 과 같이 작성할 수 있습니다 (이것은 내 작업 프로그램의 실제 스크랩입니다).

  # 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

Afterwerds, 다음과 같이이 함수를 호출 할 수 있습니다.

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

선택 항목을 필터링 할 필요가 없으면 블록을 생략하면됩니다.

@categories = make_select_list( Category ) # selects all categories

Ruby 블록의 힘을 위해 너무나 많이.