测试Haskell中的四个点是否形成正方形

时间:2018-02-17 17:37:21

标签: haskell

在网站上,我发现了以下挑战并被抓住了,我不知道如何解决它。我们需要定义以下函数:

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

我试过这个:

  1. 定义计算点之间距离的函数

  2. distance映射到由points格式化的列表。

  3. 如果距离相等,则为True

2 个答案:

答案 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)

测试四个点是否形成正方形现在归结为两个步骤:

  1. 在前三个点中找到正确的角度
  2. 测试第四点是否在正确的位置
  3. 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)]形式。可以测试以下内容以确定给定的一组点形成正方形:

  • 所有距离列表都相同
  • 较短的两个距离相等
  • 最长距离= sqrt(2 * a ^ 2)其中a是短距离

在实践中,我们不需要为所有点建立上述条件,因此您可以从以下函数中删除几行

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