Reputation: 223
I have the following ruby method: a single do
iteration without any break
, next
, or return
. Here, cats
is an array of cat objects; is_cat_red
evaluates to true
if cat has a color property of red.
def get_non_red_cats cats
cats.each do |cat|
!is_cat_red?(cat)
end
end
What does the method return (what does the loop evaluate to)?
Upvotes: 1
Views: 80
Reputation: 369458
There is no loop in your code. Ruby has two kinds of loops: while
and for
/in
. (Actually, the latter is just syntactic sugar for each
.)
In Ruby, an expression evaluates to the value of the last sub-expression evaluated inside the expression. A message send evaluates to the return value of the method that was executed as a result of the message send. The return value of a method is either explicitly the value of the return
expression that ended the method execution or implicitly the value of the last expression evaluated inside the method body. (Note that the last expression evaluated inside the body is also what a module or class definition expression evaluates to. A method definition expression however evaluates to a Symbol
denoting the name of the method.)
So, what does get_non_red_cats
return? Well, there is no return
in it, so it returns the value of the last expression evaluated inside the method body. The last expression evaluated inside the method body is a message send of the message each
to the object referenced by the parameter binding cats
. Ergo, the return value of get_non_red_cats
is the return value of the method that gets executed as a result of sending the each
message to cats
.
And that is all we positively know.
We can make some assumptions, though. In general, each
should return self
. That's what all implementations of each
in the entire core library and standard library do, and it is part of the standard "Iterable" Protocol in Ruby. It would be highly unusual and highly confusing if that were not the case. So, we can assume that whatever implementation of each
ends up being executed, it will return self
, i.e. the receiver of the message send, i.e. the object referenced by the parameter binding cats
.
In other words: the method get_non_red_cats
simply returns whatever was passed in as an argument. It is a pretty boring method. In fact, it is the identity method, which is pretty much the most boring method possible.
However, it could have a side-effect. You didn't ask about side-effects, only the return value, but let's look at it anyway.
Since each
is supposed to simply return its receiver, it is in some sense also an identity method and thus extremely boring. However, each
is generally supposed to evaluate the block it is passed, passing each element of the collection in turn as an argument. But, it ignores the value that the block evaluates to; the block is evaluated purely for its side-effect. Note that each
with a block that has no side-effect makes no sense whatsoever. If the block has no side-effect, then the only thing interesting about the block is its value, but each
ignores the block's value, and simply returns self
.
foo.each do
# something that has no side-effect
end
is fully equivalent to
foo
Another Ruby convention is that message sends that end in a question mark ?
should be used for asking questions (duh!) I.e. a message send that ends in a question mark should return something that is suitable to used as a conditional. It also generally shouldn't have a side-effect. (This is called the Command-Query Separation Principle and is a fundamental design principle of Object-Oriented Software Construction.)
And lastly, the !
unary prefix operator, when applied to something that is intended to be used in a conditional (i.e. a boolean value or something equivalent) is generally not supposed to have side-effect. Ergo, since the message send in the block ends with a question mark, it is not supposed to have a side-effect, and the !
operator is also not supposed to have a side-effect, we can assume that the entire block has no side-effect.
This, in turn, means that each
shouldn't have a side-effect, and thus get_non_red_cats
doesn't have a side-effect. As a result, the only other thing get_non_red_cats
can do, is return a value, and it very likely simply returns the value that was passed in.
Ergo, the entire method is equivalent to
def get_non_red_cats(cats)
cats
end
All of this is assuming that the author followed standard Ruby conventions. If she didn't, then this method could do and return anything whatsoever, it could format your harddrive, launch a nuclear attack, return 42
, or do absolutely nothing at all.
Upvotes: 1
Reputation: 211560
This is some unusual code and it depends entirely on what the cats
method does. You can pass a block to any Ruby method and that method can get executed zero more more times at any point between immediately and the end of the program's execution.
The return value is whatever cats
returns, which is not clear from this snippet.
Imagine this in JavaScript terms as that language is a lot less ambiguous:
function get_non_red_cats(cats) {
return cats(function(cat) {
return !is_cat_red?(cat);
}
}
Where this shows that cats
is just a function that, potentially, takes a function. It might ignore your function, too.
Now if this is cats.each
that changes things as that's probably the Enumerable each
method which has well-defined behaviour.
In that case the return value is whatever cats
is.
Upvotes: 1