直角三角形属性计算:最少条件语句与优雅实现方案问询
Great question! Those repetitive if/else chains are definitely a pain to maintain—let's break down a cleaner, more scalable approach to solve this problem. The core issue with your original code is that it's handling order-dependent property pairs (like adjacent+opposite and opposite+adjacent as separate cases), which doubles the number of checks needed. We can fix this by normalizing input first, then focusing on unique property combinations.
Step-by-Step Optimized Solution
1. Normalize Input to Eliminate Order Dependency
First, we'll take the input properties and map them to instance variables regardless of their order. This way, we only need to handle each unique pair of properties once, not twice.
2. Separate Calculation Logic into Modular Blocks
We'll move the math logic into a dedicated method, with each block handling one unique combination of known properties. This makes the code easier to debug and extend if needed.
Full Optimized Code
import math class RightAngleTriangle: def __init__(self, prop1_type, prop1_val, prop2_type, prop2_val): # Initialize all properties as None (unknown) self.adjacent = None self.opposite = None self.hypotenuse = None self.area = None # Map input properties to instance variables (ignores order) known_properties = {prop1_type: prop1_val, prop2_type: prop2_val} for prop_name, value in known_properties.items(): setattr(self, prop_name, value) # Calculate missing properties self._calculate_missing() def _calculate_missing(self): # Case 1: Adjacent + Opposite known if self.adjacent is not None and self.opposite is not None: self.hypotenuse = math.hypot(self.adjacent, self.opposite) self.area = (self.adjacent * self.opposite) / 2 # Case 2: Adjacent + Hypotenuse known elif self.adjacent is not None and self.hypotenuse is not None: self.opposite = math.sqrt(self.hypotenuse**2 - self.adjacent**2) self.area = (self.adjacent * self.opposite) / 2 # Case 3: Adjacent + Area known elif self.adjacent is not None and self.area is not None: self.opposite = (2 * self.area) / self.adjacent self.hypotenuse = math.hypot(self.adjacent, self.opposite) # Case 4: Opposite + Hypotenuse known elif self.opposite is not None and self.hypotenuse is not None: self.adjacent = math.sqrt(self.hypotenuse**2 - self.opposite**2) self.area = (self.adjacent * self.opposite) / 2 # Case 5: Opposite + Area known elif self.opposite is not None and self.area is not None: self.adjacent = (2 * self.area) / self.opposite self.hypotenuse = math.hypot(self.adjacent, self.opposite) # Case 6: Hypotenuse + Area known (requires quadratic solution) elif self.hypotenuse is not None and self.area is not None: sum_ao = math.sqrt(self.hypotenuse**2 + 4 * self.area) diff_ao = math.sqrt(self.hypotenuse**2 - 4 * self.area) self.adjacent = (sum_ao + diff_ao) / 2 self.opposite = (sum_ao - diff_ao) / 2 else: raise ValueError("Invalid or duplicate property pair provided")
Usage Example
# Original input order t1 = RightAngleTriangle("adjacent", 10, "opposite", 12) print(t1.adjacent) # 10 print(t1.opposite) # 12 print(t1.hypotenuse) # ~15.6205 print(t1.area) # 60 # Reversed input order (works the same!) t2 = RightAngleTriangle("opposite", 12, "adjacent", 10) print(t2.hypotenuse) # ~15.6205
Key Improvements Over Your Original Code
- Reduced Redundancy: We went from 12 if/else blocks to 6, since we no longer handle reversed property pairs separately.
- Better Readability: Logic is grouped by unique property combinations, making it easy to follow or modify individual calculations.
- Bug Fix: Your original code had an error in the
adjacent+hypotenusecase (using+instead of-in the square root)—this approach makes such mistakes easier to catch. - Modularity: The calculation logic is isolated in
_calculate_missing, so you can update math rules without touching input handling.
Bonus: Even More Scalable Approach (Using a Function Map)
If you want to make the code even more extensible, you can map property pairs to dedicated calculation functions:
import math class RightAngleTriangle: def __init__(self, prop1_type, prop1_val, prop2_type, prop2_val): self.adjacent = None self.opposite = None self.hypotenuse = None self.area = None known_properties = {prop1_type: prop1_val, prop2_type: prop2_val} for prop_name, value in known_properties.items(): setattr(self, prop_name, value) self._calculate_missing() def _calc_adj_opp(self): self.hypotenuse = math.hypot(self.adjacent, self.opposite) self.area = (self.adjacent * self.opposite) / 2 def _calc_adj_hyp(self): self.opposite = math.sqrt(self.hypotenuse**2 - self.adjacent**2) self.area = (self.adjacent * self.opposite) / 2 def _calc_adj_area(self): self.opposite = (2 * self.area) / self.adjacent self.hypotenuse = math.hypot(self.adjacent, self.opposite) def _calc_opp_hyp(self): self.adjacent = math.sqrt(self.hypotenuse**2 - self.opposite**2) self.area = (self.adjacent * self.opposite) / 2 def _calc_opp_area(self): self.adjacent = (2 * self.area) / self.opposite self.hypotenuse = math.hypot(self.adjacent, self.opposite) def _calc_hyp_area(self): sum_ao = math.sqrt(self.hypotenuse**2 + 4 * self.area) diff_ao = math.sqrt(self.hypotenuse**2 - 4 * self.area) self.adjacent = (sum_ao + diff_ao) / 2 self.opposite = (sum_ao - diff_ao) / 2 def _calculate_missing(self): # Get sorted tuple of known properties to handle order known_props = tuple(sorted( prop for prop in ["adjacent", "opposite", "hypotenuse", "area"] if getattr(self, prop) is not None )) # Map property pairs to their calculation functions calc_map = { ("adjacent", "opposite"): self._calc_adj_opp, ("adjacent", "hypotenuse"): self._calc_adj_hyp, ("adjacent", "area"): self._calc_adj_area, ("opposite", "hypotenuse"): self._calc_opp_hyp, ("opposite", "area"): self._calc_opp_area, ("hypotenuse", "area"): self._calc_hyp_area, } if known_props in calc_map: calc_map[known_props]() else: raise ValueError("Invalid property pair provided")
This version makes it trivial to add new property combinations (if needed) by just adding a new function and updating the calc_map.
内容的提问来源于stack exchange,提问作者J. Chapman

