My issues are two-fold:
I didn't realize I could nest multiple conditionals like this. It seems pretty nasty. I think I know what's going on here, but could someone who really gets it explain so I can really get the concept?
Since I don't understand nested conditionals too well, I'm a bit lost on the refactor, an area I am pretty weak in to begin with. Would y'all be so kind as to present some possible refactor solutions with explanations? It would help my limited understanding greatly.
def valid_triangle?(a, b, c, sum)
if a != 0 || b != 0 || c != 0
if a >= b
largest = a
sum += b
else largest = b
sum += a
end
if c > largest
sum += largest
largest = c
else sum += c
end
if sum > largest
return "true"
else return "false"
end
else return false
end
end
You can trim this down considerably by doing it a more Ruby-like way:
def valid_triangle?(*sides)
case (sides.length)
when 3
sides.sort!
sides[0] + sides[1] >= sides[2]
else
false
end
end
Whenever possible, try and express your logic as a series of transformations on data, it's usually a lot easier to follow. In this case treat the incoming points as an array and sort them rather than having special-case logic for each point. That special case code is always trouble, hard to test, and prone to subtle failure if you make even a tiny mistake.
It's worth noting that in Ruby you should have your if
statements formatted like this:
if condition
# ...
elsif condition
# ...
else
# ...
end
Including code immediately following else
may be valid syntax, but it's very confusing to look at. It seems you're testing against largest == b
and have made a mistake, doing an assignment inadvertently, when really that's the content of the else
block, not a condition for an elusive
.
This code can be easily updated to include tests for negative lengths, like adding sides[0] > 0
as part of your logic.
Also, embrace the Ruby implicit return
where the last value provided is the one returned from the method. The case
statement actually propagates values from the one that triggered, making it very convenient for passing things through.
Really the concept of nested conditionals is not much different from the concept of any other type of conditional. A conditional controls whether or not all the code inside it is executed. So if you nest one conditional inside the other, the outer conditional will control whether the inner conditional is executed.
some_var = 5
if some_var > 1
# Code here will be executed
if some_var > 10
# Code here will not be executed
end
end
if some_var > 10
# Code here will not be executed
# The following conditional won't be checked at all,
# because execution won't reach this point.
if some_var > 1
# This won't be executed.
end
end
# The execution point will immediately jump to here once
# the some_var > 10 condition is determined to be false.
As for refactoring your code, there are a number of things here which could be improved. For one, nested conditionals often can and should be avoided. Not because they're difficult to understand, but because there are often more readable alternatives.
For example, your code encloses the entire method in one large conditional which basically says "don't execute the rest of this method unless this condition is true":
if a != 0 || b != 0 || c != 0
# ...
else
return false # Note: Statements are conventionally
# placed on the next line after else.
# Not on the same line.
end
This can be avoided by including one condition at the start of the method which immediately returns if the condition is false:
return false unless a != 0 || b != 0 || c != 0
The other thing I noticed is that you're returning strings in some cases instead of booleans:
if sum > largest
return "true"
else
return "false"
end
This isn't a good idea. Return an actual boolean instead:
if sum > largest
return true
else
return false
end
In fact though, this entire condition isn't all that useful. sum > largest
is already evaluating to true in one case and false in the other, so you can just:
return sum > largest
Also, why are you passing sum
in as an argument? Shouldn't that always start off as zero? You can remove that statement from the method signature and just initialize sum as 0 in the method body:
sum = 0
Now let's see what your code looks like:
def valid_triangle?(a, b, c)
return false unless a != 0 || b != 0 || c != 0
sum = 0
if a >= b
largest = a
sum += b
else
largest = b
sum += a
end
if c > largest
sum += largest
largest = c
else
sum += c
end
return sum > largest
end
That's looking much better. The code in the middle there is still pretty complicated though. Let's think about that for a moment... it seems you want to set largest
to the largest value of the three sides, then set sum to the sum of the remaining sides. This is pretty easy to do with an array sort:
sides = [a, b, c].sort
Now, sides
contains a sorted array of all sides. The last element will be the largest...
largest = sides.last
...and the other two sides can be used to find the sum:
sum = sides[0] + sides[1]
Now let's see where we're at:
def valid_triangle?(a, b, c)
return false unless a != 0 || b != 0 || c != 0
sides = [a, b, c].sort
largest = sides.last
sum = sides[0] + sides[2]
return sum > largest
end
Nice!
With a better understanding of some of Ruby's built-in methods, this can be made even simpler. I won't go into all the details, but here's how I'd probably write this code:
def valid_triangle?(a, b, c)
sides = [a, b, c].sort
sides.all?{|s| s != 0} && sides.pop <= sides.reduce(:+)
end
How does this work? Let me break it down.
First, just as before, we collect all the sides into a sorted list so that the smallest side is the first element, and the largest side is the last element. This will make our later calculations much easier.
sides = [a, b, c].sort
Next, we check that all sides are non-zero.
sides.all?{|s| s != 0}
Then, we check that the largest side is at least as small as the sum of the other sides. I do this by removing the largest side from the sorted list, then checking that this value is smaller than the sum (found using sides.reduce(:+)
) of the remaining two sides in the list:
sides.pop <= sides.reduce(:+)
The result of these two conditions is combined with &&
and returned from the method (Ruby automatically returns the last executed statement in a method, so we can omit the return keyword here).
Here is a list links to the documentation for the built-in methods I used in this solution. Please examine the documentation for any methods you don't understand to see how they work:
Without touching the core algorithm, here are some things you could do to improve the method structure.
def valid_triangle?(a, b, c, sum)
# terminate early if inputs are not in expected state
return false if [a,b,c].include? 0
if a >= b
largest = a
sum += b
else
largest = b
sum += a
end
# space to separate this from the previous if/else block
if c > largest
sum += largest
largest = c
else
sum += c
end
# be consistent on return type (string or boolean)
sum > largest
# ternary if can be used if return type is not a boolean
# strings are not recommended for true/false values though
# sum > largest ? "true" : "false"
end
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.