Ruby -If-Else Statement (Triangle Test) - ruby

The question is Create a function that takes three numbers as input and returns true or false depending on whether those three numbers can form a triangle. Three numbers can form a triangle if the sum of any two sides is greater than the third side.
my answer is:
def is_triangle(a,b,c)
if a+b > c
return true
elsif a+c>b
return true
elsif b+c>a
return true
else
return false
end
end
the thing is: my supposed false return keeps returning true. please help!

This logic should work for finding your triangle
def is_triangle?(a,b,c)
sorted = [a,b,c].sort
greatest_side = sorted.pop
greatest_side < sorted.sum
end

Yet another approach:
def is_triangle?(a,b,c)
[a,b,c].max < [a,b,c].sum/2.0
end
Or for Ruby outside of Rails:
def is_triangle?(a,b,c)
[a,b,c].max < [a,b,c].inject(:+)/2.0
end

Your problem is that unless all 3 numbers are 0 one of your ifs will always be true. What you want is something more like
def is_triangle(a,b,c)
a + b > c && a + c > b && b + c > a
end
is_triangle(3,6,8) #=> true
is_triangle(3,6,80) #=> false

Nothing you pass into this is going to return false. Your method is wrong.
You can tell if three sides make a triangle by finding the longest side and then adding the remaining two sides. If they are greater than the longest side, then the sides can make a traingle.

I suggest if you are sure your logic is correct change the method to
def is_triangle?(a, b, c)
a+b > c or b+c > a or c+a > b
end
But according to me it is not so the method should be
def is_triangle?(a, b, c)
a+b>c and b+c>a and c+a>b
end
Some points to note about ruby conventions:
Method which returns boolean ends with '?'
A ruby method returns the last evaluated expression, so writing return is redundant here.

puts " enter a, b and c values"
a=gets.to_i
b=gets.to_i
c=gets.to_i
if a+b > c
print true
elsif a+c>b
print true
elsif b+c>a
print true
else
print false
end
you can also use this code too.. this is much easy

This is also a solution to this problem :
if 2*[a,b,c].max < a + b + c
true
else
false
end
There are a variety of different inequalities theoretically possible:
http://en.wikipedia.org/wiki/Triangle_inequality

Related

Identifying Triangle with if/else

Question is a user gives 3 sides and identifies triangles, like equilateral, isosceles and scalene. Here is my coding, I don't know why gives any sides that always show up "invalid". I think it's logic wrong, but I can't figure out.
puts "please input the length of 3 sides:"
a = gets.chomp.to_i
b = gets.chomp.to_i
c = gets.chomp.to_i
if a + b <= c
puts "invalid"
elsif a <= 0 || b <= 0 || c <= 0
puts "invalid"
else
if a == b && b == c
puts"equilateral triangle"
elsif a == b
puts"isosceles triangle"
else
puts"scalene triangle"
end
end
The fact that your code always prints "invalid" makes me think that input is passed in on one line instead of being on separate lines. For example, when the input is:
50 50 50
instead of getting 50 in all three variables you would get 50 in a and 0 in b, c. This is because gets takes in an entire line instead of taking one value.
In such an event, this is what you need:
a, b, c = gets.split.map{ |value| value.to_i }
A better more effective way to do this is to store the values of the triangle sides into a hash first, the value of of each triangle side will be the keys, and the value of each key can be the repeats. This will work with strings too.
Here is an Example.
# First you get an array, you can use gets.chomp as string and split to array, whichever way you choose, but in the end we end up with an array, and we pass the array to the method.
def triangle_type(arr)
# Create new empty hash
repeated_sides = Hash.new(0)
# First make sure the array is only a length of three. (this is optional)
if arr.length == 3
# Iterate through each value in the array and store it to to a hash to find duplicates
arr.each do |x|
repeated_sides[x] += 1
end
# sort the hash by it's values in descending order, for logic to work later.
repeated_sides = repeated_sides.sort_by {|k,v| v}.reverse.to_h
# uncomment this below to see the duplicate sides hash
#puts "#{repeated_sides}"
# Iterate through the sorted hash, apply logic starting from highest and first value the iterator will find.
repeated_sides.each do |k,v|
return v == 3 ? 'Equilateral Triangle' : v == 2 ? 'Isosceles Triangle' : 'Scalene Triangle'
end
end
# Return Not a triangle if the condition fails
return 'Not a triangle'
end
# Test with integers
puts triangle_type([4,1,2,5]) # output: Not a triangle
puts triangle_type([3,3,3]) # output: Equilateral Triangle
puts triangle_type([4,3,3]) # output: Isosceles Triangle
puts triangle_type([4,2,3]) # output: Scalene Triangle
# Test with strings
puts triangle_type(['4','1','2','5']) # output: Not a triangle
puts triangle_type(['3','3','3']) # output: Equilateral Triangle
puts triangle_type(['4','3','3']) # output: Isosceles Triangle
puts triangle_type(['4','2','3']) # output: Scalene Triangle
puts triangle_type(['a','a','a']) # output: Equilateral Triangle
puts triangle_type(['a','c','c']) # output: Isosceles Triangle
puts triangle_type(['a','b','c']) # output: Scalene Triangle
Skipping user inputs, since I can not reproduce the error (even if Unihedron found a fix) there is still a problem with the logic.
When the input is a = 1000, b = 1, c = 1, the result is "scalene triangle", but it should return "invalid". Below a fix I suggest.
Let's store the input in an array (already converted into integer or float):
sides = [a, b, c]
First you need to check that all sides are positive:
sides.all? { |x| x > 0 }
Then, check that the sum of two sides is greater than the other:
sides.combination(2).map{ |x| x.sum }.zip(sides.reverse).all? { |xy, z| xy > z }
Finally (I'm missing something?), to pick the triangle denomination you can use an hash accessing it by sides.uniq result:
triangle_kinds = {1 => 'equilateral', 2 => 'isosceles', 3 => 'scalene'}
triangle_kinds[sides.uniq.size]
Used the following methods over array (enumerable):
https://ruby-doc.org/core-2.5.1/Enumerable.html#method-i-all-3F
https://ruby-doc.org/core-2.5.1/Array.html#method-i-combination
https://ruby-doc.org/core-2.5.1/Array.html#method-i-map
https://ruby-doc.org/core-2.5.1/Array.html#method-i-zip
https://ruby-doc.org/core-2.5.1/Array.html#method-i-reverse
https://ruby-doc.org/core-2.5.1/Array.html#method-i-sum
https://ruby-doc.org/core-2.5.1/Array.html#method-i-uniq

Understanding 'next' in ruby - comparing solutions

I'm doing an online course:
Write a method that takes a string and returns true if the letter "z" appears within three letters after an "a". You may assume that the string contains only lowercase letters.
This is the solution given:
def nearby_az(string)
i = 0
while i < string.length
if string[i] != "a"
i += 1
next
end
j = i + 1
while (j < string.length) && (j <= i + 3)
if string[j] == "z"
return true
end
j += 1
end
i += 1
end
return false
end
This solution seems unnecessarily complex to me. Specifically, I don't understand why next is needed. I was wondering if someone could help me understand this function.
My solution appears to work as well with the tests I tried, but I'm wondering if the other solution is better and if my solution would have problems that I just haven't come across yet.
def nearby_az(string)
i = 0
while i < string.length
while string[i] != "a"
i += 1
end
if string[i + 1] == "z" || string[i + 2] == "z" || string[i + 3] == "z"
return true
else
i += 1
end
return false
end
end
In the original answer, if you remove next, the i index will be assumed to be representing an "a" position even if it is not, and will return a wrong result.
When you pass a string like "b" to your code, it will go into an endless loop because the outer condition while i < string.length has no control over:
while string[i] != "a"
i += 1
end
Notice that, once i is beyond the last position of a string, the condition string[i] becomes nil, and string[i] != "a" will be satisfied from there on.
Your solution will not work if the string will look like this "bbbbb..." or like this "abb" - that is -
1) your code will crash if string.length < 4 (no string[i+3])
2) your code will crash if no "a"s are in the string.
And here the "next" came in handy:
The purpose of the "next" is to skip the rest of the loop and jump right back to the start of it. So the original solution will first go over chars until it finds on "a" (skipping the other part of the loop is not) and when and only when it finds "a" - it goes to find the "z".
So, as sawa stated, your loop doesn't accept strings without an a in them. It also doesn't understand strings with multiple a's. For example aaaaaaaz will return false because it finds the first a, checks the next 3 doesn't find a z and bails out.
You can solve your problem by removing the return false from the end and adding the same length condition as the outer loop to the inner loop.
His use of next is equivalent to the above. It is a way of threading the two things together. It is needlessly complex though, I agree.
A far simpler way (assuming you don't want to use regexps) is to just track the last occurance of an 'a'.
def nearby_az(string)
last_a = -4
string.chars.each_with_index do |c, i|
last_a = i if c == 'a'
return true if c == 'z' and i - last_a <= 3
end
return false
end

Trying to check if two strings are permutations of each other

I am trying to implement this function that checks if two strings are permutations of each other. The code itself is straightforward.
def permutation(a, b)
if a.length != b.length
return False
end
a = a.chars.sort.join
b = b.chars.sort.join
return a == b
end
a = "abcedff"
b = "acbedf"
puts (permutation(a, b).to_s)
However, when I try to run this file on the terminal, I keep getting an error that says
permutation.rb:3:in permutation': uninitialized constant False (NameError)
from permutation.rb:13:in'
I don't understand the reason for this.
Ruby is not Python. You want true and false, not True and False.
Don't over-complicate it. All you need to do is compare two character arrays. For example:
def permutation a, b
a.chars.sort == b.chars.sort
end
Given your corpus as posted, this yields:
a = "abcedff"
b = "acbedf"
permutation a, b
#=> false
permutation a, a
#=> true

Project Euler: Riddle 4 - What is my Ruby code doing?

I'm NOT asking for a solution to this riddle.
So, I'm working through the Project Euler problems using Ruby.
I made a palindrome checker with the following method:
def is_palindrome(n)
if n.to_s == n.to_s.reverse
true
else
false
end
end
Using this, I use the next method to try and find the largest palindrome made with two 3 digit numbers.
x = 999
y = 998
while y > 100
if is_palindrome(x * y) == true
puts (x * y)
else
x-=1
y-=1
end
end
The result is the terminal throws a complete fit and gets stuck on 289982 and has to be Ctrl+C'd for it to stop.
As I said, I'm not looking for a solution to this riddle, but I want to know what it is my code is doing for this to happen. I'm still learning so my code is likely to be rather ugly, so extra karma points to anyone that can help me out with this.
In case a palindrome is found, you don't decrement your variables. Since the variables remain the same, the same palindrome is found again. And so it goes...
Amadan's solved your problem, so I'll go for the extra karma points :)
First thing, you can just do if something instead of if something == true.
Also, you don't need to do
if something == something_else
true
else
false
end
because the == operator returns either true or false already, so your is_palindrome method could be as simple as
def is_palindrome(n)
n.to_s == n.to_s.reverse
end
Also, in Ruby, you will usually see methods like this one (a boolean check) named with a question mark at the end, because it's really like you're asking it a yes/no question! So usually you'd see that called is_palindrome?(n). Similar ones you might see elsewhere are Array#empty? and Hash#has_key?(k).
Not answering your question, but your use of booleans is horrendous. Remember boolean expressions return a boolean itself, you don't need another comparison. i.e.
def is_palindrome(n)
if n.to_s == n.to_s.reverse
true
else
false
end
end
Should be:
def is_palindrome(n)
n.to_s == n.to_s.reverse
end
And
while y > 100
if is_palindrome(x * y) == true
Should be
while y > 100
if is_palindrome(x * y)
Here's my solution:
# Find largest palindrome made from the product of two 3-digit numbers
largest = 0
for i in 100..999
for j in i..999
product = j*i
# Check for palindrome and determine largest
largest = product.to_s == product.to_s.reverse && product > largest ? product : largest
end
end
p largest

How to execute multiple succeeding functions in 1 line in Ruby?

I have two succeeding function with the same condition and I wonder what is the best way to write this? I know the one way to do this is using if (condition) ... end, but I'm wondering if I can do it in one-line similar to bash, '[$n == $value] echo "$n" && break'.
n = 0
loop do
puts n if n == value # puts and break is having the same condition, but
break if n == value # can we do it in one line?
n += 1
end
Because n is truthy, you can use the 'and' joiner. It reads really nicely:
n = 0
loop do
puts n and break if n == value
n += 1
end
--edit--
As pointed out in comments, that won't actually work because puts returns nil, which isn't truthy. My bad. You can use 'or' instead, but that doesn't read nicely. So I'd say just group the statements with parenthesis.
n = 0
loop do
(puts n; break) if n == value
n += 1
end
You could also change the puts method to return the value it prints, and that would work with 'and', but that's probably not the smartest idea :)
I'm guessing your actual code is different to what you've pasted, so if the first method in your chain returns something, you can use 'and'.
One easy way is to just parenthesize the statements:
ruby-1.9.1-p378 > 0.upto(5) do |n|
ruby-1.9.1-p378 > (puts n; break;) if n == 3
ruby-1.9.1-p378 ?> puts ">>#{n}<<"
ruby-1.9.1-p378 ?> end
>>0<<
>>1<<
>>2<<
3
If it's a bit much to put in parentheses, a begin-end will do the trick:
0.upto(5) do |n|
begin
puts "I found a matching n!"
puts n
puts "And if you multiply it by 10, it is #{10*n}"
break;
end if n == 3
puts "((#{n}))"
end
Output:
((0))
((1))
((2))
I found a matching n!
3
And if you multiply it by 10, it is 30
proc { puts n; break; }.() if n == 3
One of the golden rules of Ruby is that if you are writing a loop, you are probably doing it wrong. In this particular case, all that your loop is doing is to find an element in a collection. In Ruby, there already is a method for finding an element in a collection: Enumerable#find. There is no need to write your own.
So, the code gets simplified to:
puts (0...1.0/0).find {|n| n == value }
Now that we have a nice declarative formulation of the problem, it is easy to see that (assuming sane equality semantics and sane semantics of value.to_s), this is exactly the same as:
puts value
So, the whole loop was completely unnecessary to begin with.

Resources