List 惯用表结构

List 惯用表结构,list,haskell,functional-programming,idioms,List,Haskell,Functional Programming,Idioms,一般来说,我对Haskell和函数式编程非常陌生,所以我真的不知道如何使这段代码习惯化: type Coord = Double data Point = Point Coord Coord Coord deriving Show type Polyline = [Point] -- Add a point to a polyline addPoint :: Polyline -> Point -> Polyline addPoint line p = p:line line

一般来说,我对Haskell和函数式编程非常陌生,所以我真的不知道如何使这段代码习惯化:

type Coord = Double
data Point = Point Coord Coord Coord deriving Show

type Polyline = [Point]

-- Add a point to a polyline
addPoint :: Polyline -> Point -> Polyline
addPoint line p = p:line

line :: Polyline
line = []

constructLine :: Polyline -> Polyline
constructLine line =
    let 
        p1 = Point 2 4 87
        p2 = Point 3 7 2
        p3 = Point 23 4 8
    in addPoint (addPoint (addPoint line p1) p2) p3

main :: IO() 
main = do
    putStrLn ( show (constructLine line))

我的问题是
constructLine
函数。如果我想添加很多点,嵌套的
addPoint
函数将是一个问题。我怎样才能考虑到这一点?还有其他可以改进的地方吗?

对addPoints的多次调用可以用一次折叠来代替。正如一条评论中所建议的,反转addPoint函数将使事情变得更简单:

addPoint' :: Point -> Polyline ->  Polyline
addPoint' p line = p:line
这样,您的constructLine函数就可以构建一个临时的点列表来添加使用折叠:

constructLine :: Polyline -> Polyline
constructLine line =
    let
        p1 = Point 2 4 87
        p2 = Point 3 7 2
        p3 = Point 23 4 8
    in foldr addPoint' line [p3,p2,p1]

这不会破坏封装(您可以用点列表以外的其他内容替换多段线的实现),并按照新点的结束顺序(p2前面的p3等)使用新点。

对addPoints的多次调用可以用折叠来代替。正如一条评论中所建议的,反转addPoint函数将使事情变得更简单:

addPoint' :: Point -> Polyline ->  Polyline
addPoint' p line = p:line
这样,您的constructLine函数就可以构建一个临时的点列表来添加使用折叠:

constructLine :: Polyline -> Polyline
constructLine line =
    let
        p1 = Point 2 4 87
        p2 = Point 3 7 2
        p3 = Point 23 4 8
    in foldr addPoint' line [p3,p2,p1]

这不会破坏封装(您可以用点列表以外的其他内容替换多段线的实现),并按照新点的结束顺序(p2前面的p3等)使用新点。

您的
构造线
示例让我觉得这是一个冗长的版本:

constructLine :: Polyline -> Polyline
constructLine line = [Point 23 4 8, Point 3 7 2, Point 2 4 87] ++ line
type Coord = Double
data Point = Point Coord Coord Coord deriving Show

main :: IO () 
main = putStrLn (show points)
     where points = [Point 23 4 8, Point 3 7 2, Point 2 4 87]
我不知道您是否遇到过这些问题,但可以肯定的是:

  • [第23 4 8点、第3 7 2点、第2 4 87点]
    只是一个列表文字
  • ++
    是附加列表的函数
通常,要向列表中添加多个元素,您需要做的是创建一个要添加的元素列表,并将该列表与原始列表一起追加

这种模式还在继续。如果我们注意到,
是一个定义为
[]
的常数,那么您的整个程序实际上只是一个冗长的版本:

constructLine :: Polyline -> Polyline
constructLine line = [Point 23 4 8, Point 3 7 2, Point 2 4 87] ++ line
type Coord = Double
data Point = Point Coord Coord Coord deriving Show

main :: IO () 
main = putStrLn (show points)
     where points = [Point 23 4 8, Point 3 7 2, Point 2 4 87]

基本上,如果点的值在编译时已知,您只需编写列表,无需进行所有这些间接操作

您的
constructLine
示例对我来说是一个冗长的版本:

constructLine :: Polyline -> Polyline
constructLine line = [Point 23 4 8, Point 3 7 2, Point 2 4 87] ++ line
type Coord = Double
data Point = Point Coord Coord Coord deriving Show

main :: IO () 
main = putStrLn (show points)
     where points = [Point 23 4 8, Point 3 7 2, Point 2 4 87]
我不知道您是否遇到过这些问题,但可以肯定的是:

  • [第23 4 8点、第3 7 2点、第2 4 87点]
    只是一个列表文字
  • ++
    是附加列表的函数
通常,要向列表中添加多个元素,您需要做的是创建一个要添加的元素列表,并将该列表与原始列表一起追加

这种模式还在继续。如果我们注意到,
是一个定义为
[]
的常数,那么您的整个程序实际上只是一个冗长的版本:

constructLine :: Polyline -> Polyline
constructLine line = [Point 23 4 8, Point 3 7 2, Point 2 4 87] ++ line
type Coord = Double
data Point = Point Coord Coord Coord deriving Show

main :: IO () 
main = putStrLn (show points)
     where points = [Point 23 4 8, Point 3 7 2, Point 2 4 87]

基本上,如果点的值在编译时已知,您只需编写列表,无需进行所有这些间接操作

提供一个函数,该函数在给定点列表的情况下构造多段线。例如,您可以执行添加点行ps=ps++line或类似操作。顺便说一句:你也可以用中缀符号调用
addPoint
。惯用的方法可能是放弃
addPoint
,直接将列表构建为
[p3,p2,p1]
,但是如果你想保留这个抽象,那么只需创建你自己的操作符,比如
infixr 5
(\code>)::Point->Polyline->Polyline
(#)=addPoint
(注意,我在这里交换了参数顺序)。
infixr
行很重要,请确保您保留它,但这意味着您可以将其写成
p3#p2#p1#行
。如果您真的想保持参数顺序,请执行
infixl 5#
,它将被写为
line#p1#p2#p3
。请注意,如果您更改参数顺序,使其成为
addPoint::Point->Polyline->Polyline
,则可以将其写为
addPoint p3$addPoint p2$addPoint p1 line
,这对于您的目的可能已经足够了。请提供一个函数,该函数给定一个点列表来构造一条多段线。例如,您可以执行添加点行ps=ps++line或类似操作。顺便说一句:你也可以用中缀符号调用
addPoint
。惯用的方法可能是放弃
addPoint
,直接将列表构建为
[p3,p2,p1]
,但是如果你想保留这个抽象,那么只需创建你自己的操作符,比如
infixr 5
(\code>)::Point->Polyline->Polyline
(#)=addPoint
(注意,我在这里交换了参数顺序)。
infixr
行很重要,请确保您保留它,但这意味着您可以将其写成
p3#p2#p1#行
。如果您真的想保持参数顺序,请执行
infixl 5#
,它将被写为
line#p1#p2#p3
。请注意,如果您更改参数顺序,使其成为
addPoint::Point->Polyline->Polyline
,则可以将其写为
addPoint p3$addPoint p2$addPoint p1 line
,这对你的目的可能足够了。是的,我知道这一点,但我想抽象多段线结构。无论如何,谢谢你的回答@ElieGnrd:如果多段线在概念上类似于列表,但有一些不变量需要保护,一种简单的方法是在不透明的
多段线
类型和点列表之间来回转换函数。然后,可以通过列表操作(Haskell非常支持)完成很多逻辑,要创建
多段线,只需提供一个列表即可。例如,Haskell平台中的
Data.Map.Map
类型的工作原理如下:创建一个映射的最常见方法之一是使用函数
fromList::Ord k=>[(k,v)]->Map k