In Ruby Why is the If/Then logic written without the If/Then structure failing? - ruby

Why does the following code fail to produce expected output, for the first two test cases if I don't add the "then" section to the the 'If?' I set the default value of the second variable "False" and I was under the impression that in Ruby a method could take an unspecified number of parameters, and the lack of a parameter when the method is called will roll back to using the default values of that parameters within the method if set.
def alphabetize(arr,rev=false)
arr.sort!
if rev == true
arr.reverse!
end
end
numbers = [1,9,2,1,10]
Test cases:
print alphabetize(numbers,false)
=begin
input: numbers,false
output: nil
expected output: 1,1,2,9,10
=end
print alphabetize(numbers)
=begin
input: numbers
output: nil
expected output 1,1,2,9,10
=end
print alphabetize(numbers,true)
=begin
input: numbers,true
output: 10,9,2,1,1
expected output: 10,9,2,1,1
=end
This code produced the expected results:
def alphabetize(arr,rev=false)
if rev == true
arr.sort!.reverse!
else
arr.sort!
end
end
numbers = [1,9,2,1,10]

You are printing the return value of the alphabetize method, which is not necessarily the value of the array.
In your first code, you have an if without a corresponding else as the last statement. In Ruby, the return value of an if statement without an else is nil, when the if condition fails.
In your second code, you have an if with an else statement. So the return value of the method, when the ifcondition fails, will be what gets executed inside the else block. In this case, arr.sort!.
It's worth mentioning the alphabetize method modifies the numbers array being passed in (indicated by the ! in the sort! and reverse! methods). If you printed numbers, instead of return value of alphabhetize, you would have the expected output as well.

def alphabetize(arr,rev=false)
arr.sort!
if rev == true
arr.reverse!
end
arr
end
Return array at the end when if false it return nil. To make sure the it returns the array value at the end of method.
Tested on rubyfiddle.com
http://rubyfiddle.com/riddles/c36bc/1

Related

How to do a simple test in ruby

I have some code below that gives square numbers. I also wrote a simple test that prints true or false.
numbers = (1..20)
test=numbers.each do |number|
puts number * number
end
puts test == [1,4,9,16,25,36,49,64,81,100,121,144,169,196,225,256,289,324,361,400]
I expect it to print true, but it puts false.
I was wondering if anyone could help to see why.
Try below code to get your answer :
numbers = (1..20)
test = numbers.map{|a| a*a}
puts test == [1,4,9,16,25,36,49,64,81,100,121,144,169,196,225,256,289,324,361,400]
You are using first test which contents 1..20. So you have to modify your code.
In your code numbers.each do |number| .. end returns the range (1..20) and that's the value that test will be assigned to after the loop through numbers is done. So even if you deleted the line puts number* number, test will still have the value (1..20). So using map would be the right choice to keep your values in an array. But you should first delete puts, because it shows the value on the screen but returns nil. So if you used map but didn't delete puts test will be assigned to an array of nils.

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.

Ruby Boolean assignment operator in a loop

Given the following code:
File.open('file1.txt', 'r') do |file|
while line = file.gets
puts "** " + line.chomp.reverse + " **"
end
end
I am confused to what is the question being asked? This is a simple piece of code I got off my tutorial, that reads a file's lines and puts it out. I do understand most of it, I believe you are assigning a variable line to the return value of file.gets, and it retrieves the value of those lines, and puts it out.
Where I am having trouble is the initial loop statement: while line = file gets
My question is that what kind of question are you asking and how does it break out of the loop?
i.e.:x=3 x ==3--> You are asking is X equal to 3, if true will return true, if false will return false.
Also, are you simultaneously assigning the return value of file.gets to the variable line, in addition to putting it in the while statement?
In Ruby everything evaluates to truthy or falsey.
There are two falsey things:
nil
false
Everything else is truthy.
The while loop checks for truthiness of line variable.
Until it is anything but either nil or false it loops.
In your example the loop will stop when file.gets returns nil, meaning, there's no next line.
What happens is that while is using the variable line as its condition. line = file.gets is assigned before while checks the condition. Additionally, while knows how to break out of the loop because at EOF file.gets returns nil which is false-y.

Iterating over array without returning the array

I'm working on a module to format print output for the console. The problem I'm running into is when I call .each on an array that I have, it returns the array in the console. I need to control what gets returned in the console.
How can I iterate through an array without having the array returned to the console?
#values.each do |value| # The end result of this is being returned, do not want.
printf(#format,
value[0], value[1], value[2], value[3], value[4], value[5],
value[6]
)
end
Why not create a method for your desired behavior?
def print_each_value(values, format)
values.each do |value|
printf(format, value[0], value[1], value[2], value[3], value[4], value[5], value[6])
end
nil # Here you set what you would like to return to the console.
end
print_each_value(#values, #format)
Edit: Removed annotative variable.
If you're only interested in the output and not the intermediate array which the interactive Ruby will always display you have two options. The first is to pass in the value you want returned:
#values.each_with_object(nil) do |value, x|
# ...
end
Whatever you supply as the argument there will be what is returned.
The second is to return the strings and print those:
puts(
#values.collect do |value|
#format % values
end.join("\n")
)
This has the advantage of dramatically simplifying your call.
As a note, if you have an array and you want to pass it through as arguments then use the splat operator:
printf(#format, *values)
Add ; to the end.
#values.each do |value| # The end result of this is being returned, do not want.
printf(#format,
value[0], value[1], value[2], value[3], value[4], value[5],
value[6])
end;
You can see the explanation in this article.
If you chain multiple statements together in the interactive shell, only the output of the last command that was executed will be displayed to the screen
And basicaly ; in Ruby is used for chaining.
Try #values.cycle(1) do |value|
I wish there'd be a dedicated method for iterating for side-effects only that returns nothing tho.

Returning a value of ruby is strange

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.

Resources