Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 具有多个数据构造函数的单一类型与多个类型_Haskell - Fatal编程技术网

Haskell 具有多个数据构造函数的单一类型与多个类型

Haskell 具有多个数据构造函数的单一类型与多个类型,haskell,Haskell,考虑以下描述一些二维形状结构的类型: data DrawingElem = Rect Pos Size | Circle Pos Radius | Ellipse Pos Radius Radius | Line Pos Pos | Polygon [Pos] | Polyline [Pos] | Group [DrawingElem] | Drawing [DrawingElem] 使用这些定义

考虑以下描述一些二维形状结构的类型:

data DrawingElem
    = Rect     Pos Size
    | Circle   Pos Radius
    | Ellipse  Pos Radius Radius
    | Line     Pos Pos
    | Polygon  [Pos]
    | Polyline [Pos]

    | Group    [DrawingElem]
    | Drawing  [DrawingElem]
使用这些定义的:

data Vec    = Vec Double Double

type Pos    = Vec
type Size   = Vec
type Radius = Double
DrawingElem
的最后两个数据构造函数在某种程度上是特殊的,因为它们使其他类型的树状排列成为可能

mydrawing = Drawing [Rect (Vec 0 0) (Vec 10 10),
                     Group (Vec 30 40) [Circle (Vec 0 0) 90,
                                        Line (Vec 0 0) (Vec 50 50)]]
这样的数据结构最终应转换为可呈现的SVG字符串:

toSvg :: DrawingElem -> String
toSvg (Drawing xs)               = "<svg>" ++ concatMap toSvg xs ++ "</svg>"
toSvg (Group xs)                 = "<g>" ++ concatMap toSvg xs ++ "</g>"
toSvg (Rect (Vec x y) (Vec w h)) = "<rect x='" ++ x ... "</rect>"
这当然不适用于上述定义,必须:

setSize :: Size -> DrawingElem -> DrawingElem
setSize (Rect p s) = ..
setSize x          = x
因此,我必须实现一个通配符,使函数完整。然而,在没有得到类型错误的情况下编写
setSize someSize someCircle
,对我来说似乎有问题

最后,我在努力将绘图元素包装在一个类型中,而不是让它们成为不同的类型。如上所述,在不同的情况下需要这两个属性。 有人对此有什么建议吗?是一种非此即彼的方法,还是有一种可以同时利用这两种方法的建模方法

然而,在没有得到类型错误的情况下编写
setSize someSize someCircle
,对我来说似乎有问题

这确实是个问题。为了避免这种情况,我建议使用第三种方法:也许您根本不需要一个特定于矩形的
setSize
函数。另一种方法是保持单个
DrawingElem
类型,在矩形构造上设置初始尺寸(在圆形构造上设置初始半径等),并使用可用于所有类型图元的函数在构造后调整尺寸,例如:

scale :: Double -> DrawingElem -> DrawingElem

scaleX :: Double -> DrawingElem -> DrawingElem

scaleY :: Double -> DrawingElem -> DrawingElem

这与光泽处理形状的方式非常相似(参见和一些)。另一个值得一提的例子是,它使用了一个非常复杂的图片模型,涉及到过多的类型和类,并且以类似的方式处理缩放等操作

一个选项是使用另一个间接层,每个元素都有一个精确的类型:

data DrawingElem
    = DERect     Rect
    | DECircle   Circle
    ...

data Rect   = Rect Pos Size
data Circle = Circle Pos Radius

toSvg :: DrawingElem -> String
...

setSize :: Size -> Rect -> Rect
...
这里的一个小缺点是,我们需要使两个层的模式匹配,例如

toSvg (DERect (Rect pos size)) = ...
更高级的替代方案可能是使用GADT。不过,这对于您的任务来说可能有些过分

{-# LANGUAGE GADTs, DataKinds #-}

data ElemType = Rect | Circle | ...

data DrawingElem (t :: ElemType) where
   DERect   :: Pos -> Size   -> DrawingElem Rect
   DECircle :: Pos -> Radius -> DrawingElem Circle
   ...

-- this works on all element types t
toSvg :: DrawingElem t -> String
...

-- this works only on a rectangle element
setSize :: Size -> DrawingElem Rect -> DrawingElem Rect
setSize size (DERect pos _) = DERect pos size

我不相信你是否真的需要这个。如果有疑问,请使用更简单的替代方法。

我调整了数据类型定义中的一些语法。请检查我是否更改了不应该更改的内容。需要您进行初始编辑。但是,您似乎在另一次编辑中还原了它们。它肯定应该是DrawingElement,因此我们应该回滚到修订版2吗?我还原了它们,因为我不能100%确定您想要的
DrawingElem
Shape
是同一件事(这意味着一个组可以包含其他组,一个图形可以包含其他图形).如果是这样,我将重新还原它们。这篇文章看起来像是现在就要发布的。但是,可能会讨论引入另一种类型形状是否有意义。我给出的示例可能不是最好的。对于缩放,我认为您的方法是合理的。但是,会有其他不同的属性更多的是形状。例如,一些形状可以用颜色填充,其他的则不能。@AntonHarald在这种情况下的替代方案可能包括使用间接层,如chi的回答,并引入一些不同的类型来覆盖无法克服的差异,加上一个常见的“较低级别”中间表示法支持一组最小的操作,您可以在必要时将“更高级别”元素转换为这些操作(例如用于对它们进行分组)。对于第一种方法,甚至可以有如下默认构造函数:
rect f=toSvg.f$(rect(Vec 1)(Pos 0 0))
,可以这样使用:
rect(填充红色。尺寸4。位置7 5)
。或者你知道在这样的系统中方便地创建形状的另一种方法吗?第二种方法,我会把它放在口袋里,以备将来的项目使用。只是意识到
rect
构造函数必须要复杂一点,例如设置默认颜色。但是它应该显示这一点。@AntonHarald是的,如果你愿意,你可以这样做。Sometimes记录更新用于类似的目标,例如,
defaultRect{pos=…,size=…}
,其中所有未设置的字段都由默认矩形值给出。
{-# LANGUAGE GADTs, DataKinds #-}

data ElemType = Rect | Circle | ...

data DrawingElem (t :: ElemType) where
   DERect   :: Pos -> Size   -> DrawingElem Rect
   DECircle :: Pos -> Radius -> DrawingElem Circle
   ...

-- this works on all element types t
toSvg :: DrawingElem t -> String
...

-- this works only on a rectangle element
setSize :: Size -> DrawingElem Rect -> DrawingElem Rect
setSize size (DERect pos _) = DERect pos size