Returning a value of ruby is strange - ruby

could anyone tell me the return value of this function give the parameter listed blew:
def sequence(*enumerables)
enumerables.each do |enumerable|
print "#{enumerable},"
end
end
a,b,c = [1,2,3],4..6,'a'..'e'
value = sequence(a,b,c)
print value
why the value is evaluated to be:
[[1,2,3],4..6,"a".."e"]

Remember that the last thing left on the stack is the return value of your method. This is always the case. If the return value is important, you must pay close attention to how you exit from your method.
The each method returns what it has been iterating over. Since the each is the last statement in your method, stack-wise, that value gets returned.
You can fix this by returning nothing:
def sequence(*enumerables)
enumerables.each do |enumerable|
print "#{enumerable},"
end
return
end
This approach is generally frowned on as the return method seems out of place. If the caller of this method is not expecting any particular return value, then it's not necessary.
The alternative is to return something useful:
def sequence(*enumerables)
enumerables.join(",")
end
puts sequence(a,b,c)
It's often the case that methods which do not set an expectation for a particular return value may return an arbitrary one.

enumerables is an array. The splat (*) operator causes this.
The return value of sequence is the return value of enumerables.each which is enumerables
A simple example:
def foo(*args)
args
end
foo(1,2,3,4) == [1,2,3,4] # true

You're returning enumerables, which is an array containing all the arguments to the method.
What did you expect value to contain? You haven't made any explicit attempt to return anything, so whatever value the last statement in the method resolves to "falls off" the method to become its return value.

Related

Comparing the length of two strings with Ruby

Define a method called first_longer_than_second with a parameter called first and another called second. The method will return true if the first word passed in is greater than or equal to the length of the second word. It returns false otherwise. Here's how the method would be called and the expected return:
This is what I have:
def first_longer_than_second(first, second)
if first.length >= second.length
puts true
else
puts false
end
end
I am getting errors and I not sure why.
Ruby comparison operators like >= return boolean values naturally. You don't need to use a conditional, and you almost never want to return string equivalents of true and false. Also, Ruby convention is to use a question mark in the name of methods that return boolean values.
For this kind of method, Ruby lets us write this:
def first_longer_than_second?(first, second)
first.length >= second.length
end
Then you can call the method like this:
>> first_longer_than_second?('hello', 'sir')
=> true
Note that the method name is somewhat confusing since it returns true if first is the same length as second. You might consider renaming the method as appropriate. Names do matter!

Why isn't my print_linked_list_in_reverse function working?

One challenge in a Ruby course I'm doing is to print the :data values of the following linked list, in reverse:
{:data=>3, :next=>{:data=>2, :next=>{:data=>1, :next=>nil}}}
So when my method is passed the above code, it should return
1
2
3
Here's my attempt, which doesn't work for the above code. I can't figure out why, and I'd appreciate it if someone could explain what I'm doing wrong:
def print_list_in_reverse(hash)
if hash[:next].nil? #i.e. is this the final list element?
print "#{hash[:data]}\n"
return true
else
#as I understand it, the next line should run the method on `hash[:next]` as well as checking if it returns true.
print "#{hash[:data]}\n" if print_list_in_reverse(hash[:next])
end
end
Here's a solution, in case it helps you spot my mistake.
def print_list_in_reverse(list)
return unless list
print_list_in_reverse list[:next]
puts list[:data]
end
Thank you.
Your solution relies on return values, and you don't explicitly provide one in your else clause. In fact, you implicitly do because Ruby returns the result of the last statement evaluated, which for a print statement is nil. In Ruby false and nil are both logically false, causing the print to get bypassed for all but the last two calls. Your choices are to add a true at the end of the else, or make a solution that doesn't rely on return values.
To negate the need for return values, just check what logic is kosher based on info in the current invocation. You can simplify your life by leveraging the "truthiness" non-nil objects. Your basic recursive logic to get things in reverse is "print the stuff from the rest of my list, then print my stuff." A straightforward implementation based on truthiness would be:
def print_list_in_reverse(hash)
print_list_in_reverse(hash[:next]) if hash[:next]
print "#{hash[:data]}\n"
end
The problem with that is that you might have been handed an empty list, in which case you don't want to print anything. That's easy to check:
def print_list_in_reverse(hash)
print_list_in_reverse(hash[:next]) if hash[:next]
print "#{hash[:data]}\n" if hash
end
That will work as long as you get handed a hash, even if it's empty. If you're paranoid about being handed a nil:
def print_list_in_reverse(hash)
print_list_in_reverse(hash[:next]) if hash && hash[:next]
print "#{hash[:data]}\n" if hash
end
The other alternative is to start by checking if the current list element is nil and returning immediately in that case. Otherwise, follow the basic recursive logic outlined above. That results in the solution you provided.
Better to iterate over every value in your hash, and push the values until there's no any other hash as value inside the main hash.
def print_list_in_reverse(hash, results = [])
hash.each_value do |value|
if value.is_a? Hash
print_list_in_reverse(value, results)
else
results << value unless value.nil?
end
end
results.reverse
end
p print_list_in_reverse(data)
=> [1, 2, 3]
The problem in your code is in the else-case. You need to return true to print the hash[:data].
Your method always print the last 2 elements.

When method chaining, how can you determine which return value will be returned?

I am wondering if a class or any variable is chained to one or more methods? Which return value would it give? (Class.method1.method2 --> what is the return value?)
Given the following code below:
restaurants = []
restaurants << Restaurant.new.import_line(line.chomp)
def import_line(line)
return self
end
In this case, both methods (new and import_line(line) return the instance, so I know the instance gets put into the array. I am curious though if import_line(line), returned something like true
def import_line(line)
return true
end
What would be the return value on Restaurant.new.import_line(line.chomp). Would it be set to true or return the instance (.new returns instance)?
In essence, when Class.method1.method2 or Variable.method1.method2 exists, which method has priority in determining the return value?
Any help would be greatly appreciated!
The return value is always the last thing in the chain, no exceptions. The result is not necessarily the thing you'd expect, though, as the last thing in the chain might do some tricky stuff.
For example, just to be clear:
Restaurant.new.import_line do |line|
line + '!'
end
There's no obligation here for import_line to return the result of that block, and it's often the case that it won't.
You can also have occasions where the thing you're chaining switches completely and catches you off guard so you have to be sure about the return values from each function in the chain you're calling.
Example here:
"help!".gsub!(/!/, '?').length
# => 5
"help?".gsub!(/!/, '?').length
#! NoMethodError: undefined method `length' for nil:NilClass
In this case gsub! returns the string if and only if a change was made, otherwise nil, which means you can't chain it in that case.

Simple way to understand returning from a block in ruby

My code is supposed to print integers in an array.
odds_n_ends = [:weezard, 42, "Trady Blix", 3, true, 19, 12.345]
ints = odds_n_ends.select { |x| if x.is_a?(Integer) then return x end }
puts ints
It gives me an error in the 2nd line - in 'block in <main>': unexpected return (LocalJumpError)
When I remove the return, the code works exactly as desired.
To find the mistake in my understanding of blocks, I read related posts post1 and post2. But, I am not able to figure out how exactly are methods and blocks being called and why my approach is incorrect.
Is there some call stack diagram explanation for this ? Any simple explanation ?
I am confused because I have only programmed in Java before.
You generally don't need to worry exactly what blocks are to use them.
In this situation, return will return from the outside scope, e.g. if these lines were in a method, then from that method. It's the same as if you put a return statement inside a loop in Java.
Additional tips:
select is used to create a copied array where only the elements satisfying the condition inside the block are selected:
only_ints = odds_n_ends.select { |x| x.is_a?(Integer) }
You're using it as a loop to "pass back" variables that are integers, in which case you'd do:
only_ints = []
odds_n_ends.each { |x| if x.is_a?(Integer) then only_ints << x end }
If you try to wrap your code in a method then it won't give you an error:
def some_method
odds_n_ends = [:weezard, 42, "Trady Blix", 3, true, 19, 12.345]
ints = odds_n_ends.select { |x| if x.is_a?(Integer) then return true end }
puts ints
end
puts some_method
This code output is true. But wait, where's puts ints??? Ruby didn't reach that. When you put return inside a Proc, then you're returning in the scope of the entire method. In your example, you didn't have any method in which you put your code, so after it encountered 'return', it didn't know where to 'jump to', where to continue to.
Array#select basically works this way: For each element of the array (represented with |x| in your code), it evaluates the block you've just put in and if the block evaluates to true, then that element will be included in the new array. Try removing 'return' from the second line and your code will work:
ints = odds_n_ends.select { |x| if x.is_a?(Integer) then true end }
However, this isn't the most Ruby-ish way, you don't have to tell Ruby to explicitly return true. Blocks (the code between the {} ) are just like methods, with the last expression being the return value of the method. So this will work just as well:
ints = odds_n_ends.select { |x| if x.is_a?(Integer) } # imagine the code between {} is
#a method, just without name like 'def is_a_integer?' with the value of the last expression
#being returned.
Btw, there's a more elegant way to solve your problem:
odds_n_ends = [:weezard, 42, "Trady Blix", 3, true, 19, 12.345]
ints = odds_n_ends.grep(Integer)
puts ints
See this link. It basically states:
Returns an array of every element in enum for which Pattern ===
element.
To understand Pattern === element, simply imagine that Pattern is a set (let's say a set of Integers). Element might or might not be an element of that set (an integer). How to find out? Use ===. If you type in Ruby:
puts Integer === 34
it will evalute to true. If you put:
puts Integer === 'hey'
it will evalute to false.
Hope this helped!
In ruby a method always returns it's last statement, so in generall you do not need to return unless you want to return prematurely.
In your case you do not need to return anything, as select will create a new array with just the elements that return true for the given block. As ruby automatically returns it's last statement using
{ |x| x.is_a?(Integer) }
would be sufficient. (Additionally you would want to return true and not x if you think about "return what select expects", but as ruby treats not nil as true it also works...)
Another thing that is important is to understand a key difference of procs (& blocks) and lambdas which is causing your problem:
Using return in a Proc will return the method the proc is used in.
Using return in a Lambdas will return it's value like a method.
Think of procs as code pieces you inject in a method and of lambdas as anonymous methods.
Good and easy to comprehend read: Understanding Ruby Blocks, Procs and Lambdas
When passing blocks to methods you should simply put the value you want to be returned as the last statement, which can also be in an if-else clause and ruby will use the last actually reached statement.

Simple Detect Solution Causing Difficulties

I have the following code, which is supposed to provide a simple true-false wrapper over Array#detect, which is nil-element.
class Array
def any &expr
if (self.detect expr)
return true
else
return false
end
end
end
For some weird reason, no matter what is passed to &expr, it ALWAYS returns true! Why is this?
The documentation for Enumerable#detect says that it can optionally take one argument. If it doesn't find the element that matched your block, it returns this argument. In your case, you're passing a Proc object, expr to detect, and not passing a block. This causes detect to return an enumerator, which won't be interpreted as a "falsy" value.
I think instead you want self.detect &expr to pass an actual block instead of a Proc.

Resources