Я практикую программу Ruby для операций с 2D-координатами. Среди них меня смущает написание def +(other)
и def -(other)
. У меня есть следующие три вопроса:
это код
class Point
attr_accessor :x, :y
def initialize(x=0, y=0)
@x, @y = x, y
end
def inspect # p印出(x, y)
"(#{x}, #{y})"
end
def +(other)
self.class.new(x + other.x, y + other.y)
end
def -(other)
self.class.new(x - other.x, y - other.y)
end
end
point0 = Point.new(3, 6)
point1 = Point.new(1, 8)
p point0
p point1
p point0 + point1
p point0 - point1
в конце концов он напечатает
(3, 6)
(1, 8)
(4, 14)
(2, -2)
- Why is the method name equal to the operator name?
Почему нет? Почему символ, используемый для определять оператора, не должен быть символом, используемым для вызов? В конце концов, именно так мы делаем это для любого другого метода: если я хочу вызвать foo
, я определяю def foo
.
Разработчики языка могли выбрать любое произвольное имя. Но имеет смысл, чтобы имя метода было тем же символом, который вы используете для его вызова. Вы можете себе представить путаницу, если бы разработчики языка выбрали, например, символ -
для имени метода, который соответствует оператору +
?
Другие языки программирования делают другой выбор. Например, в C++ имя функции, соответствующей оператору +
, равно operator+
. В Python имя метода, соответствующего оператору +
, — __add__
. (Точнее, при встрече с выражением a + b
Python сначала попытается вызвать a.__add__(b)
, и если это не реализовано a
, то он попытается «обратное добавление» b.__radd__(a)
.)
В C# нет имени метода, соответствующего оператору, скорее, есть ключевое слово operator
, за которым следует символ, соответствующий оператору.
Если вы хотите узнать все подробности о том, как операторные выражения оцениваются в Ruby, я рекомендую ознакомиться с Разделом 11.4.3 Выражения унарных операторов и Разделом 11.4.4 Выражения бинарных операторов документа Спецификация ISO/IEC 30170:2012 Информационные технологии — Языки программирования — Ruby.
- Where are the parameters received? Passed from where?
Не совсем понятно, что вы хотите этим сказать. параметр — это что-то вроде «дыры» или заполнителя в определении метода. Затем эта «дыра» заполняется аргумент, когда вы фактически вызываете метод. Например, здесь:
def foo(a, b) a + b end
a
и b
являются параметры (точнее, обязательные позиционные параметры, они обязательное, потому что вы должны передать аргумент для них, и они позиционный, потому что какой аргумент привязывается к какому параметру в списке параметров, зависит от позиции аргумента в списке аргументов ). На самом деле вы не знаете, каким будет результат этого метода, потому что вы не знаете, что такое a
и b
. Это заполнители для значений, которые будут заполнены при вызове метода:
foo(2, 3)
Здесь 2
и 3
— это аргументы (точнее, позиционные аргументы). Аргумент 2
привязывается к параметру a
, потому что 2
— это первый позиционный аргумент в списке аргументов, а a
— первый позиционный параметр в списке параметров. Аргумент 3
привязывается к параметру b
, потому что 3
— второй позиционный аргумент в списке аргументов, а b
— второй позиционный параметр в списке параметров.
Итак, в конечном итоге выполняется код для этого конкретного вызова метода (замена a
на 2
и b
на 3
):
2 + 3
Обратите внимание: это упрощенное объяснение. Ментальная модель замены каждого вхождения параметра в теле определения метода выражением аргумента является хорошим первым приближением, но на самом деле это не то, что делает Ruby. В частности, та мысленная модель, которую я только что описал, соответствует вызов по имени стратегия оценки, тогда как Ruby на самом деле использует особый случай вызов по значению стратегия оценки, называемый совместное использование вызова по объекту.
Вы можете наблюдать разницу в этом коде:
def bar(a) a + a end
bar((puts "Hello"; 23))
# Hello
#=> 46
В ментальной модели «заменить каждое вхождение параметра выражением аргумента», которая соответствует вызову по имени, код будет выглядеть следующим образом:
(puts "Hello"; 23) + (puts "Hello"; 23)
# Hello
# Hello
#=> 46
и Hello
будет напечатано дважды.
Однако при вызове по значению и совместном вызове по объекту выражение аргумента оценивается перед вызовом метода, и результат этой оценки передается, поэтому фактическое выполнение кода выглядит примерно так:
__fresh_variable_with_unspeakable_name__ = (puts "Hello"; 23)
# Hello
__fresh_variable_with_unspeakable_name__ + __fresh_variable_with_unspeakable_name__
#=> 46
Если вы хотите узнать все подробности о том, как аргументы методов оцениваются в Ruby, я рекомендую ознакомиться с разделом 11.3 Выражения вызова метода, в частности с разделом 11.3.1 Общее описание и разделом 11.3.2 Аргументы метода стандарта ISO/IEC 30170:2012 Информационные технологии — Языки программирования — Ruby. Технические характеристики.
Также есть некоторая (к сожалению, неполная) информация, которую можно найти в Ruby Spec Suite, также известный как ruby/spec
, в частности, в language/def_spec.rb
и language/method_spec.rb
.
- Why is the parameter other called other, and what is its value and transmission process?
Параметр other
называется other
, потому что именно так его назвал автор этого фрагмента кода. Они могли бы назвать это a
, или b
, или x
, или y
, или foo
, или bar
, или i_dont_have_the_slightest_idea_what_to_call_this_parameter_so_i_am_just_choosing_something_totally_random
. Если вы хотите знать, что Зачем автор этого фрагмента кода решил назвать его other
, вам нужно будет спросить автора этого фрагмента кода.
other
— довольно популярное имя для «другого» операнда определения бинарного оператора не только в Ruby, как вы можете видеть, например, в документации Python. Это имеет смысл, если вы прочитаете это вслух: в объектно-ориентированном языке программирования, таком как Ruby или Python, где операторы интерпретируются как отправленные одному из операндов, один из двух операндов бинарного оператора всегда будет self
или this
и "другой" операнд будет... ну... "другой" операнд. Так что назвать его other
вполне естественно. В Ruby это закреплено в некоторых руководствах по стилю, например, в Руководство по стилю Rubocop Ruby.
В
point0 + point1
point0
— получатель, аpoint1
— аргумент. Большинство операторов являются реальными методами в Ruby. Полный список и несколько примеров см. в документации по методы.