I'm practicing a Ruby program for 2D coordinate operations. Among them, the writing of def (other)
and def -(other)
confuses me. I have the following three questions:
- Why is the method name equal to the operator name?
- Where are the parameters received? Passed from where?
- Why is the parameter other called other, and what is its value and transmission process?
This is code
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
it will eventually print
(3, 6)
(1, 8)
(4, 14)
(2, -2)
CodePudding user response:
- Why is the method name equal to the operator name?
Why not? Why should the symbol used to define the operator not be the symbol used to call it? That's how we do it for every other method, after all: if I want to call foo
, I define def foo
.
The Language designers could have chosen any arbitrary name. But it just makes sense to have the name of the method be the same symbol that you use to call it. Can you imagine the confusion if the language designers had chosen, for example, the symbol -
for the name of the method which corresponds to the
operator?
Other programming languages make different choices. For example, in C , the name of the function corresponding to the
operator is operator
. In Python, the name of the method corresponding to the
operator is __add__
. (More precisely, when encountering the expression a b
, Python will first try calling a.__add__(b)
and if that is not implemented by a
, then it will try the "reverse add" b.__radd__(a)
.)
In C#, there is no method name which corresponds to the operator, rather, there is an operator
keyword followed by the symbol corresponding to the operator.
If you want to know all the nitty-gritty details about how operator expressions are evaluated in Ruby, I recommend checking out Section 11.4.3 Unary operator expressions and Section 11.4.4 Binary operator expressions of the ISO/IEC 30170:2012 Information technology — Programming languages — Ruby specification.
- Where are the parameters received? Passed from where?
It's not quite clear what you mean by this. A parameter is kind of like a "hole" or a placeholder in the method definition. This "hole" then gets filled in with an argument when you actually call the method. For example, here:
def foo(a, b) a b end
a
and b
are parameters (more precisely, mandatory positional parameters, they are mandatory because you have to pass an argument for them and they are positional because which argument gets bound to which parameter in the parameter list depends on the position of the argument in the argument list). You don't actually know what the result of this method will be because you don't know what a
and b
are. They are placeholders for values that will be filled in when you call the method:
foo(2, 3)
Here, 2
and 3
are arguments (more precisely, positional arguments). The argument 2
gets bound to the parameter a
because 2
is the first positional argument in the argument list and a
is the first positional parameter in the parameter list. The argument 3
gets bound to the parameter b
because 3
is the second positional argument in the argument list and b
is the second positional parameter in the parameter list.
So, the code that gets ultimately executed for this particular method call is (replacing a
with 2
and b
with 3
):
2 3
Please note: this is a simplified explanation. The mental model of replacing every occurrence of the parameter in the method definition body with the argument expression is a good first approximation, but it is not actually what Ruby does. In particular, that mental model I just described corresponds to the call-by-name evaluation strategy, whereas Ruby actually uses a special case of the call-by-value evaluation strategy called call-by-object-sharing.
You can observe the difference in this code:
def bar(a) a a end
bar((puts "Hello"; 23))
# Hello
#=> 46
In the "replace every occurrence of the parameter with the argument expression" mental model which corresponds to call-by-name, the code would look like this:
(puts "Hello"; 23) (puts "Hello"; 23)
# Hello
# Hello
#=> 46
and Hello
would be printed twice.
However, with call-by-value and call-by-object-sharing, the argument expression gets evaluated before calling the method and the result of that evaluation gets passed in, so the actual code execution looks more like this:
__fresh_variable_with_unspeakable_name__ = (puts "Hello"; 23)
# Hello
__fresh_variable_with_unspeakable_name__ __fresh_variable_with_unspeakable_name__
#=> 46
If you want to know all the nitty-gritty details about how method arguments are evaluated in Ruby, I recommend checking out Section 11.3 Method invocation expressions, in particular Section 11.3.1 General description and Section 11.3.2 Method arguments of the ISO/IEC 30170:2012 Information technology — Programming languages — Ruby specification.
There is also some (unfortunately incomplete) information to be found in The Ruby Spec Suite aka ruby/spec
, in particular in language/def_spec.rb
and language/method_spec.rb
.
- Why is the parameter other called other, and what is its value and transmission process?
The parameter other
is called other
because that is what the author of that piece of code chose to call it. They could have called it a
or b
or x
or y
or foo
or bar
or i_dont_have_the_slightest_idea_what_to_call_this_parameter_so_i_am_just_choosing_something_totally_random
. If you want to know why the author of that piece of code chose to call it other
, you would have to ask the author of that piece of code.
other
is a somewhat popular name for the "other" operand of a binary operator definition, not just in Ruby, as you can see for example in the Python documentation as well. It does make sense if you read it out loud: in an Object-Oriented Programming Language like Ruby or Python, where operators are interpreted as being sent to one of the operands, one of the two operands of a binary operator will always be self
or this
and the "other" operand will be … well … the "other" operand. So, naming it other
is just natural. In Ruby, this is codified in some Style Guides, for example the Rubocop Ruby Style Guide.