Почему ruby ​​не поддерживает перегрузку методов?

ruby

44755 просмотра

7 ответа

Вместо поддержки перегрузки методов Ruby перезаписывает существующие методы. Кто-нибудь может объяснить, почему язык был разработан таким образом?

Автор: Rambo Источник Размещён: 12.11.2019 09:06

Ответы (7)


157 плюса

Решение

Перегрузка методов может быть достигнута путем объявления двух методов с одинаковым именем и разными сигнатурами. Эти разные подписи могут быть

  1. Аргументы с разными типами данных, например: method(int a, int b) vs method(String a, String b)
  2. Переменное количество аргументов, например: method(a) vs method(a, b)

Мы не можем добиться перегрузки метода, используя первый способ, потому что в ruby ​​( динамически типизированном языке ) отсутствует объявление типа данных . Таким образом, единственный способ определить вышеупомянутый метод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 ​​должен поддерживать один метод в цепочке поиска метода с уникальным именем.

Автор: nkm Размещён: 21.02.2012 07:38

204 плюса

«Перегрузка» - это термин, который просто не имеет смысла в Ruby. Это в основном является синонимом «статического аргумента на основе отправки», но Рубин не имеет статическую отправку вообще . Итак, причина, по которой Ruby не поддерживает статическую диспетчеризацию на основе аргументов, заключается в том, что она не поддерживает статическую диспетчеризацию, точка. Он не поддерживает статическую диспетчеризацию любого типа , основанную на аргументах или иным образом.

Теперь, если вы не на самом деле конкретно спрашивать о перегрузке, но , может быть , о динамическом аргументе на основе отправки, то ответ: потому что Мацы не реализовали его. Потому что никто не удосужился предложить это. Потому что никто не удосужился реализовать это.

В целом, динамическая диспетчеризация на языке аргументов в языке с необязательными аргументами и списками аргументов переменной длины очень трудно понять правильно, и еще сложнее сделать ее понятной. Даже в языках со статической диспетчеризацией, основанной на аргументах, и без дополнительных аргументов (например, в Java), для простого смертного иногда почти невозможно сказать, какая перегрузка будет выбрана.

В C # вы можете фактически закодировать любую проблему 3-SAT в разрешение перегрузки, что означает, что разрешение перегрузки в C # является NP-сложным.

Теперь попробуйте это с динамической диспетчеризацией, где у вас есть дополнительное измерение времени, которое нужно держать в голове.

Существуют языки, которые динамически распределяются на основе всех аргументов процедуры, в отличие от объектно-ориентированных языков, которые распределяются только по «скрытому» нулевому selfаргументу. Common Lisp, например, отправляет динамические типы и даже динамические значения всех аргументов. Clojure рассылает произвольную функцию от всех аргументов (что, кстати, чрезвычайно круто и чрезвычайно мощно).

Но я не знаю ни одного языка OO с динамической диспетчеризацией на основе аргументов. Мартин Одерски сказал, что он мог бы рассмотреть возможность добавления диспетчеризации на основе аргументов в Scala, но только если он может одновременно устранить перегрузку и быть обратно совместимым как с существующим кодом Scala, который использует перегрузку, так и с Java (особенно он упомянул Swing и AWT). которые разыгрывают очень сложные трюки, выполняющие практически все неприятные темные ситуации с довольно сложными правилами перегрузки Java). У меня были некоторые идеи по поводу добавления рассылки на основе аргументов в Ruby, но я никогда не мог понять, как сделать это обратно совместимым способом.

Автор: Jörg W Mittag Размещён: 21.02.2012 03:32

83 плюса

Я полагаю, вы ищете возможность сделать это:

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'

надеюсь, это поможет

Автор: Derek Ekins Размещён: 21.02.2012 06:31

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
Автор: bjelli Размещён: 21.02.2012 06:32

8 плюса

Это не отвечает на вопрос, почему в ruby ​​нет перегрузки методов, но сторонние библиотеки могут это предоставить.

Библиотека contract.ru позволяет перегружать. Пример адаптирован из учебника:

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), а не просто типы.

Вы увидите снижение производительности при использовании этого, хотя; вам придется запустить тесты, чтобы решить, сколько вы можете терпеть.

Автор: Kelvin Размещён: 18.03.2015 09:29

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: метод. Но если он хочет оптимизировать выполнение, он может напрямую вызвать правильный метод.

Кроме того, это делает ваши тесты яснее и лучше.

Автор: Dam Размещён: 26.05.2013 02:50

1 плюс

уже есть отличные ответы о том, почему сторона вопроса. однако, если кто-то ищет другие решения, обратите внимание на функциональную жемчужину, которая вдохновлена возможностями сопоставления с образцом Elixir .

     класс фу
       include Functional :: PatternMatching

       ## Конструктор Перегрузка
       defn (: initialize) {@name = 'baz'}
       defn (: initialize, _) {| name | @name = name.to_s}

       ## Перегрузка метода
       defn (: greet,: male) {
         ставит "Привет, сэр!"
       }

       defn (: greet,: female) {
         ставит "Привет, мэм!"
       }
     конец

     foo = Foo.new или Foo.new ('Bar')
     foo.greet (: male) => "Здравствуйте, сэр!"
     foo.greet (: female) => "Привет, мэм!"   
Автор: Oshan Wisumperuma Размещён: 06.01.2019 06:57
Вопросы из категории :
32x32