Mengapa metode dukungan ruby ​​tidak kelebihan?

146

Alih-alih mendukung metode overload, Ruby menimpa metode yang ada. Adakah yang bisa menjelaskan mengapa bahasanya dirancang seperti ini?

Rambo
sumber

Jawaban:

166

Metode overloading dapat dicapai dengan mendeklarasikan dua metode dengan nama yang sama dan tanda tangan yang berbeda. Tanda tangan yang berbeda ini bisa berupa,

  1. Argumen dengan tipe data yang berbeda, misalnya: method(int a, int b) vs method(String a, String b)
  2. Jumlah variabel argumen, misalnya: method(a) vs method(a, b)

Kami tidak dapat mencapai metode overload menggunakan cara pertama karena tidak ada deklarasi tipe data di ruby ​​( bahasa pengetikan dinamis ). Jadi satu-satunya cara untuk mendefinisikan metode di atas adalahdef(a,b)

Dengan opsi kedua, sepertinya kita bisa mencapai metode overloading, tetapi kita tidak bisa. Katakanlah saya memiliki dua metode dengan jumlah argumen yang berbeda,

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.

Jadi ruby ​​perlu mempertahankan satu metode dalam metode mencari rantai dengan nama yang unik.

nkm
sumber
22
@ Jörg W Mittag jawaban, terkubur jauh di bawah, pasti layak dibaca.
user2398029
1
Dan jawaban @Dekek Ekins, terkubur lebih jauh, memberikan alternatif
Cyril Duchon-Doris
Catatan untuk rubyist masa depan ... FWIW Anda dapat melakukan ini dengan kontrak permata egonschiele.github.io/contracts.ruby/#method-overloading
engineerDave
214

"Kelebihan beban" adalah istilah yang bahkan tidak masuk akal di Ruby. Ini pada dasarnya adalah sinonim untuk "pengiriman berbasis argumen statis", tetapi Ruby tidak memiliki pengiriman statis sama sekali . Jadi, alasan mengapa Ruby tidak mendukung pengiriman statis berdasarkan argumen, adalah karena Ruby tidak mendukung pengiriman statis, titik. Itu tidak mendukung pengiriman statis dalam bentuk apa pun , baik berbasis argumen atau sebaliknya.

Sekarang, jika Anda tidak benar-benar secara spesifik bertanya tentang kelebihan muatan, tetapi mungkin tentang pengiriman berbasis argumen dinamis , maka jawabannya adalah: karena Matz tidak mengimplementasikannya. Karena tidak ada orang lain yang mau mengusulkannya. Karena tidak ada orang lain yang peduli untuk mengimplementasikannya.

Secara umum, pengiriman berbasis argumen dinamis dalam bahasa dengan argumen opsional dan daftar argumen panjang variabel, sangat sulit untuk diperbaiki, dan bahkan lebih sulit untuk membuatnya dimengerti. Bahkan dalam bahasa dengan pengiriman berbasis argumen statis dan tanpa argumen opsional (seperti Java, misalnya), kadang-kadang hampir tidak mungkin untuk mengatakan untuk manusia biasa, yang kelebihan beban akan dipilih.

Dalam C #, Anda benar-benar dapat menyandikan masalah 3-SAT ke resolusi kelebihan beban, yang berarti bahwa resolusi kelebihan beban di C # adalah NP-hard.

Sekarang coba dengan pengiriman dinamis , di mana Anda memiliki dimensi waktu tambahan untuk tetap di kepala Anda.

Ada bahasa yang secara dinamis mengirimkan berdasarkan semua argumen prosedur, sebagai lawan dari bahasa berorientasi objek, yang hanya mengirim pada "nol" selfargumen yang tersembunyi . Lisp umum, misalnya, mengirimkan tipe dinamis dan bahkan nilai dinamis semua argumen. Clojure mengirimkan fungsi sewenang-wenang dari semua argumen (yang BTW sangat keren dan sangat kuat).

Tapi saya tidak tahu bahasa OO dengan pengiriman berbasis argumen yang dinamis. Martin Odersky mengatakan bahwa ia mungkin mempertimbangkan untuk menambahkan pengiriman berbasis argumen ke Scala, tetapi hanya jika ia dapat menghapus kelebihan muatan pada saat yang sama dan kompatibel dengan keduanya dengan kode Scala yang ada yang menggunakan kelebihan muatan dan kompatibel dengan Java (ia terutama menyebut Swing dan AWT yang memainkan beberapa trik yang sangat kompleks dengan melakukan hampir setiap kasus sudut gelap yang buruk dari aturan overload yang agak rumit dari Java) Saya sendiri memiliki beberapa ide tentang menambahkan pengiriman berbasis argumen ke Ruby, tetapi saya tidak pernah tahu bagaimana melakukannya dengan cara yang kompatibel dengan mundur.

Jörg W Mittag
sumber
5
Ini adalah jawaban yang benar. Jawaban yang diterima terlalu menyederhanakan. C # APAKAH telah menamai parameter dan parameter opsional dan masih menerapkan overloading, jadi itu tidak sesederhana " def method(a, b = true)tidak akan bekerja, oleh karena itu metode overloading tidak mungkin." Ini bukan; itu hanya sulit. Namun, saya menemukan jawaban ini sangat informatif.
tandrewnichols
1
@tandrewnichols: hanya untuk memberikan beberapa ide betapa "sulit" resolusi kelebihan dalam C # ... dimungkinkan untuk menyandikan masalah 3-SAT sebagai resolusi kelebihan dalam C # dan meminta kompilator menyelesaikannya pada waktu kompilasi, sehingga membuat resolusi kelebihan dalam C # NP -hard (3-SAT dikenal sebagai NP-complete). Sekarang bayangkan harus melakukan itu tidak sekali per panggilan situs pada waktu kompilasi tetapi sekali per panggilan metode untuk setiap panggilan metode tunggal saat runtime.
Jörg W Mittag
1
@ JörgWMittag Bisakah Anda menyertakan tautan yang menunjukkan penyandian masalah 3-SAT dalam mekanisme resolusi kelebihan beban?
Squidly
2
Itu tidak terlihat seperti "kelebihan" adalah sinonim untuk "pengiriman berbasis argumen statis". Pengiriman berbasis argumen statis hanyalah implementasi overloading yang paling umum. Overloading adalah istilah agnostik implementasi yang berarti "nama metode yang sama tetapi implementasi yang berbeda dalam cakupan yang sama".
snovity
85

Saya kira Anda sedang mencari kemampuan untuk melakukan ini:

def my_method(arg1)
..
end

def my_method(arg1, arg2)
..
end

Ruby mendukung ini dengan cara yang berbeda:

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

Pola umum juga untuk meneruskan opsi sebagai hash:

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'

Semoga itu bisa membantu

Derek Ekins
sumber
15
+1 untuk memberikan apa yang ingin kita ketahui: cara bekerja dengan sejumlah variabel argumen dalam metode Ruby.
ashes999
3
Jawaban ini mungkin mendapat manfaat dari informasi tambahan tentang argumen opsional. (Dan mungkin juga menyebut argumen, sekarang itu adalah sesuatu.)
Ajedi32
9

Metode overloading masuk akal dalam bahasa dengan pengetikan statis, di mana Anda dapat membedakan berbagai jenis argumen

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

serta antara jumlah argumen yang berbeda

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

Perbedaan pertama tidak ada di ruby. Ruby menggunakan pengetikan dinamis atau "pengetikan bebek". Perbedaan kedua dapat ditangani oleh argumen default atau dengan bekerja dengan argumen:

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


def f(*args)
  case args.size
  when  
     ...
  when 2
    ...
  when 3
    ...
  end
end
bjelli
sumber
Ini tidak ada hubungannya dengan pengetikan yang kuat. Ruby adalah sangat diketik, setelah semua.
Jörg W Mittag
8

Ini tidak menjawab pertanyaan mengapa ruby ​​tidak memiliki kelebihan metode, tetapi perpustakaan pihak ketiga dapat menyediakannya.

The contracts.ruby perpustakaan memungkinkan overloading. Contoh diadaptasi dari tutorial:

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

Perhatikan bahwa ini sebenarnya lebih kuat daripada kelebihan beban Java, karena Anda dapat menentukan nilai yang cocok (misalnya 1), bukan hanya tipe.

Anda akan melihat penurunan kinerja menggunakan ini; Anda harus menjalankan tolok ukur untuk memutuskan seberapa banyak Anda bisa mentolerir.

Kelvin
sumber
1
Dalam aplikasi dunia nyata dengan segala jenis IO, Anda hanya akan mengalami penurunan 0,1-10% (tergantung pada jenis IO apa).
Waterlink
1

Saya sering melakukan struktur berikut:

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

    ...

    else
         #default implementation
    end
end

Ini memungkinkan pengguna objek untuk menggunakan method_name: metode yang bersih dan jelas. Tetapi jika ia ingin mengoptimalkan eksekusi, ia dapat langsung memanggil metode yang benar.

Juga, itu membuat tes Anda lebih jelas dan lebih baik.

Bendungan
sumber
1

sudah ada jawaban bagus mengapa dari sisi pertanyaan. Namun, jika ada yang mencari solusi lain, periksa permata fungsional-ruby yang terinspirasi oleh fitur pencocokan pola 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!"   
Oshan Wisumperuma
sumber