Tại sao không quá tải phương pháp hỗ trợ ruby?


146

Thay vì hỗ trợ phương thức nạp chồng, Ruby ghi đè lên các phương thức hiện có. Bất cứ ai có thể giải thích tại sao ngôn ngữ được thiết kế theo cách này?

Câu trả lời:


166

Quá tải phương thức có thể đạt được bằng cách khai báo hai phương thức có cùng tên và chữ ký khác nhau. Những chữ ký khác nhau có thể là một trong hai

  1. Đối số với các loại dữ liệu khác nhau, ví dụ: method(int a, int b) vs method(String a, String b)
  2. Số lượng đối số biến, ví dụ: method(a) vs method(a, b)

Chúng ta không thể đạt được quá tải phương thức bằng cách đầu tiên vì không có khai báo kiểu dữ liệu trong ruby ​​( ngôn ngữ gõ động ). Vì vậy, cách duy nhất để xác định phương pháp trên làdef(a,b)

Với tùy chọn thứ hai, có vẻ như chúng ta có thể đạt được quá tải phương thức, nhưng chúng ta không thể. Giả sử tôi có hai phương thức với số lượng đối số khác nhau,

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.

Vì vậy, ruby ​​cần duy trì một phương thức trong phương thức tra cứu chuỗi với một tên duy nhất.


22
@ Jorg W Câu trả lời của Mittag, chôn sâu bên dưới, chắc chắn đáng đọc.
dùng2398029

1
Và câu trả lời của @Derek Ekins, được chôn vùi hơn nữa, cung cấp một giải pháp thay thế
Cyril Duchon-Doris

Lưu ý với những người chơi ruby ​​trong tương lai ... FWIW bạn có thể làm điều này với các hợp đồng gem egonschiele.github.io/contuces.ruby/#method-overloading
kỹ

214

"Quá tải" là một thuật ngữ đơn giản thậm chí không có ý nghĩa trong Ruby. Nó cơ bản là một từ đồng nghĩa với "văn lập luận dựa trên tĩnh", nhưng Ruby không công văn tĩnh gì cả . Vì vậy, lý do tại sao Ruby không hỗ trợ công văn tĩnh dựa trên các đối số, là vì nó không hỗ trợ công văn tĩnh, thời gian. Nó không hỗ trợ công văn tĩnh dưới bất kỳ hình thức nào , cho dù dựa trên đối số hay cách khác.

Bây giờ, nếu bạn không thực sự hỏi cụ thể về việc quá tải, nhưng có thể về công văn dựa trên đối số động , thì câu trả lời là: vì Matz đã không thực hiện nó. Bởi vì không ai bận tâm đề xuất nó. Bởi vì không ai bận tâm để thực hiện nó.

Nói chung, công văn dựa trên đối số động trong một ngôn ngữ với các đối số tùy chọn và danh sách đối số có độ dài thay đổi, rất khó để có được quyền, và thậm chí còn khó hơn để giữ cho nó dễ hiểu. Ngay cả trong các ngôn ngữ có công văn dựa trên đối số tĩnh và không có đối số tùy chọn (ví dụ như Java), đôi khi hầu như không thể nói cho một phàm nhân, quá tải sẽ được chọn.

Trong C #, bạn thực sự có thể mã hóa bất kỳ vấn đề 3-SAT nào thành độ phân giải quá tải, điều đó có nghĩa là độ phân giải quá tải trong C # là NP-hard.

Bây giờ hãy thử điều đó với công văn động , nơi bạn có thứ nguyên thời gian bổ sung để giữ trong đầu.

Có những ngôn ngữ tự động gửi dựa trên tất cả các đối số của một thủ tục, trái ngược với các ngôn ngữ hướng đối tượng, chỉ gửi các selfđối số zeroth "ẩn" . Ví dụ, Lisp chung gửi các kiểu động và thậm chí các giá trị động của tất cả các đối số. Clojure gửi đến một hàm tùy ý của tất cả các đối số (mà BTW cực kỳ hay và cực kỳ mạnh mẽ).

Nhưng tôi không biết bất kỳ ngôn ngữ OO nào với công văn dựa trên đối số động. Martin Oderky nói rằng anh ta có thể xem xét việc thêm công văn dựa trên đối số vào Scala, nhưng chỉ khi anh ta có thể loại bỏ quá tải cùng một lúc tương thích ngược với cả mã Scala hiện có sử dụng quá tải và tương thích với Java (anh ta đặc biệt đề cập đến Swing và AWT chơi một số thủ thuật cực kỳ phức tạp thực hiện khá nhiều trường hợp góc tối khó chịu của các quy tắc quá tải khá phức tạp của Java). Bản thân tôi đã có một số ý tưởng về việc thêm công văn dựa trên đối số vào Ruby, nhưng tôi không bao giờ có thể tìm ra cách thực hiện theo cách tương thích ngược.


5
Đây là câu trả lời đúng. Câu trả lời được chấp nhận quá mức. C # DOES đã đặt tên tham số và tham số tùy chọn và vẫn thực hiện quá tải, do đó không đơn giản như " def method(a, b = true)sẽ không hoạt động, do đó quá tải phương thức là không thể." Nó không thể; thật khó khăn Tôi tìm thấy câu trả lời NÀY thực sự nhiều thông tin, tuy nhiên.
tandrewnichols

1
@tandrewnichols: chỉ cần đưa ra một số ý tưởng về độ phân giải quá tải "khó khăn" trong C #, có thể mã hóa bất kỳ vấn đề 3-SAT nào như độ phân giải quá tải trong C # và để trình biên dịch giải quyết nó trong thời gian biên dịch, do đó thực hiện giải quyết quá tải trong C # NP -hard (3-SAT được gọi là NP-đầy đủ). Bây giờ hãy tưởng tượng bạn phải làm điều đó không chỉ một lần trên mỗi trang web cuộc gọi vào thời gian biên dịch mà một lần cho mỗi cuộc gọi phương thức cho mỗi cuộc gọi phương thức duy nhất khi chạy.
Jörg W Mittag

1
@ JörgWMittag Bạn có thể bao gồm một liên kết hiển thị mã hóa của vấn đề 3-SAT trong cơ chế giải quyết quá tải không?
Mực


2
Nó không giống như "quá tải" là một từ đồng nghĩa với "công văn dựa trên đối số tĩnh". Công văn dựa trên đối số tĩnh chỉ đơn giản là việc thực hiện quá tải phổ biến nhất. Quá tải là một thuật ngữ bất khả tri thực hiện có nghĩa là "cùng tên phương thức nhưng thực hiện khác nhau trong cùng một phạm vi".
snovity

85

Tôi đoán bạn đang tìm kiếm khả năng để làm điều này:

def my_method(arg1)
..
end

def my_method(arg1, arg2)
..
end

Ruby hỗ trợ điều này theo một cách khác:

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

Một mô hình phổ biến cũng là để vượt qua trong các tùy chọn dưới dạng băm:

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'

Mong rằng sẽ giúp


15
+1 để cung cấp những gì nhiều người trong chúng ta chỉ muốn biết: cách làm việc với số lượng đối số thay đổi trong các phương thức Ruby.
tro999

3
Câu trả lời này có thể được hưởng lợi từ thông tin bổ sung về các đối số tùy chọn. (Và có lẽ cũng được đặt tên là đối số, bây giờ đó là một điều.)
Ajedi32

9

Quá tải phương thức có ý nghĩa trong một ngôn ngữ với kiểu gõ tĩnh, nơi bạn có thể phân biệt giữa các loại đối số khác nhau

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

cũng như giữa số lượng đối số khác nhau

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

Sự phân biệt đầu tiên không tồn tại trong ruby. Ruby sử dụng kiểu gõ động hoặc "gõ vịt". Sự khác biệt thứ hai có thể được xử lý bằng các đối số mặc định hoặc bằng cách làm việc với các đối số:

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


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

Điều này không có gì để làm với gõ mạnh. Ruby được đánh máy mạnh mẽ, sau tất cả.
Jörg W Mittag

8

Điều này không trả lời câu hỏi tại sao ruby ​​không bị quá tải phương thức, nhưng các thư viện bên thứ ba có thể cung cấp nó.

Các contracts.ruby thư viện cho phép quá tải. Ví dụ được điều chỉnh từ hướng dẫn:

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

Lưu ý rằng điều này thực sự mạnh hơn so với quá tải của Java, bởi vì bạn có thể chỉ định các giá trị khớp với (ví dụ 1), không chỉ các loại.

Bạn sẽ thấy hiệu suất giảm khi sử dụng điều này mặc dù; bạn sẽ phải chạy điểm chuẩn để quyết định mức độ bạn có thể chịu đựng được.


1
Trong các ứng dụng trong thế giới thực với bất kỳ loại IO nào, bạn sẽ chỉ bị chậm lại 0,1-10% (tùy thuộc vào loại IO nào).
Waterlink

1

Tôi thường làm cấu trúc sau:

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

    ...

    else
         #default implementation
    end
end

Điều này cho phép người dùng của đối tượng sử dụng phương thức rõ ràng và phương thức rõ ràng: Nếu anh ta muốn tối ưu hóa thực thi, anh ta có thể gọi trực tiếp phương thức chính xác.

Ngoài ra, nó làm cho bài kiểm tra của bạn rõ ràng hơn và betters.


1

đã có câu trả lời tuyệt vời về lý do tại sao bên của câu hỏi. tuy nhiên, nếu bất cứ ai tìm kiếm các giải pháp khác hãy kiểm tra đá quý ruby chức năng được lấy cảm hứng từ các tính năng khớp mẫu của 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!"   
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.