Haskell中判断四点是否构成正方形的isSquare函数实现问题
Hey there! Let's work through fixing your square-detection function in Haskell. Your initial thought to use distances is a solid starting point, but it misses a few critical details that are tripping you up—let's break this down clearly.
The Problem With Your Initial Approach
Checking if all pairwise distances are equal only works if all four points are identical (which definitely isn't a square!). A valid square has two distinct pairwise distances:
- 4 instances of the side length (between adjacent vertices)
- 2 instances of the diagonal length (between opposite vertices)
Plus, the diagonal length squared should be exactly twice the side length squared (thanks to the Pythagorean theorem: (d^2 = s^2 + s^2 = 2s^2)).
A Robust Solution
We can avoid floating-point precision issues entirely by working with distance squared instead of actual distances. Here's how to implement this:
First, import Data.List for sorting (we'll need it to group our distances):
import Data.List (sort)
Next, write a helper function to calculate the squared distance between two points:
distanceSquared :: (Num a, Ord a) => (a,a) -> (a,a) -> a distanceSquared (x1, y1) (x2, y2) = (x1 - x2)^2 + (y1 - y2)^2
Now, build the isSquare function. We'll generate all unique pairwise distances, sort them, and verify they match the pattern we expect for a square:
isSquare :: (Num a, Ord a) => (a,a) -> (a,a) -> (a,a) -> (a,a) -> Bool isSquare p1 p2 p3 p4 = let points = [p1, p2, p3, p4] -- Generate all unique pairwise squared distances (6 total for 4 points) dists = sort [ distanceSquared a b | a <- points, b <- points, a < b ] in case dists of -- Check for 4 equal side lengths, 2 equal diagonals, valid proportions, and no overlapping points [sideSq, sideSq, sideSq, sideSq, diagSq, diagSq] -> sideSq > 0 && diagSq == 2 * sideSq -- Any other pattern means it's not a square _ -> False
Why This Works
- No floating points: Using squared distances avoids precision errors that come with square roots.
- Handles any vertex order: Since we check all pairwise distances, the input order of the four points doesn't matter.
- Eliminates edge cases: We explicitly check that
sideSq > 0to rule out overlapping points, and verify the diagonal/side proportion to avoid mistaking rhombuses or other quadrilaterals for squares.
Example Tests
To confirm it works, try these cases:
-- A square with side length 1 isSquare (0,0) (0,1) (1,1) (1,0) -- Returns True -- A rhombus that's not a square isSquare (0,0) (1,2) (3,2) (2,0) -- Returns False -- Four identical points isSquare (5,5) (5,5) (5,5) (5,5) -- Returns False
内容的提问来源于stack exchange,提问作者mathandtic

