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


117

루비를 조금 엉망으로 만들려고합니다. 그래서 나는 책 "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)

답변:


100

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

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


1
두 번째 옵션이 가장 좋은 옵션이라고 생각합니다 (즉, Object.send () 사용), 단점은 모든 클래스에 대해 클래스를 사용해야한다는 것입니다 (어쨌든 OO에서 수행해야하는 방법입니다 :)). 항상 블록 (Proc)을 전달하는 것보다 더 건조하며 래퍼 메서드를 통해 인수를 전달할 수도 있습니다.
Jimmy Stenke

4
또한 foo.bar(a,b)send 로 수행하려면 foo.send(:bar, a, b). splat (*) 연산자를 사용하면 foo.send(:bar, *[a,b])임의의 길이의 인수 배열을 원할 경우 수행 할 수 있습니다. bar 메서드가 인수를 흡수 할 수 있다고 가정하면
xxjjnn

99

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

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

3
이건 재미 있네. method( :<name> )메서드 이름을 호출 가능한 기호로 변환 할 때 한 번만 호출한다는 점은 주목할 가치가 있습니다 . 그 결과를 변수 나 매개 변수에 저장하고 그때부터 다른 변수처럼 자식 함수에 계속 전달할 수 있습니다.

1
또는 인수 목록에서 메서드 구문 을 사용하는 대신 다음과 같이 메서드를 호출하는 동안 사용할 수 있습니다. weightedknn (data, vec1, k, method (: gaussian))
Yahya

1
이 방법은 매개 변수를 처리 할 필요가 없기 때문에 proc 또는 블록을 사용하는 것보다 낫습니다. 방법이 원하는대로 작동합니다.
danuker

3
완료를 위해 다른 곳에 정의 된 메서드를 전달하려면 SomewhereElse.method(:method_name). 꽤 괜찮은데!
medik

이것은 자체 질문 일 수 있지만 기호가 함수 또는 다른 것을 참조하는지 어떻게 확인할 수 있습니까? 나는 시도 :func.class하지만 그건 단지입니다symbol
quietContest

46

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

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

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

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

7
나는 더 복잡 범위와 방법에 대한 문제를 직면하고, 마지막으로 수행이 캔 도움말 사람을 희망하는 방법을 알아 냈 : 당신의 방법은 다른 클래스에서, 예를 들어 경우, 마지막과 같은 코드의 라인을 호출해야 method_with_function_as_param(Class.method(:method_name),...)하지를method(:Class.method_name)
V를 . Déhaye

귀하의 답변 덕분에라는 방법을 발견했습니다 method. 내 하루를 만들었지 만 그것이 내가 기능적 언어를 선호하는 이유라고 생각합니다. 원하는 것을 얻기 위해 그러한 곡예를 만들 필요가 없습니다. 어쨌든, 나는 루비를 발굴
Ludovic Kuty

25

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

따라서 다음과 같습니다.

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

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

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

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



1

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

weight = weightf.call( dist )

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


1
그가 weightf = gaussianarg 목록에서 할 때 실제로 gaussian는 그 결과를 weightf의 기본값으로 실행 하고 할당 하려고합니다 . 호출에는 필수 인수 및 충돌이 없습니다. 따라서 weightf는 아직 call 메소드가있는 proc 객체가 아닙니다.
Alex Wayne

1
이것은 (즉, 잘못하고 이유를 설명하는 주석) 실제로 내가 받아 들인 대답을 완전히 이해할 수있게 해주었습니다. 감사합니다! 한
rmcsharry

1

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

  # 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 블록의 힘을 위해 너무나 많이.


-5

"eval"을 사용하고 메서드를 문자열 인수로 전달한 다음 다른 메서드에서 간단히 평가할 수도 있습니다.


1
이것은 정말 나쁜 습관입니다. 절대하지 마세요!
개발자

@Developer 이것이 나쁜 습관으로 간주되는 이유는 무엇입니까?
jlesse

성능상의 이유로 eval임의의 코드를 실행할 수 있으므로 다양한 공격에 매우 취약합니다.
개발자
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.