Haskell 什么';我的格雷厄姆扫描功能出了什么问题?

Haskell 什么';我的格雷厄姆扫描功能出了什么问题?,haskell,grahams-scan,Haskell,Grahams Scan,我几乎完成了真实世界哈斯克尔的第三章。最后一个练习阻碍了我。我的代码在运行时会崩溃。有人能告诉我代码中哪一部分是错的吗?谢谢 问题: 使用前面三个练习中的代码,对一组2D点的凸包实现Graham的扫描算法。你可以在维基百科上找到关于凸包是什么以及格雷厄姆扫描算法应该如何工作的很好的描述 答复: -- Graham Scan, get the convex hull. -- Implement the algorithm on http://en.wikipedia.org/wiki/Graham

我几乎完成了真实世界哈斯克尔的第三章。最后一个练习阻碍了我。我的代码在运行时会崩溃。有人能告诉我代码中哪一部分是错的吗?谢谢

问题: 使用前面三个练习中的代码,对一组2D点的凸包实现Graham的扫描算法。你可以在维基百科上找到关于凸包是什么以及格雷厄姆扫描算法应该如何工作的很好的描述

答复:

-- Graham Scan, get the convex hull.
-- Implement the algorithm on http://en.wikipedia.org/wiki/Graham_scan

import Data.List
import Data.Ord

data Direction = TurnLeft | TurnRight | GoStraight deriving (Eq, Show)

-- Determine if three points constitute a "left turn" or "right turn" or "go straight".
-- For three points (x1,y1), (x2,y2) and (x3,y3), simply compute the direction of the cross product of the two vectors defined by points (x1,y1), (x2,y2) and (x1,y1), (x3,y3), characterized by the sign of the expression (x2 − x1)(y3 − y1) − (y2 − y1)(x3 − x1). If the result is 0, the points are collinear; if it is positive, the three points constitute a "left turn", otherwise a "right turn".
direction a b c = case compare ((x2 - x1) * (y3 - y1)) ((y2 - y1) * (x3 - x1)) of
    EQ -> GoStraight
    GT -> TurnLeft
    LT -> TurnRight
    where   x1 = fst a
            y1 = snd a
            x2 = fst b
            y2 = snd b
            x3 = fst c
            y3 = snd c

grahamScan points = scan (sort points)
            -- For each point, it is determined whether moving from the two previously considered points to this point is a "left turn" or a "right turn". If it is a "right turn", this means that the second-to-last point is not part of the convex hull and should be removed from consideration. This process is continued for as long as the set of the last three points is a "right turn". As soon as a "left turn" is encountered, the algorithm moves on to the next point in the sorted array. (If at any stage the three points are collinear, one may opt either to discard or to report it, since in some applications it is required to find all points on the boundary of the convex hull.)
    where   scan (a : (b : (c : points)))
                | (direction a b c) == TurnRight    = scan (a : (c : points))
                | otherwise                         = a : (scan (b : (c : points)))
            scan (a : (b : []))                     = []
            -- Put prime to the head.
            sort points                             = prime : (reverse (sortBy (compareByCosine prime) rest))
                        -- Sort a list to compute. The first step is to find a point whose y-coordinate is lowest. If there are more than one points with lowest y-coordinate, take the one whose x-coordinate is lowest. I name it prime.
                where   compareByYAndX a b
                            | compareByY == EQ  = comparing fst a b
                            | otherwise         = compareByY
                            where   compareByY  = comparing snd a b 
                        prime                   = minimumBy compareByYAndX points
                        -- Delete prime from the candidate points. Sort the rest part by the cosine value of the angle between each vector from prime to each point. Reverse it and put prime to the head.
                        rest                    = delete prime points
                        compareByCosine p a b   = comparing cosine pa pb
                            where   pa          = (fst a - fst p, snd a - snd p)
                                    pb          = (fst b - fst p, snd b - snd p)
                                    cosine v    = x / sqrt (x ^ 2 + y ^ 2)
                                        where   x = fst v
                                                y = snd v

在对已排序的点集执行扫描时,完成扫描后,您将丢弃应该在外壳中的点。在这一行

scan (a : (b : [])) = []
您正在删除外壳中的最后两个点。真的应该是

scan (a : (b : [])) = [a, b]
甚至更好

scan ps = ps

其中只涉及一点。

您能否提供有关代码失败原因的更多详细信息?错误消息,无限循环。。。?可能是一个示例输入和预期输出。让我们很容易地重现这个问题。只是一个风格说明:你应该在where子句中写
(x1,x2)=a
等。而不是使用
fst
snd
@Landei,或者仅仅是
方向(x1,y1)(x2,y2)(x3,y3)=……
这不会崩溃,只是给出了错误的答案(用于我的测试)。也许除了重新表述问题外,您还应该重新格式化代码,以便其他人可以阅读它(删除嵌套的where子句并添加类型签名)。我甚至不知道您可以执行嵌套的
where
s。