메소드 오버로드를 지원하는 대신 Ruby는 기존 메소드를 덮어 씁니다. 왜 언어가 이런 식으로 디자인되었는지 설명 할 수 있습니까?
답변
동일한 이름과 다른 서명을 가진 두 개의 메소드를 선언하여 메소드 오버로드를 수행 할 수 있습니다. 서로 다른 서명은 다음 중 하나 일 수 있습니다.
- 다른 데이터 유형의 인수, 예 :
method(int a, int b) vs method(String a, String b)
- 가변 개수의 인수, 예 :
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.
따라서 루비는 고유 한 이름을 가진 메소드 조회 체인에서 하나의 메소드를 유지해야합니다.
답변
“오버로딩”은 루비에서는 의미가없는 용어입니다. 그것은 기본적으로 “정적 인수를 기반 파견”에 대한 동의어이지만, 루비하지 않습니다 이 정적 파견을 전혀 . 따라서 Ruby가 인수를 기반으로 정적 디스패치를 지원하지 않는 이유는 정적 디스패치 기간을 지원하지 않기 때문입니다. 인수 기반이든 아니든 어떤 종류 의 정적 디스패치도 지원하지 않습니다 .
이제, 당신이 경우 하지 실제로 특별히 과부하에 대해 물어,하지만 어쩌면에 대한 동적 인수를 기반 파견, 다음 대답은 : 마츠 그것을 구현하지 않았기 때문에. 다른 누구도 그것을 제안하지 않았기 때문입니다. 아무도 그것을 구현하기 위해 귀찮게하지 않았기 때문입니다.
일반적으로 선택적 인수와 가변 길이 인수 목록이있는 언어로 된 동적 인수 기반 디스패치는 올바르게 이해 하기 가 매우 어렵고 이해 하기 가 더 어렵 습니다. 정적 인수 기반 디스패치가 있고 선택적 인수가없는 언어 (예 : Java 등)의 경우에도 단순한 필사자에게 어떤 과부하를 선택 해야하는지 말하기가 거의 불가능합니다 .
C #에서는 실제로 모든 3-SAT 문제를 과부하 해결로 인코딩 할 수 있습니다. 즉, C #의 과부하 해결은 NP-hard입니다.
이제 동적 파견으로 시도해보십시오 . 여기서 머리에 유지할 추가 시간 차원이 있습니다.
“숨겨진”0 번째 self
인수 에서만 전달하는 객체 지향 언어와 달리 프로 시저의 모든 인수를 기반으로 동적으로 전달하는 언어가 있습니다 . 예를 들어, 일반적인 Lisp은 동적 유형과 모든 인수의 동적 값을 전달합니다. Clojure는 모든 인수 (BTW는 매우 차갑고 매우 강력 함)의 임의 함수를 전달합니다.
그러나 동적 인수 기반 디스패치가있는 OO 언어는 모릅니다. 마틴 오더 스키는 그가 말했다 수도 있지만, 스칼라에 인수 기반의 파견을 추가하는 것을 고려 만 그는 같은 시간에 과부하를 제거 할 수있는 경우 와 모두 사용이 과부하 및 Java와 호환 것을 스칼라 기존 코드와 이전 버전과 호환 (그는 특히 스윙과 AWT를 언급 Java의 다소 복잡한 오버로드 규칙의 불쾌한 어두운 구석 사례를 거의 모두 수행하는 매우 복잡한 트릭을 재생합니다). Ruby에 인수 기반 디스패치를 추가하는 것에 대한 아이디어가 있었지만 이전 버전과 호환되는 방식으로 수행하는 방법을 알 수 없었습니다.
답변
나는 당신이 이것을 할 수있는 능력을 찾고 있다고 가정합니다.
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'
희망이 도움이
답변
다른 유형의 인수를 구별 할 수있는 정적 유형의 언어에서는 메소드 오버로드가 의미가 있습니다.
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
답변
루비에 왜 메소드 오버로드가 없는지에 대한 질문에는 답하지 않지만, 타사 라이브러리가이를 제공 할 수 있습니다.
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의 오버로드보다 강력 합니다.
이 기능을 사용하면 성능이 저하됩니다. 허용 할 수있는 양을 결정하려면 벤치 마크를 실행해야합니다.
답변
나는 종종 다음 구조를 수행합니다.
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를 사용할 수 있지만 실행을 최적화하려면 올바른 메소드를 직접 호출 할 수 있습니다.
또한 테스트를보다 명확하고 향상시킵니다.
답변
왜 질문의 측면에 대해 큰 대답이 있습니다. 그러나 다른 솔루션을 찾는 사람이라면 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!"