How to fix end-of-input with two Boolean statements - ruby

I'm filtering some Array elements in Ruby and want to filter the positive ones, only if they're all Integers.
l = [1,2,'a','b']
l.select do |number|
new_array = []
new_array.push(number) if number.positive? && number.kind_of? Integer
end
but I got a Syntax error asking for expecting end-of-input.
Why doesn't number.positive? && number.kind_of? Integer work?

The select method is a filtering method and it needs a boolean value that describes if the element should or shouldn't be in the resulting output. In that block you should focus on one thing and one thing only: Describing in minimal terms the acceptance criteria for the filter.
What you're doing here is declaring a local variable, conditionally populating it, then throwing it away, and also discarding the resulting output.
What you really want is to strip this down to the basics:
l = [1,2,'a','b']
filtered = l.select do |number|
number.positive? && number.kind_of? Integer
end
Don't forget to capture the result of this operation or it goes straight in the trash.
There's still a bug here because strings don't have a positive? method, so just reverse the order:
filtered = l.select do |number|
number.kind_of?(Integer) && number.positive?
end
That requires adding brackets to the kind_of? call to avoid ambiguity. && is a very aggressive operator and will interpret the call as this if not properly contained:
number.kind_of?(Integer && number.positive?)

Surround it in parentheses:
new_array.push(number) if number.positive? && (number.kind_of? Integer)
Which results in undefined method `positive?' for "a":String because positive? isn't defined for the strings 'a' and 'b'.
Checking the type first works:
new_array.push(number) if (number.kind_of? Integer) && number.positive?

Just out of curiosity, Monkey patching Object class:
module MyPatches
def integer_and_positive?
kind_of?(Integer) && self > 0
end
end
Object.include MyPatches
For using this way, with Array#keep_if:
l.keep_if(&:integer_and_positive?)
#=> [1, 2]
Note: it alters the original array, for avoiding it use Array#select.

Related

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.

Division: Ruby no method error

I am trying to run this:
def ArithGeo(arr)
if arr[2]/arr[1] == arr[3]/arr[2]
return "Geometric"
else
return "Arithmetic"
end
end
print ArithGeo(STDIN.gets)
It comes back with line 2 having an "undefined method" when I run this in terminal.
Why? The array is all numbers when testing with [1,2,3,100]. (And yes I know that this will return "Arithmetic" when it isn't. I haven't gotten to that part yet.)
Also, is to_i necessary? If items in an array are already considered an integer, they're an integer, right? I also tried with to_i on each array item but it returned a "division by zero" error even when none of the items in the array were 0, and I wasn't using position 0.
One (or more) of the elements in your arr is a String.
irb(main):009:0> "string"/"another string"
NoMethodError: undefined method `/' for "string":String
When you call to_i on a String it becomes 0.
irb(main):013:0* "string".to_i
=> 0
When you divide by 0 you get an error because you can't do that.
irb(main):011:0> "string".to_i/"another string".to_i
ZeroDivisionError: divided by 0
You can fix your code by changing this line:
print ArithGeo(STDIN.gets)
to this:
print ArithGeo(STDIN.gets.strip.split(',').map(&:to_i))
Then enter your inputs like this:
1,2,3,100 # don't include the "[]" around the numbers
Since your input is of ruby syntax [1,2,3,100] you need to evaluate it.
def ArithGeo(arr)
puts "#{arr.class} , #{arr}"
if arr[2]/arr[1] == arr[3]/arr[2]
return "Geometric"
else
return "Arithmetic"
end
end
puts ArithGeo(eval STDIN.gets )
The input:
[1, 2, 3, 100]
The result:
Array , [1, 2, 3, 100]
Arithmetic
Also , I would recommend using floats to prevent integer rounding.
if arr[2].to_f/arr[1] == arr[3].to_f/arr[2]
Edit:
A much better (safer + more generic) is to use:
JSON.parse( array_string )
For example:
JSON.parse("[1 , 2]")
=> [1, 2]
JSON.parse("[1 , 2]").class
=> Array
And if you really want to be on the safe side , you'll need to add exception handling for JSON parsing errors.
You're passing a String to your method (IO.gets returns a string) when what you really want is an array of integers. If you just pass in this string, you will find that division is not defined for Strings. If you attempt to convert the input to an integer first, any leading non-numeric characters will cause the string to be converted to 0.
Try
arr = STDIN.gets.split(', ').map(&:to_i)
ArithGeo(arr)
It depends on your arr elements. Say, if arr elements are strings, then you will get a undefined method '/' for String (NoMethodError).
You need to make sure your arr elements are numbers i.e. integer or floats etc. on which the division (/) method is implemented.
Update
You can input the values comma separated and convert that string to an array using String#split method as I mentioned in the comment:
def ArithGeo(str)
arr = str.split(',').map(&:to_i) # split the values by comma and make them integer and put in array arr
# then you can use the following beause now your arr is an array, make sure you have at least 4 elements as you
# used index 3 in the calculation
if arr[2]/arr[1] == arr[3]/arr[2]
return "Geometric"
else
return "Arithmetic"
end
end
print ArithGeo(STDIN.gets)

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.

Ruby type conversion arbitrary by needed class

Example. I have two variables with random classes:
first = 12345 #Fixnum
second = "12345" #String
Can i convert second var to class identical of first variable?
i can do it with if block:
if first.class == Fixnum
second = second.to_i
elsif first.class == String
# do nothing
end
but, can a do it simple, instead if or case constructions?
You can use a case statement.
result = case first
when Fixnum
second.to_i
when Array
[second]
else
second
end
However, if you start to have several values, you may want to consider a better design pattern. For example, you can wrap second in custom object types that properly implement a casting technique.
result = first.class.cast(second)

Ruby block method help

I was trying to see if I could reconstruct the Array class' delete_if iterator as my own method in order to see if I understood methods and blocks correctly. Here is what I coded:
def delete_if(arr)
for x in 0...arr.length
if (yield arr[x])
arr[x]=arr[x+1,arr.length]
redo
end
end
end
arr = [0,1,2,3,4,5]
delete_if(arr) {|value| value % 2 == 0}
This resulted in an error saying that the % method could not be identified in the last line. I know that value is going to be an integer so I am not sure why it would say this error. Can someone please explain? Also, in Ruby in general, how can you be sure that someone passes the correct type into a method? What if the method is supposed to take a string but they pass in an integer -- how do you prevent that??
Thanks!
def delete_if arr
for x in 0...arr.length
return if x >= arr.length
if yield arr[x]
arr[x..-1] = arr[(x + 1)..-1]
redo
end
end
end
Things I fixed:
it's necessary to mutate the array, if all you do is assign to the parameter, your changes will be local to the method. And for that matter, you were assigning your calculated array object to an element of the original array, which was the immediate cause of the error message.
since the array may become shorter, we need to bail out at the (new) end
of course you could just use arr.delete_at x but I couldn't correct the slice assignment without keeping the code pattern

Resources