在网站上,我发现了以下挑战并被抓住了,我不知道如何解决它。我们需要定义以下函数:
isSquare :: (Num a, Ord a) => (a,a) -> (a,a) -> (a,a) -> (a,a) -> Bool
当点是正方形的顶点时返回True,否则返回False。
isSquare (1,1) (1,1) (1,1) (1,1) == True
isSquare (0,0) (0,2) (3,2) (3,0) == False
isSquare (0,0) (3,4) (8,4) (5,0) == False
isSquare (0,0) (0,0) (1,1) (0,0) == False
我试过这个:
定义计算点之间距离的函数
将distance
映射到由points格式化的列表。
如果距离相等,则为True
答案 0 :(得分:3)
我确定有很多方法可以解决这个问题,但这是我遇到的第一个问题。
import Data.Functor
import Data.Maybe
import Control.Applicative
import Control.Monad
首先让我们为二维向量定义一个类型,并对它们进行一些基本操作。我们需要能够添加和减去,并使用点积来测试正交性。
data V2 a = V2 a a
deriving Eq
(^+), (^-) :: Num a => V2 a -> V2 a -> V2 a
V2 x y ^+ V2 x' y' = V2 (x + x') (y + y')
V2 x y ^- V2 x' y' = V2 (x - x') (y - y')
infixl 6 ^+, ^-
dotProduct :: (Num a) => V2 a -> V2 a -> a
dotProduct (V2 x y) (V2 x' y') = (x * x') + (y * y')
orthogonal :: (Eq a, Num a) => V2 a -> V2 a -> Bool
orthogonal a b = dotProduct a b == 0
我们还会定义一个线段:
data LineSegment a = LineSegment (V2 a) (V2 a)
isSquare
函数将从前三个点开始,看它们是否形成一个直角三角形。因此,让我们定义一个直角三角形类型,以及一个将三个点转换为直角三角形的函数,如果它们之间存在直角的话。
data RightTriangle a =
RightTriangle
(V2 a) -- Point at the right angle
(LineSegment a) -- Hypotenuse
rightTriangleMaybe :: (Eq a, Num a)
=> V2 a -> LineSegment a -> Maybe (RightTriangle a)
rightTriangleMaybe x hyp@(LineSegment a b) =
guard (orthogonal (x ^- a) (x ^- b)) $> RightTriangle x hyp
rightTriangleMaybe' :: (Eq a, Num a)
=> V2 a -> V2 a -> V2 a -> Maybe (RightTriangle a)
rightTriangleMaybe' a b c =
rightTriangleMaybe a (LineSegment b c) <|>
rightTriangleMaybe b (LineSegment a c) <|>
rightTriangleMaybe c (LineSegment a b)
测试四个点是否形成正方形现在归结为两个步骤:
isSquare :: (Eq a, Num a) => V2 a -> V2 a -> V2 a -> V2 a -> Bool
isSquare a b c d = isJust $ do
RightTriangle x (LineSegment y z) <- rightTriangleMaybe' a b c
guard $ y ^+ z ^- x == d
(尝试绘制视觉证据以了解为什么第四点必须位于y + z - x
)
试验:
λ> isSquare (V2 0 1) (V2 1 0) (V2 0 (-1)) (V2 (-1) 0)
True
λ> isSquare (V2 0 1) (V2 1 0) (V2 0 (-1)) (V2 (-2) 0)
False
λ> isSquare (V2 0 1) (V2 1 0) (V2 0 0) (V2 1 1)
True
λ> isSquare (V2 0 1) (V2 1 0) (V2 0 0) (V2 1 2)
False
λ> isSquare (V2 3 4) (V2 3 4) (V2 3 4) (V2 3 4)
True
λ> isSquare (V2 3 4) (V2 3 4) (V2 3 4) (V2 3 5)
False
答案 1 :(得分:0)
我们创建一个函数,返回两点之间的笛卡尔距离:
dist (x1, y1) (x2, y2) = sqrt $ (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)
从每个点我们可以计算到其他三个点的距离。
对于正方形,这些距离对于每个点都是相同的,并且将具有[a, a, sqrt(2*a^2)]
形式。可以测试以下内容以确定给定的一组点形成正方形:
在实践中,我们不需要为所有点建立上述条件,因此您可以从以下函数中删除几行
import Data.List
isSquare (x1, y1) (x2, y2) (x3, y3) (x4, y4) =
let m1 = dist (x1,y1)
m2 = dist (x2,y2)
m3 = dist (x3,y3)
m4 = dist (x4,y4)
d1 = sort $ map m1 [(x2,y2),(x3,y3),(x4,y4)]
d2 = sort $ map m2 [(x1,y1),(x3,y3),(x4,y4)]
d3 = sort $ map m3 [(x1,y1),(x2,y2),(x4,y4)]
d4 = sort $ map m4 [(x1,y1),(x2,y2),(x3,y3)]
in d1 == d2 && d2 == d3 && d3 == d4 && d1!!0 == d1!!1 && sqrt((d1!!0)^2+(d1!!1)^2) == d1!!2
这里d1,d2,d3&amp; d4是每个点的排序距离列表
示例:
表示点(0,0) (0,1) (1,1) (1,0)
点(0,0)的排序距离列表为[1,1,1.414]
* distance to (0,1) = 1
* distance to (1,0) = 1
* distance to (1,1) = sqrt(2) ~ 1.414
同样,所有其他点的距离列表为[1,1,1.414]
我们通过在列表上映射部分距离函数来计算距离,然后我们进行排序,以确保较短的距离保持在头部。
然后我们可以查看距离列表并确保前两个元素相等,
d1!!0 == d1!!1
并且对角线距离等于两个边缘距离的平方和的平方根
sqrt((d1!!0)^2+(d1!!1)^2) == d1!!2
您可以将此最后一个条件等效地写为
(d1!!0)^2+(d1!!1)^2 == (d1!!2)^2
实际上,正如用户@dfeuer指出的那样,我们可以使用平方距离来建立一组点是一个正方形。然后,函数可以写为
dist (x1, y1) (x2, y2) = (x2-x1)^2 + (y2-y1)^2
isSquare (x1,y1) (x2,y2) (x3,y3) (x4,y4) =
let m1 = dist (x1,y1)
m2 = dist (x2,y2)
m3 = dist (x3,y3)
m4 = dist (x4,y4)
d1 = sort $ map m1 [(x2,y2),(x3,y3),(x4,y4)]
d2 = sort $ map m2 [(x1,y1),(x3,y3),(x4,y4)]
d3 = sort $ map m3 [(x1,y1),(x2,y2),(x4,y4)]
d4 = sort $ map m4 [(x1,y1),(x2,y2),(x3,y3)]
in d1 == d2 && d2 == d3 && d3 == d4 && d1!!0 == d1!!1 && 2*d1!!0 == d1!!2