List 尝试从Haskell中的数据类型列表创建字符串列表
我试图获取一个数据类型列表(多边形),对每个多边形(calcPerim)执行一个操作,该操作返回一个(Double,Double)。然后我将其转换为字符串(lengthsToString)。我想要一个包含所有这些字符串的列表List 尝试从Haskell中的数据类型列表创建字符串列表,list,haskell,List,Haskell,我试图获取一个数据类型列表(多边形),对每个多边形(calcPerim)执行一个操作,该操作返回一个(Double,Double)。然后我将其转换为字符串(lengthsToString)。我想要一个包含所有这些字符串的列表 import System.IO import Data.List import Text.Printf type Point = (Double, Double) type Segment = (Point, Point) type Length = Double d
import System.IO
import Data.List
import Text.Printf
type Point = (Double, Double)
type Segment = (Point, Point)
type Length = Double
data Polygon = Polygon { vertices :: Int
, yline :: Double
, points :: [Point]
} deriving (Show)
main = do
let list = []
handle <- openFile "polycake.in" ReadMode
contents <- hGetContents handle
let singlewords = words contents
list = fileToList singlewords
n = list!!0
list' = drop 1 list
polygons = polyList n list'
output = outList polygons n
print (output)
hClose handle
outList :: [Polygon] -> Int -> [String]
outList polygons i =
if i < length polygons
then
let polygon = polygons!!i
perim = calcPerim (yline polygon) (points polygon)
perim' = fromJust perim
lenString = lengthsToString perim'
nextString = outList polygons (i+1)
in (lenString:nextString)
else []
show3Decimals :: Double -> String
show3Decimals x = printf "%.3f" x
lengthsToString :: (Double, Double) -> String
lengthsToString (min, max) = show3Decimals min ++ " " ++ show3Decimals max
maxLength :: (Length, Length) -> Length
maxLength (a, b) =
if a > b
then a
else b
minLength :: (Length, Length) -> Length
minLength (a, b) =
if a < b
then a
else b
--unwraps value from Just
fromJust :: Maybe a -> a
fromJust Nothing = error "Maybe.fromJust: Nothing"
fromJust (Just x) = x
--function to convert file to a list of Ints
--In: list of strings
--Out: list of Ints
fileToList :: [String] -> [Int]
fileToList = map read
--function to create a list of Polygon data types
--In: # of test cases, list containing test case data
--Out: list of Polygons
polyList :: Int -> [Int] -> [Polygon]
polyList n [] = []
polyList _ [x] = error "Too few points remaining"
polyList n (v:y:list') =
let pointList = take (2*v) list' -- Note: list' may not *have* 2*v points
points = getPoints pointList
list'' = drop (2*v) list'
poly = Polygon { vertices = v, yline = fromIntegral y, points = points}
nextPoly = polyList (n-1) list''
in (poly:nextPoly)
--function to convert a list of ints, into a list of tuples containing 2 Doubles
--In: list of ints
--Out: list of Points
getPoints :: [Int] -> [Point]
getPoints [] = []
getPoints (k:v:t) = (fromIntegral k, fromIntegral v) : getPoints t
-- Check whether a segment is over, under or on the line given by y
segmentCompare :: Double -> Segment -> Ordering
segmentCompare y (p,q) =
case () of
_ | all (`isUnder` y) [p,q] -> LT
_ | all (`isOver` y) [p,q] -> GT
_ -> EQ
-- Partition a list into (lt, eq, gt) based on f
partition3 :: (Segment -> Ordering) -> [Segment] -> ([Segment], [Segment], [Segment])
partition3 f = p' ([], [], [])
where
p' (lt, eq, gt) (x:xs) =
case f x of
LT -> p' (x:lt, eq, gt) xs
EQ -> p' (lt, x:eq, gt) xs
GT -> p' (lt, eq, x:gt) xs
p' result [] = result
-- Split a crossing segment into an under part and over part, and return middle
divvy :: Double -> Segment -> (Segment, Segment, Point)
divvy y (start, end) =
if start `isUnder` y
then ((start, middle), (middle, end), middle)
else ((middle, end), (start, middle), middle)
where
middle = intersectPoint y (start, end)
-- Split a polygon in two, or Nothing if it's not convex enough
splitPolygon :: Double -> [Point] -> Maybe ([Segment], [Segment])
splitPolygon y list = do
let (under, crossing, over) = partition3 (segmentCompare y) pairs
case crossing of
-- No lines cross. Simple.
[] -> return (under, over)
-- Two segments cross. Divide them up.
[(p1,p2),(q1,q2)] ->
let (u1, o1, mid1) = divvy y (p1,p2)
(u2, o2, mid2) = divvy y (q1, q2)
split = (mid1, mid2) :: Segment
in return (split:u1:u2:under, split:o1:o2:over)
-- More segments cross. Algorithm doesn't work.
rest -> fail "Can't split polygons concave at y"
where
pairs = zip list (drop 1 $ cycle list) :: [Segment]
--
calcPerim :: Double -> [Point] -> Maybe (Length, Length)
calcPerim y list = do
(under, over) <- (splitPolygon y list :: Maybe ([Segment], [Segment]))
return (sumSegments under, sumSegments over)
-- Self explanatory helpers
distance :: Segment -> Length
distance ((ax, ay), (bx, by)) = sqrt $ (bx-ax)^2 + (by-ay)^2
intersectPoint :: Double -> Segment -> Point
intersectPoint y ((px, py), (qx, qy)) =
let t = (y-py)/(qy-py)
x = px + t * (qx - px)
in
(x,y)
sumSegments :: [Segment] -> Length
sumSegments = sum . map distance
isUnder :: Point -> Double -> Bool
isUnder (_, py) y = py < y
isOver (_, py) y = py > y
样本输出:
12.000 12.000
25.690 35.302
17.071 27.071
主要的问题是在outList函数中,因为我已经测试了其他所有内容,现在我只想将输出放入一个列表中,以便以非常确定的方式将其打印出来。因为我需要通过在程序输出和给定输出之间进行“差异”来通过测试用例。首先,您当前的方法不是编写Haskell的最佳方法。很容易把事情搞砸。让我们试着后退一点。 您说过要“对每个多边形执行操作”。这就是函数的作用 看看类型签名:
map :: (a -> b) -> [a] -> [b]
在您的情况下,a
将是一个多边形
,b
将是一个字符串
。所以map函数将接受一个函数和一个多边形列表,并返回一个字符串列表,这就是您想要的
但是,您需要使用一些函数组合以及lambda来创建一个函数,该函数采用多边形
,并输出字符串
。
下面是一个可能的示例,说明您可以做什么:
outList :: [Polygon] -> [String]
outList polygons = map (\poly -> lengthsToString . fromJust $
calcPerim (yline poly) (points poly)) polygons
一般来说,您应该尽量避免Haskell中的显式递归,而是使用Prelude函数来完成您需要的工作。与手动操作相比,它还提供了更干净、更清晰和更短的代码。您能解释一下\poly->部分吗?(yline多边形)应该是(yline多边形)吗?poly是列表中的当前多边形吗?我得到一个错误:无法将预期类型“[String]”与实际类型“[polygon]->[String]”匹配抱歉,我在手机上编写了该代码,因此无法编译它。关于命名问题,你是对的。此外,我忘了将贴图应用到
多边形
列表。我认为代码现在应该可以工作了。您能简化您的示例并更清楚地说明问题所在吗:outList
是否引发某种运行时错误?它没有通过打字检查吗?(发布错误消息)或者它只是不能正常工作(它应该做什么)?它只是返回一个空列表,所以我假设它只是立即跳转到else,不需要假设。如果outList
返回[]
,那么您可以确定它的参数是什么?听起来您需要更仔细地考虑在main
中调用outList
的站点。
outList :: [Polygon] -> [String]
outList polygons = map (\poly -> lengthsToString . fromJust $
calcPerim (yline poly) (points poly)) polygons