Вместо поддержки метода перегрузки Ruby перезаписывает существующие методы. Может ли кто-нибудь объяснить, почему язык был разработан таким образом?
Почему рубина не поддерживает перегрузку метода?
Ответ 1
Перегрузка метода может быть достигнута путем объявления двух методов с одинаковым именем и разными сигнатурами. Эти разные сигнатуры могут быть либо:
- Аргументы с разными типами данных, например:
method(int a, int b) vs method(String a, String b)
- Переменная количество аргументов, например:
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 должен поддерживать один метод в цепочке поиска метода с уникальным именем.
Ответ 2
"Перегрузка" - это термин, который просто не имеет смысла в Ruby. Это в основном синоним "статической аргументной отправки", но Ruby не имеет статической отправки вообще. Итак, причина, по которой Ruby не поддерживает статическую отправку на основе аргументов, заключается в том, что она не поддерживает статическую отправку, период. Он не поддерживает статическую отправку любого типа, будь то на основе аргументов или иначе.
Теперь, если вы фактически не спрашиваете о перегрузке, но, возможно, о динамической отправке на основе аргументов, тогда ответ таков: потому что Matz не реализовал его. Потому что никто не потрудился предложить это. Потому что никто не потрудился ее реализовать.
В общем, динамическая отправка на основе аргументов на языке с необязательными аргументами и списками аргументов переменной длины очень трудно получить право, и еще труднее сохранить его понятным. Даже в языках со статической отправкой на основе аргументов и без необязательных аргументов (например, Java, например), иногда почти невозможно сказать для простого смертного, который будет перегружен.
В С# вы можете фактически закодировать любую проблему с 3-SAT в разрешении перегрузки, что означает, что разрешение перегрузки в С# NP-hard.
Теперь попробуйте с динамической отправкой, где у вас есть дополнительное измерение времени, которое нужно держать в голове.
Существуют языки, динамически отправляемые на основе всех аргументов процедуры, в отличие от объектно-ориентированных языков, которые отправляют только "скрытый" нулевой аргумент self
. Общий Lisp, например, отправляет динамические типы и даже динамические значения всех аргументов. Clojure отправляет на произвольную функцию всех аргументов (что BTW чрезвычайно круто и чрезвычайно мощно).
Но я не знаю ни одного языка OO с динамической отправкой на основе аргументов. Мартин Одерски сказал, что он может рассмотреть возможность добавления отправки на основе аргументов в Scala, но только в том случае, если он может одновременно удалить перегрузку и быть обратно совместимым как с существующим кодом Scala, который использует перегрузку и совместим с Java (особенно упомянутые Swing и AWT, которые играют некоторые чрезвычайно сложные трюки, выполняющие почти все неприятные темные угловые случаи Java довольно сложных правил перегрузки). У меня были некоторые идеи о добавлении отправки на основе аргументов в Ruby, но я никогда не мог понять, как сделать это с обратной совместимостью.
Ответ 3
Я предполагаю, что вы ищете возможность сделать это:
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'
Надеюсь, что поможет
Ответ 4
Перегрузка метода имеет смысл на языке со статической типизацией, где вы можете различать разные типы аргументов
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
Ответ 5
Это не отвечает на вопрос, почему 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
), а не просто типы.
При этом вы увидите снижение производительности; вам придется запускать тесты, чтобы решить, сколько вы можете терпеть.
Ответ 6
Я часто делаю следующую структуру:
def method(param)
case param
when String
method_for_String(param)
when Type1
method_for_Type1(param)
...
else
#default implementation
end
end
Это позволяет пользователю объекта использовать чистый и понятный метод имя_ метода: Но если он хочет оптимизировать выполнение, он может напрямую вызвать правильный метод.
Кроме того, он делает ваши тестовые очистители и улучшает.