【问题标题】:haskell fromInteger with multiple data constructorshaskell fromInteger 具有多个数据构造函数
【发布时间】:2015-11-11 01:35:52
【问题描述】:

我正在寻找一种我认为非常通用的解决方案。我的目标是找到一种干净的方法来对点(坐标)进行操作,无论是 2D 还是 3D。我的数据点是 Num 的一个实例,它提供了执行基本数学运算的函数。

data Point = Point2D Float Float
       | Point3D Float Float Float deriving Show

add :: Point -> Point -> Point
add (Point2D xa ya) (Point2D xb yb) = Point2D (xa+xb) (ya+yb)
add (Point3D xa ya za) (Point3D xb yb zb) = Point3D (xa+xb) (ya+yb) (za+zb)

divT (Point2D x y) v = Point2D (x / v) (y / v)
divT (Point3D x y z) v = Point3D (x / v) (y / v) (z / v)

fromIntegerP :: Integer -> Point
fromIntegerP v = Point2D (fromIntegral v) 0
--fromIntegerP v = Point3D (fromIntegral v) 0 0

instance Num Point where
    (+) = add
    fromInteger = fromIntegerP

p2D1 = Point2D 1.0 2.0
p2D2 = Point2D 4.0 5.0
p3D1 = Point3D 1.0 2.0 3.0
p3D2 = Point3D 6.0 6.0 6.0

main = do
    putStrLn . show $ sum [p2D1,p2D2]
    putStrLn . show $ sum [p3D1,p3D2]

此代码输出:

Point2D 5.0 7.0
*** Exception: pointStackoverflow.hs:(5,1)-(6,75): Non-exhaustive patterns in function add

...因为每个 Point.fromInteger 都会产生一个 Point2D,即使我们期望一个 Point3D。我想在“instance Num Point”中说,如果需要 Point2D,则 fromInteger 是 fromIntegerToPoint2D,否则 fromIntegerToPoint3D。但是我不知道如何根据返回类型做出选择。

有什么提示吗?谢谢

【问题讨论】:

  • 看来你需要在类型级别区分Point2DPoint3D,这样你就可以给他们不同的Num实例。
  • 提示?不,事实?是的。如果您希望 fromInteger 自动将文字转换为两种形式之一,则需要为这两个点使用不同的类型。另一种类型比这有更多的好处——你对二维和三维的Point 的滥用真的很骇人听闻,这意味着像+ 这样的操作是部分的。例如,Point2D x y + Point3D a b c 将因模式匹配不完整而失败。
  • @user2407038 和 M. DuBuisson:感谢您的回答,这是有道理的!我会尽快发布工作版本。
  • 您能否从您的问题中删除解决方案并将其作为正确答案提交? (与元问题略有相关meta.stackoverflow.com/q/309266/2564301
  • 是的,感谢您的“元反馈”。

标签: haskell constructor return


【解决方案1】:

您想让Point2DPoint3D 成为不同的类型,而不是同一类型的不同构造函数。

从这里开始并填写定义:

data Point2D = Point2D Float Float
data Point3D = Point3D Float Float Float

instance Num Point2D where
    fromInteger = ...
    (+) = ...

instance Num Point3D where
    fromInteger = ...
    (+) = ...

【讨论】:

    【解决方案2】:

    这是一种解决方案:

    class Point a where
        add :: a -> a -> a
        fromIntegerP :: Integer -> a
    
    data Point2D = Point2D Float Float deriving Show
    data Point3D = Point3D Float Float Float deriving Show
    
    instance Point Point2D where
        add (Point2D xa ya) (Point2D xb yb) = Point2D (xa+xb) (ya+yb)
        fromIntegerP v = Point2D (fromIntegral v) 0
    
    instance Point Point3D where    
        add (Point3D xa ya za) (Point3D xb yb zb) = Point3D (xa+xb) (ya+yb) (za+zb)
        fromIntegerP v = Point3D (fromIntegral v) 0 0
    
    instance Num Point2D where
        (+) = add
        fromInteger = fromIntegerP
    
    instance Num Point3D where
        (+) = add
        fromInteger = fromIntegerP
    
    p2D1 = Point2D 1.0 2.0
    p2D2 = Point2D 4.0 5.0
    
    p3D1 = Point3D 1.0 2.0 3.0
    p3D2 = Point3D 6.0 6.0 6.0
    
    main = do
        putStrLn . show $ sum [p2D1,p2D2]
        putStrLn . show $ sum [p3D1,p3D2]
    

    我仍在寻求改进(instance Num Point2D whereinstance Num Point3D where),但它确实有效!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多