binary alphabet sort ruby - ruby

i tried writing my own alphabet search for Chris Pine tutorial chapter 7, and i waned to implement a binary method. there is no validity for string input, so i don't know what will happen with integers mixed with strings, but the idea was to do it for a list of strings only.
#get list of strings
puts "type words to make a list. type 'exit' to leave program."
x = ()
list = []
while x.to_s.upcase != 'EXIT'
x = gets.chomp
list.push(x)
end
list.pop
#binary method
nano = list.length
half= list.each_slice(nano/2).to_a
left = half[0]
right = half[1]
nanol=left.length
nanor=right.length
#initialize results array
A = []
for i in 0..nano-1
smallest_left = left.min
smallest_right = right.min
#no difference with this commented out or not
#if nanol==0
# A<<smallest_right
#end
#if nanor==0
# A<<smallest_left
#end
#error message points to the line below (rb:44)
if smallest_left<smallest_right
A << smallest_left
print A
left.pop[i]
elsif smallest_left>smallest_right
A << smallest_right
print A
right.pop[i]
else
print A
end
end
for input = ['z','b','r','a'] i can see the list being sorted in the error:
["a"]["a", "b"]["a", "b", "r"] rb:44:in `<': comparison of String with nil failed (ArgumentError)
please help me see my error :) Thanks in advance!

The exception is occurring because you are trying to compare nil. You get a different exception when nil is on the left.
'1' < nil
#=> scratch.rb:1:in `<': comparison of String with nil failed (ArgumentError)
nil > '1'
scratch.rb:1:in `<main>': undefined method `>' for nil:NilClass (NoMethodError)
Your code gets into this situation when the left or right array is empty (ie all of its elements have been added to A already). Presumably, this is why you had originally added the if-statements for nanol == 0 and nanor == 0 (ie to handle when one of the arrays is empty).
Your if-statements have a couple of issues:
You do need the nanol == 0 and nanor == 0 statements
The three if-statements are always run, even though only one would apply in an iteration
nanol and nanor are never re-calculated (ie they will never get to zero)
When the left and right values are equal, you don't actually add anything to the A array
The inside of your iteration should be:
smallest_left = left.min
smallest_right = right.min
nanol=left.length
nanor=right.length
if nanol == 0 #Handles left no longer having values
A << right.delete_at(right.index(smallest_right) || right.length)
elsif nanor == 0 #Handles right no longer having values
A << left.delete_at(left.index(smallest_left) || left.length)
elsif smallest_left < smallest_right
A << left.delete_at(left.index(smallest_left) || left.length)
elsif smallest_left > smallest_right
A << right.delete_at(right.index(smallest_right) || right.length)
else #They are equal so take one
A << left.delete_at(left.index(smallest_left) || left.length)
end
You will still have an issue (no error, but unexpected results) when your list has an odd number of elements. But hopefully that answers your question.

Related

longest_palindrome, expected 0, got nil, where should if-else statement?

Taking on the CodeWar Challenge. Struggling to fix my code so that if the length of the string input is 0, it would show 0 instead of nil.
Code is below: (suggestions?)
def longest_palindrome(string)
i = 0
a = []
while !string[i..-1].empty?
j = -1
while !string[i..j].empty?
s = string[i..j]
if s.reverse == s
a << s.length
if s.length == nil
a.max = 0
end
end
j -= 1
end
i += 1
end
a.max
end
First, I'd like to point out a couple of issues with the code you posted.
The body of the innermost if statement is never executed because
0 != nil
This means that even if s.length evaluates to zero
s.length == nil
will still be false.
Another issue I'd like to point out is that
a.max = 0
will throw an error that looks like this:
undefined method max=' for []:Array (repl):17:inlongest_palindrome'
(repl):1:in `initialize'
You can't set the max value directly. The reason you never run into this error with the code you posted is because of the first issue I outlined above.
Now to answer your question. There are a lot of ways to do what you are asking. For example, you could just check whether the input string is empty
at the beginning of the code and immediately return 0 if it is therefore
never executing the while loops at all. Maybe with something like
return 0 if string.empty?
at the beginning of the code.
But from your question, I think what you are looking is something more like the following:
def longest_palindrome(string)
i = 0
a = []
while !string[i..-1].empty?
j = -1
while !string[i..j].empty?
s = string[i..j]
if s.reverse == s
a << s.length
end
j -= 1
end
i += 1
end
a.max.to_i
end
Of interest here is the second last line which makes sure a.max is converted
to an integer using the to_i method. Calling this method on nil converts it to 0.
Also, please note I have changed the code to fix the issues I had highlighted earlier.

Towers of Hanoi, Ruby conditional

I'm having trouble with my first if conditional, which checks to make sure the the new piece added is smaller than the one under/before it. My Towers of Hanoi game worked fine until I added it. Below is my code:
arrays = [[5,4,3,2,1],[],[]]
win = false
while win != true
puts "Choose a top piece: (1, 2, 3) "
top = gets.to_i
puts "Which stack to place this piece? (1, 2, 3)"
stack = gets.to_i
if (arrays[stack-1] == nil) ||
(arrays[stack-1][arrays[stack-1].count-1] > arrays[top-1][arrays[top-1][arrays[top-1].count]])
arrays[stack-1].push(arrays[top-1].pop)
else
"You need to follow the rules."
end
print arrays
if arrays[1] == [5,4,3,2,1] || arrays[2] == [5,4,3,2,1]
print "You're a winner!"
win = true
end
end
~
Below is the error I get. How do I perform my check and deal with my nil value arrays in a concise manner?
towers_hanoi:13:in `[]': no implicit conversion from nil to integer (TypeError)
from towers_hanoi:13:in `<main>'
Use the empty? method to determine if an array is empty. FYI, though, if you want to see if a variable has a nil value, use nil?
Also, the last method will help a ton here and subtracting 1 from the inputs right away will make the code more readable. Try this:
arrays = [[5,4,3,2,1],[],[]]
win = false
while win != true
puts "Choose a top piece: (1, 2, 3) "
stack_from = gets.to_i - 1
puts "Which stack to place this piece? (1, 2, 3)"
stack_to = gets.to_i - 1
if (arrays[stack_to].empty?) ||
(arrays[stack_to].last > arrays[stack_from].last)
arrays[stack_to].push(arrays[stack_from].pop)
else
"You need to follow the rules."
end
print arrays
if arrays[1] == [5,4,3,2,1] || arrays[2] == [5,4,3,2,1]
print "You're a winner!"
win = true
end
end
There are a lot of strange things going on in that if statement.
Definitely use Array#empty? to check if an array if empty. An empty array is not nil.
Secondly some of your array bracketing is way too convoluted, I'm not sure what you are trying to accomplish here but you are definitely going to be checking if nil > number in some cases:
(arrays[stack-1][arrays[stack-1].count-1] > arrays[top-1][arrays[top-1][arrays[top-1].count]])
I doubt this is what you are trying to do (since it will throw an error). I would take a minute to think about your logic and refactor. In Towers of Hanoi, you only need to worry about checking if the piece you are moving is less than the LAST piece on the stack you are moving to (which represents the top).
Use Array#last and you will be on your way to a much simpler solution.

variable addition within index returns nil in ruby

I've come across this error twice now in exercises when I'm trying to iterate over indexes in a string with a conditional. When I break it all out with individual cases the logic seems to work, but Ruby doesn't like something about the way it's expressed.
def NumberAddition(str)
def is_num(num)
(0..9).include? num.to_i
end
i = 0
sum = 0
while i < str.length
if is_num(str[i])
if !is_num(str[i+1])
sum += str[i].to_i
else
mult = str[i]
n = 1
while is_num(str[i+n])
mult << str[i+n]
n += 1
end
sum += mult.to_i
end
end
i += 1
end
sum
end
NumberAddition("75Number9")
throws this error:
no implicit conversion of nil into String
(repl):18:in `NumberAddition'
for the line:
mult << str[i+n]
so obviously instead of returning, say, the string "5" for str[i+n],
where i=0 and n=1, it finds nil. Is there a way to express this with my methodology or do I need to retool the whole loop?
Your is_num function doesn't take into account that nil.to_i is 0. That's why you're getting error, because you're trying to append nil to a string. You need to use something like this:
def is_num(num)
# convert string to integer and back to string should be equal to string itself
num.to_i.to_s == num
end
Or, if you want to make sure that you concatenating strings, just convert the argument to a string
mult << str[i+n].to_s # nil gets converted to an empty string

Why does changing the order of conditionals break my code?

I have created a ping-pong test in Ruby. It monkey patches the Fixnum class with a new method, ping_pong, that loops over a range (0..self), checks some conditions on each element, and constructs an array of the results.
The resulting array will have Ping for numbers in the range divisible by 3, Pong for numbers divisible by 5, and Ping-Pong for numbers divisible by both.
My question now is, why does the code only work if the part:
elsif (num.%(3) == 0) && (num.%(5) == 0) array.push("Ping-Pong")
is ahead of the other elsif statements? I tried putting it after the other elsifs but it didn't work.
Here's my code:
class Fixnum
define_method(:ping_pong) do
array = [0]
total = (0..self)
total = total.to_a
total.each() do |num|
if (num == 0)
array.push(num)
elsif (num.%(3) == 0) && (num.%(5) == 0)
array.push("Ping-Pong")
elsif (num.%(3) == 0)
array.push("Ping")
elsif (num.%(5) == 0)
array.push("Pong")
else
array.push(num)
end
end
array
end
end
When you have multiple if/elsif blocks chained together, only one of them will run, and the first block to have a true condition will be the one to be run. So, the order of the blocks matters. For example:
if true
puts 'this code will run'
elsif true
puts 'this code will not run'
end
Even though the conditions for those blocks are both true, only the first one is run. If you want to have both run, use two separate if blocks, like this:
if true
puts 'this code will run'
end
if true
puts 'this code will also run'
end

unexpected return (local jumperror)

def getPrime(testNumber)
temp1=testNumber -1;
bIsPrime = false;
while (temp1 > 1) do
bIsPrime = ((testNumber % temp1) == 0)
puts("Check 1 #{((testNumber % temp1) == 0)}, temp1=#{temp1} ");
if ($bIsPrime)
break;
else
temp1 = temp1-1;
end
end
return bIsPrime
end
puts("Enter any number to know if it's Prime or not");
testNumber = gets()
returnVal = getPrime(Integer(testNumber));
puts("Is enternered number Pime? #{return}");
I have just started with Ruby...So to begin with i tried to write a prime number program.
This thing is giving error "unexpected return"
Any help would be great. Thanks.
return is reserved. You cannot use it as a variable name, or otherwise, other than to return.
I believe you meant puts("Is entered number prime? #{!returnVal}");
Don't check this answer, Adam was first. But here's more info
As Adam says in his answer, the problem is that you said
puts("Is enternered number Pime? #{return}");
What was happening
Ruby evaluates whatever is inside the #{ foo } construct--if it is in an interpolated string, such as
puts "1 + 1 = #{1+1}" ==>> Will print 1 + 1 = 2
The resulting value is then converted (coerced) to be a string.
In your case, you told ruby to evaluate the return statement, which didn't make any sense in the context. Hence the slightly weird error message.
although you already have your answer I thought it would be helpful to drop this here, it follows the same idea you're using in your code:
def is_prime?(test_number)
(2..test_number-1).reverse_each do |i|
divisible = (test_number % i == 0)
puts "Check ##{test_number-i}, divisible=#{divisible}, temp=#{i}"
return false if divisible
end
return true
end
puts "Enter any number to know if it's prime or not"
test_number = gets.chomp.to_i
puts "Is entered number prime? #{is_prime?(test_number)}"

Resources