루비가 왜 메소드 오버로딩을 지원하지 않습니까?


146

메소드 오버로드를 지원하는 대신 Ruby는 기존 메소드를 덮어 씁니다. 왜 언어가 이런 식으로 디자인되었는지 설명 할 수 있습니까?

답변:


166

동일한 이름과 다른 서명을 가진 두 개의 메소드를 선언하여 메소드 오버로드를 수행 할 수 있습니다. 서로 다른 서명은 다음 중 하나 일 수 있습니다.

  1. 다른 데이터 유형의 인수, 예 : method(int a, int b) vs method(String a, String b)
  2. 가변 개수의 인수, 예 : method(a) vs method(a, b)

ruby ( dynamic typed language ) 에는 데이터 형식 선언이 없으므로 첫 번째 방법을 사용하여 메서드 오버로드를 수행 할 수 없습니다 . 따라서 위의 방법을 정의하는 유일한 방법은def(a,b)

두 번째 옵션을 사용하면 메서드 오버로드를 달성 할 수 있지만 보이지 않습니다. 인수 수가 다른 두 가지 방법이 있다고 가정 해 보겠습니다.

def method(a); end;
def method(a, b = true); end; # second argument has a default value

method(10)
# Now the method call can match the first one as well as the second one, 
# so here is the problem.

따라서 루비는 고유 한 이름을 가진 메소드 조회 체인에서 하나의 메소드를 유지해야합니다.


22
@ Jörg W Mittag의 대답은 훨씬 아래에 묻혀있어 읽을 가치가 있습니다.
user2398029

1
그리고 @Derek Ekins의 답변은 훨씬 더 묻혀 대안을 제공합니다
Cyril Duchon-Doris

미래의 루비리스트들에게 참고 사항 ... FWIW 당신은 ​​계약 보석 egonschiele.github.io/contracts.ruby/#method-overloading
engineerDave

214

"오버로딩"은 루비에서는 의미가없는 용어입니다. 그것은 기본적으로 "정적 인수를 기반 파견"에 대한 동의어이지만, 루비하지 않습니다 정적 파견을 전혀 . 따라서 Ruby가 인수를 기반으로 정적 디스패치를 ​​지원하지 않는 이유는 정적 디스패치 기간을 지원하지 않기 때문입니다. 인수 기반이든 아니든 어떤 종류 의 정적 디스패치도 지원하지 않습니다 .

이제, 당신이 경우 하지 실제로 특별히 과부하에 대해 물어,하지만 어쩌면에 대한 동적 인수를 기반 파견, 다음 대답은 : 마츠 그것을 구현하지 않았기 때문에. 다른 누구도 그것을 제안하지 않았기 때문입니다. 아무도 그것을 구현하기 위해 귀찮게하지 않았기 때문입니다.

일반적으로 선택적 인수와 가변 길이 인수 목록이있는 언어로 된 동적 인수 기반 디스패치는 올바르게 이해 하기매우 어렵고 이해 하기더 어렵 습니다. 정적 인수 기반 디스패치가 있고 선택적 인수가없는 언어 (예 : Java 등)의 경우에도 단순한 필사자에게 어떤 과부하를 선택 해야하는지 말하기가 거의 불가능합니다 .

C #에서는 실제로 모든 3-SAT 문제를 과부하 해결로 인코딩 할 수 있습니다. 즉, C #의 과부하 해결은 NP-hard입니다.

이제 동적 파견으로 시도해보십시오 . 여기서 머리에 유지할 추가 시간 차원이 있습니다.

"숨겨진"0 번째 self인수 에서만 전달하는 객체 지향 언어와 달리 프로 시저의 모든 인수를 기반으로 동적으로 전달하는 언어가 있습니다 . 예를 들어, 일반적인 Lisp은 동적 유형과 모든 인수의 동적 값을 전달합니다. Clojure는 모든 인수 (BTW는 매우 차갑고 매우 강력 함)의 임의 함수를 전달합니다.

그러나 동적 인수 기반 디스패치가있는 OO 언어는 모릅니다. 마틴 오더 스키는 그가 말했다 수도 있지만, 스칼라에 인수 기반의 파견을 추가하는 것을 고려 그는 같은 시간에 과부하를 제거 할 수있는 경우 모두 사용이 과부하 및 Java와 호환 것을 스칼라 기존 코드와 이전 버전과 호환 (그는 특히 스윙과 AWT를 언급 Java의 다소 복잡한 오버로드 규칙의 불쾌한 어두운 구석 사례를 거의 모두 수행하는 매우 복잡한 트릭을 재생합니다). Ruby에 인수 기반 디스패치를 ​​추가하는 것에 대한 아이디어가 있었지만 이전 버전과 호환되는 방식으로 수행하는 방법을 알 수 없었습니다.


5
이것이 정답입니다. 허용 된 답변이 지나치게 단순화되었습니다. C # DOES에는 명명 된 매개 변수와 선택적 매개 변수가 있으며 여전히 오버로드를 구현하므로 " def method(a, b = true)작동하지 않으므로 메서드 오버로드가 불가능합니다." 그렇지 않습니다. 어려워요 그러나이 답변이 실제로 유익하다는 것을 알았습니다.
tandrewnichols

1
@tandrewnichols : C #에서 "어려운"과부하 해결 방법을 이해하기 위해 C #의 과부하 해결 방법으로 3-SAT 문제를 인코딩하고 컴파일러가 컴파일 타임에 해결하도록하여 C # NP에서 과부하 해결을 수행 할 수 있습니다. -hard (3-SAT는 NP-complete라고 알려져 있습니다). 이제 컴파일 타임에 호출 사이트 당 한 번이 아니라 런타임 에 모든 단일 메소드 호출대해 한 번의 메소드 호출마다 이를 수행해야한다고 상상해보십시오 .
Jörg W Mittag

1
@ JörgWMittag 과부하 해결 메커니즘에 3-SAT 문제의 인코딩을 보여주는 링크를 포함시킬 수 있습니까?
오징어


2
"오버로딩"은 "정적 인수 기반 디스패치"의 동의어처럼 보이지 않습니다. 정적 인수 기반 디스패치는 단순히 가장 일반적인 오버로드 구현입니다. 오버로드는 "동일한 메소드 이름이지만 동일한 범위에서 다른 구현"을 의미하는 구현과 무관 한 용어입니다.
snovity

85

나는 당신이 이것을 할 수있는 능력을 찾고 있다고 가정합니다.

def my_method(arg1)
..
end

def my_method(arg1, arg2)
..
end

루비는 이것을 다른 방식으로 지원합니다 :

def my_method(*args)
  if args.length == 1
    #method 1
  else
    #method 2
  end
end

일반적인 패턴은 옵션을 해시로 전달하는 것입니다.

def my_method(options)
    if options[:arg1] and options[:arg2]
      #method 2
    elsif options[:arg1]
      #method 1
    end
end

my_method arg1: 'hello', arg2: 'world'

희망이 도움이


15
우리 중 많은 사람들이 알고 싶어하는 +1 : Ruby 메소드에서 다양한 수의 인수로 작업하는 방법.
ashes999

3
이 답변은 선택적 인수에 대한 추가 정보를 활용할 수 있습니다. (그리고 아마도 그것들은 논쟁의 여지가 있습니다.)
Ajedi32

9

다른 유형의 인수를 구별 할 수있는 정적 유형의 언어에서는 메소드 오버로드가 의미가 있습니다.

f(1)
f('foo')
f(true)

다른 수의 인수 사이

f(1)
f(1, 'foo')
f(1, 'foo', true)

첫 번째 차이점은 루비에는 존재하지 않습니다. 루비는 다이나믹 타이핑 또는 "덕 타이핑"을 사용합니다. 두 번째 구별은 기본 인수 또는 인수를 사용하여 처리 할 수 ​​있습니다.

def f(n, s = 'foo', flux_compensator = true)
   ...
end


def f(*args)
  case args.size
  when  
     ...
  when 2
    ...
  when 3
    ...
  end
end

이것은 강력한 타이핑과 관련이 없습니다. 결국 루비 강력하게 타이핑됩니다.
Jörg W Mittag

8

루비에 왜 메소드 오버로드가 없는지에 대한 질문에는 답하지 않지만, 타사 라이브러리가이를 제공 할 수 있습니다.

contracts.ruby의 라이브러리는 오버로드 할 수 있습니다. 튜토리얼에서 채택한 예 :

class Factorial
  include Contracts

  Contract 1 => 1
  def fact(x)
    x
  end

  Contract Num => Num
  def fact(x)
    x * fact(x - 1)
  end
end

# try it out
Factorial.new.fact(5)  # => 120

1유형이 아니라 일치하는 값 (예 :)을 지정할 수 있기 때문에 이는 실제로 Java의 오버로드보다 강력 합니다.

이 기능을 사용하면 성능이 저하됩니다. 허용 할 수있는 양을 결정하려면 벤치 마크를 실행해야합니다.


1
모든 종류의 IO가있는 실제 응용 프로그램에서는 (어떤 종류의 IO에 따라) 0.1-10 % 만 느려집니다.
Waterlink

1

나는 종종 다음 구조를 수행합니다.

def method(param)
    case param
    when String
         method_for_String(param)
    when Type1
         method_for_Type1(param)

    ...

    else
         #default implementation
    end
end

이를 통해 객체 사용자는 깨끗하고 명확한 method_name : method를 사용할 수 있지만 실행을 최적화하려면 올바른 메소드를 직접 호출 할 수 있습니다.

또한 테스트를보다 명확하고 향상시킵니다.


1

왜 질문의 측면에 대해 큰 대답이 있습니다. 그러나 다른 솔루션을 찾는 사람이라면 Elixir 패턴 일치 기능에서 영감을 얻은 기능적 루비 보석을 확인하십시오 .

 class Foo
   include Functional::PatternMatching

   ## Constructor Over loading
   defn(:initialize) { @name = 'baz' }
   defn(:initialize, _) {|name| @name = name.to_s }

   ## Method Overloading
   defn(:greet, :male) {
     puts "Hello, sir!"
   }

   defn(:greet, :female) {
     puts "Hello, ma'am!"
   }
 end

 foo = Foo.new or Foo.new('Bar')
 foo.greet(:male)   => "Hello, sir!"
 foo.greet(:female) => "Hello, ma'am!"   
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.