ทำไมทับทิมไม่รองรับเมธอดมากเกินไป?


146

แทนที่จะสนับสนุนเมธอด overloading Ruby จะเขียนทับเมธอดที่มีอยู่ ทุกคนสามารถอธิบายได้หรือไม่ว่าทำไมภาษานี้ได้รับการออกแบบในลักษณะนี้?

คำตอบ:


166

วิธีการบรรทุกเกินพิกัดสามารถทำได้โดยการประกาศสองวิธีที่มีชื่อเดียวกันและลายเซ็นที่แตกต่างกัน ลายเซ็นที่แตกต่างเหล่านี้สามารถเป็นได้ทั้ง

  1. อาร์กิวเมนต์ที่มีชนิดข้อมูลต่าง ๆ เช่น: method(int a, int b) vs method(String a, String b)
  2. จำนวนอาร์กิวเมนต์ที่แปรผันเช่น: method(a) vs method(a, b)

เราไม่สามารถใช้วิธีการมากเกินไปได้โดยใช้วิธีแรกเนื่องจากไม่มีการประกาศชนิดข้อมูลเป็นทับทิม ( ภาษาที่พิมพ์แบบไดนามิก ) ดังนั้นวิธีเดียวที่จะกำหนดวิธีการข้างต้นคือ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

"การบรรทุกเกินพิกัด" เป็นคำศัพท์ที่ไม่มีความหมายในรูบี มันเป็นพื้นไวพจน์ "ส่งข้อโต้แย้งตามคงที่" แต่ทับทิมไม่ได้มีการจัดส่งแบบคงที่ทั้งหมด ดังนั้นเหตุผลที่ทับทิมไม่สนับสนุนการแจกแจงแบบคงที่ตามข้อโต้แย้งเพราะมันไม่สนับสนุนการแจกแจงคงที่ระยะเวลา มันไม่รองรับการส่งแบบคงที่ทุกชนิดไม่ว่าจะตามอาร์กิวเมนต์หรืออย่างอื่น

ตอนนี้ถ้าคุณไม่ได้ถามเฉพาะเกี่ยวกับการบรรทุกเกินพิกัด แต่อาจเกี่ยวกับการจัดส่งตามอาร์กิวเมนต์แบบไดนามิกคำตอบคือ: เนื่องจาก Matz ไม่ได้ใช้งาน เพราะไม่มีใครสนใจที่จะเสนอ เพราะไม่มีใครสนใจที่จะใช้มัน

โดยทั่วไปการจัดส่งอาร์กิวเมนต์ตามแบบไดนามิกในภาษาที่มีการขัดแย้งที่ไม่จำเป็นและรายการอาร์กิวเมนต์ตัวแปรที่มีความยาวเป็นอย่างมากยากที่จะได้รับสิทธิและแม้ยากที่จะให้มันเข้าใจ แม้ในภาษาที่มีการแจกแจงแบบอิงอาร์กิวเมนต์แบบคงที่และไม่มีอาร์กิวเมนต์ที่เป็นตัวเลือก (เช่น Java เป็นต้น) บางครั้งแทบเป็นไปไม่ได้เลยที่จะบอกว่าเป็นเพียงมนุษย์ธรรมดาซึ่งจะมีการเลือกเกินพิกัด

ใน C # คุณสามารถเข้ารหัสปัญหา 3-SAT ใด ๆลงในการแก้ปัญหาการโอเวอร์โหลดซึ่งหมายความว่าการแก้ไขการโอเวอร์โหลดใน C # นั้นเป็น NP-hard

ตอนนี้ให้ลองด้วยการจัดส่งแบบไดนามิกที่คุณมีมิติเวลาเพิ่มเติมเพื่อให้อยู่ในหัวของคุณ

มีภาษาที่จัดส่งแบบไดนามิกตามข้อโต้แย้งทั้งหมดของขั้นตอนเมื่อเทียบกับภาษาเชิงวัตถุซึ่งส่งเฉพาะในselfข้อโต้แย้งซีร็อ ธ "ซ่อน" ยกตัวอย่างเช่น Common Lisp ในประเภทแบบไดนามิกและแม้กระทั่งค่าแบบไดนามิกของการขัดแย้งทั้งหมด Clojure ยื้อกับฟังก์ชั่นโดยพลการของข้อโต้แย้งทั้งหมด (ซึ่ง BTW จะเย็นมากและมีประสิทธิภาพมาก)

แต่ฉันไม่รู้ภาษา OO ใด ๆ กับการแจกจ่ายตามอาร์กิวเมนต์แบบไดนามิก มาร์ตินโอเดอร์ สกี บอกว่าเขาอาจพิจารณาเพิ่มการจัดส่งอาร์กิวเมนต์ที่ใช้ในการ Scala แต่เพียงว่าเขาสามารถลบมากไปในเวลาเดียวกันและจะย้อนกลับเข้ากันได้ทั้งที่มีอยู่รหัสสกาล่าที่ใช้มากไปและเข้ากันได้กับ Java (เขากล่าวถึงโดยเฉพาะอย่างยิ่ง Swing และ AWT ซึ่งเล่นเทคนิคที่ซับซ้อนมากบางอย่างออกกำลังกายสวยมากทุกกรณีมุมมืดของกฎการโอเวอร์โหลดค่อนข้างซับซ้อนของ Java) ฉันมีความคิดบางอย่างเกี่ยวกับการเพิ่มการส่งแบบอิงอาร์กิวเมนต์ไปที่ Ruby แต่ฉันไม่สามารถหาวิธีที่จะทำในลักษณะที่เข้ากันได้ย้อนหลัง


5
นี่คือคำตอบที่ถูกต้อง คำตอบที่ยอมรับเกินความจริง C # DOES ได้ตั้งชื่อพารามิเตอร์และพารามิเตอร์ทางเลือกและยังคงใช้การโอเวอร์โหลดดังนั้นจึงไม่ง่ายเหมือน " def method(a, b = true)ไม่ทำงาน" ดังนั้นการโอเวอร์โหลดวิธีจึงเป็นไปไม่ได้ " มันไม่ใช่; มันเป็นเรื่องยาก ฉันพบคำตอบนี้ให้ข้อมูลจริงๆ
tandrewnichols

1
@tandrewnichols: เพียงแค่ให้ความคิดว่า "ยาก" ความละเอียดเกินพิกัดอยู่ใน C # ... เป็นไปได้ที่จะเข้ารหัสปัญหา 3-SAT ใด ๆ ที่เป็นความละเอียดเกินพิกัดใน C # และมีคอมไพเลอร์แก้ปัญหาได้ในเวลารวบรวมจึงทำให้การแก้ไขเกินพิกัดใน C # NP - ยาก (3-SAT เป็นที่รู้จักกันว่าเป็น NP-complete) ตอนนี้จินตนาการว่าต้องทำเช่นนั้นไม่ใช่ครั้งเดียวต่อไซต์การโทรในเวลารวบรวม แต่หนึ่งครั้งต่อวิธีการโทรสำหรับทุกวิธีการโทรตอนรันไทม์
Jörg W Mittag

1
@ JörgWMittagคุณสามารถรวมลิงค์ที่แสดงการเข้ารหัสของปัญหา 3-SAT ในกลไกการแก้ปัญหาโอเวอร์โหลดได้หรือไม่?
Squidly


2
ดูเหมือนว่า "overloading" เป็นคำพ้องสำหรับ "การแจกจ่ายตามอาร์กิวเมนต์แบบคงที่" การส่งตามอาร์กิวเมนต์แบบคงที่เป็นเพียงการใช้งานมากที่สุดของการโอเวอร์โหลด การบรรทุกเกินพิกัดเป็นคำที่ไม่เชื่อเรื่องการใช้งานหมายถึง "ชื่อวิธีการเดียวกัน แต่การใช้งานที่แตกต่างกันในขอบเขตเดียวกัน"
snovity

85

ฉันคิดว่าคุณกำลังมองหาความสามารถในการทำเช่นนี้:

def my_method(arg1)
..
end

def my_method(arg1, arg2)
..
end

Ruby สนับสนุนสิ่งนี้ในวิธีที่แตกต่าง:

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)

ความแตกต่างแรกไม่มีอยู่ในทับทิม Ruby ใช้การพิมพ์แบบไดนามิกหรือ "การพิมพ์เป็ด" ความแตกต่างที่สองสามารถจัดการได้โดยอาร์กิวเมนต์เริ่มต้นหรือโดยทำงานกับอาร์กิวเมนต์:

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

นี่ไม่ได้ตอบคำถามที่ว่าทำไม ruby ​​ไม่มีเมธอดมากเกินไป แต่ห้องสมุดบุคคลที่สามสามารถให้บริการได้

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

โปรดทราบว่าสิ่งนี้มีประสิทธิภาพมากกว่าการบรรทุกเกินพิกัดของ Java เพราะคุณสามารถระบุค่าที่จะจับคู่ (เช่น1) ไม่ใช่เพียงพิมพ์

คุณจะเห็นประสิทธิภาพลดลงโดยใช้สิ่งนี้แม้ว่า; คุณจะต้องใช้การวัดประสิทธิภาพเพื่อตัดสินใจว่าคุณสามารถทนได้เท่าไหร่


1
ในแอปพลิเคชันในโลกแห่งความจริงด้วย IO ชนิดใดคุณจะมีการชะลอตัวเพียง 0.1-10% (ขึ้นอยู่กับชนิดของ IO)
Waterlink

1

ฉันมักจะทำโครงสร้างต่อไปนี้:

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

    ...

    else
         #default implementation
    end
end

สิ่งนี้อนุญาตให้ผู้ใช้ของวัตถุใช้เมธอด clean_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.