What's the difference between puts(and print) and return?


#1

While I have completed the entire Ruby course here on codecademy, I still run into certain situations where I find myself puzzled by the fact that return and puts sometimes run in the same manner, but sometimes do not. I feel that that the lessons provided here explain it briefly and do not completely cover how they differ in usage and practice.

Here are my ideas on the topic:

Puts(and print)

Puts is a command that simply tells the computer to show some text on the console, while creating a new line below it for the next command.
Print does the same to the console, but does not create a new line for the next command.

Return

Return is often(always???) used within a function, method, or process to show text on the console. It also seems that it has a sort of memory that remembers a value following a calculation.

Again, these are not definitions. This is simply what I have taken away from the lessons that I have taken on codecademy. Are these conclusions correct?

In addition, I wrote some of my own code below that is now complete and working correctly(I'm proud to say), however, at the end I noticed something peculiar. I wrote:

puts "Longest Palindrome: #{longest}"
return longest
end

and that works fine. But if I flip them around to:

return longest
puts "Longest Palindrome: #{longest}"
end

it doesn't show anything on the console for puts. Why is this happening?? Thanks, gurus.

Code:

def palindrome?(string)
i = 0
while i < string.length
if string[i] != string[(string.length - 1) - i]
return false
end

i += 1

end

return true
end

def longest_palindrome(string)
idx = 0
idx2 = (string.length - 1)
palindromes = []
while idx < string.length
while idx2 > idx
if string[idx] == string[idx2]
palindromes.push(string.slice(idx, (idx2 + 1) - idx))
if palindrome?(palindromes[palindromes.length - 1]) != true
palindromes.pop
end
end
idx2 -= 1
end
idx2 = (string.length - 1)
idx += 1
end

puts palindromes

x = 0
longest = ""
while x < palindromes.length
if longest == ""
longest = palindromes[x]
elsif palindromes[x].length > longest.length
longest = palindromes[x]
end

x += 1
end

puts "Longest Palindrome: #{longest}"
return longest
end


#2
puts n    # prints n + "\n"
print n   # prints n

These methods have no relation to return and themselves have no return, so respond nil in the console.

return is actually a path to the caller scope. Anything in the return object immediately appears in that scope.


#3

Roy, would you mind elaborating on return a little bit more?

Also, why did---->

puts "Longest Palindrome: #{longest}"
return longest
end

and that works fine. But if I flip them around to:

return longest
puts "Longest Palindrome: #{longest}"
end

<----- this happen?


#4

Any code that follows a return statement is said to be unreachable, as in it will never be read. Flow has been diverted back to the caller so the function is exited before the code is seen. Therefore, any actions we wish to take place before exiting the function need to be written before the return statement.

A basic method will have a name, an optional parameter or parameter list, a code body that does something, either to or with the parameter, or some construct (such as print), and finally an optional return statement. What do each of these components do?

A name is obvious. It lets us refer to our method by name.

#   name parameter
#    |   |
def foo(bar)                # _
    # do something with bar    |
    return bar * 2         # _| code body
end   # |
#     return statement

The caller is something that makes the call to the method.
The callee is the method

The parameter is the object or objects passed to the function from the caller.
The return value is the object or objects passed back to the caller from the method.

    obj  -->  foo(bar)   -->  return bar * 2
    #             |                      |
    #      obj passed to foo             |
    #                           object returned

The caller is in one scope, and the callee has its own local scope. The parameter permits the value to pass through a scope barrier from its scope to that of the method. The return statment permits us to return through the scope barrier from the method back to the caller.


#5

Super insightful! Thank you so much for clarifying that for me.

And just to be sure, a caller is simply just typing the name of the method with a parameter somewhere further down in the script, correct? (e.g. longest_palindrome("object"))


#6

Methods are generally associated with an object. The object is the caller. For instance:

lst.each do [i]
    puts "#{ lst[i] }"
end

In the above, each is the list method, lst is the object the method is being called on. That's one example.

def foo(bar)
    bar * 2
end

puts foo("oh")     #  ohoh

The last line is the caller, in this case. A return value will exist at this point in the flow, in the same scope as the call object, immediately following puts.

Notice that the above does not have a return keyword? That would be an explicit return. What we demonstrate here is the implicit return which is an accepted approach to writing methods in Ruby.


#7

We use the word expression a lot. Think of any expression as a value.

foo("oh")  -->  'ohoh'  (a string value)
foo(21)    -->   42     (a number value)

When we see a method call expression, we may immediately think, value. If the method has no return, Ruby responds with nil.


#8

If we don't ever need to explicitly use return then why bother writing return at all?

Also, above you listed two ways of calling a method: dot form and a parenthesis form. Are both forms completely interchangeable? e.g.

def foo(bar)
bar * 2
end

foo(24) # prints 48

24.foo # should print 48?


#9

No. foo is more like a function than a method. It is a global object. .each is a method of List. It can only be called on an object of that class (or other iterable class)..


#10

There are going to be times when an implicit return makes sense and will be fairly obvious to the reader. Definitely do more reading on this to understand the implications and edge cases. The general norm is explicit return. Cutting corners or taking shortcuts we haven't mastered is a disaster waiting to happen. Just because we know we can do something, doesn't me we should. It's up to us to learn how to identify the scenarios where implicit return is appropriate.

We need to do some more reading, you and I.


#11

But both kinds are called methods, right? How will I know if one can be called in one manner or another?

Yes, I need to do some more reading for certain. I feel that some of these points may be difficult to find online, however.


#12

Yes. It is properly explained in the Ruby docs.

Don't mind me. I paraphrase a lot, and since much of any given day is spent frittering around from language to language one can blur language concepts or terms. Invariably, it leaves gaps or introduces foreign ideas.

To fully explain this we have to understand Ruby classes. This is not the time to cover the subject. It comes up in a later module so best to wait til then.

Everything in Ruby is an object. Ruby has many built-in methods associated with the many types of objects we run up against. When we define a method in the global namespace, it is actually a member of an object that wraps that context. Which it is of Object, Kernel or main is still unclear. What is certain, though, is that it is a method of an object, which object is implicit so we needn't refer to it; hence, foo('oh') and not obj.foo('oh').

When we have our own defined class of objects, we can create methods for them just like Ruby has already done. It makes it much easier for us if we don't have to reinvent the wheel everytime we need to change our tires. Ruby has that covered.


#13

rubydocs.org

ruby-doc.org


#14

Thanks again, Roy! You are always appreciated. As I have completed all of the ruby modules, I will dive into this further on ruby-docs.org to hammer out some of these more involved concepts. :wink: