从旧考试中了解Haskell类型

时间:2017-05-29 08:38:11

标签: haskell

我正在攻读Haskell考试,我对这个问题有点困惑: enter image description here

我的第一个想法是写这样的东西

paste :: Region -> Image a -> Image a -> Image a
paste (Image bol) img1 img2 = if bol
                              then -- do the pasting
                              else -- well do nothing

但我不知道怎么做粘贴。有人能指出我正确的方向吗?

2 个答案:

答案 0 :(得分:8)

虽然当你实际参加考试时这很有用,但这里是“教一个人拖网捕鱼”的答案:开始时没有定义任何结果,让编译器(必须GHC≥7.8)评论。请注意,存根是错误的:如果Image只是type - def,则不需要值构造函数Image来对其进行模式匹配。

newtype Image a = Image (Position -> a)
    -- it should be `newtype` instead of `type`, else there
    -- won't be an explicit `Image` value constructor
type Position = (Float, Float)
type Region = Image Bool

paste :: Region -> Image a -> Image a -> Image a
paste (Image bol) img1 img2 = _

GHC将回来

/tmp/wtmpf-file6644.hs:8:31:
    Found hole ‘_’ with type: Image a
    Where: ‘a’ is a rigid type variable bound by
               the type signature for
                 paste :: Region -> Image a -> Image a -> Image a
    ...

好的,这并不奇怪:我们已经知道结果应该是一张图片。现在,Image只有一个构造函数,即Image,所以我们确定需要构造这样的图像。仍然不了解内部实现

paste (Image bol) img1 img2 = Image _

给出

/tmp/wtmpf-file6644.hs:8:37:
    Found hole ‘_’ with type: Position -> a
    Where: ‘a’ is a rigid type variable bound by
               the type signature for
                 paste :: Region -> Image a -> Image a -> Image a
               at /tmp/wtmpf-file6644.hs:7:10

啊哈,所以隐藏在Image中的额外功能参数潜伏着!我们的模式也匹配:

paste (Image bol) img1 img2 = Image $ \pos -> _

/tmp/wtmpf-file6644.hs:8:47:
    Found hole ‘_’ with type: a
    ...

好的,a是完全不透明的,所以现在我们确定没有进一步的信息我们无法做什么。幸运的是,我们确实以参数的形式提供了进一步的信息。正如GHC将通知您:

    ...
    Relevant bindings include
      pos :: Position (bound at /tmp/wtmpf-file6644.hs:8:40)
      img2 :: Image a (bound at /tmp/wtmpf-file6644.hs:8:24)
      img1 :: Image a (bound at /tmp/wtmpf-file6644.hs:8:19)
      bol :: Position -> Bool (bound at /tmp/wtmpf-file6644.hs:8:14)

我们可以使用很多 ...这很好,因为这很明显应该做什么:我们可以提供pos作为bol的论据。然后,结果会输入Bool(与bol本身不同),因此现在可以成为if切换的合适人选。

paste (Image bol) img1 img2 = Image $ \pos -> if bol pos
         then _
         else _

...

Found hole ‘_’ with type: a
      ...
之前看过,所以我们需要更多的信息。让我们回顾一下这些论点:Image a仍然可以模式匹配

paste (Image bol) (Image img1) (Image img2)
  = Image $ \pos -> if bol pos
         then _
         else _

现在说:

    Relevant bindings include
      pos :: Position (bound at /tmp/wtmpf-file6644.hs:9:14)
      img2 :: Position -> a (bound at /tmp/wtmpf-file6644.hs:8:39)
      img1 :: Position -> a (bound at /tmp/wtmpf-file6644.hs:8:26)
      bol :: Position -> Bool (bound at /tmp/wtmpf-file6644.hs:8:14)

啊哈,所以img1img2可以根据需要提供a值,我们只需要先提供Position。好吧,我们仍然有pos,所以很明显该做什么:

paste (Image bol) (Image img1) (Image img2)
  = Image $ \pos -> if bol pos
         then img1 pos
         else img2 pos

...或交换两张图片(自己想一想)。但是,实际上并没有任何其他定义只能使用给定的信息(以及所有)来编写,所以这种类型的孔过程是实现函数的一种非常简单的方法。

答案 1 :(得分:1)

好的。您的屏幕坐标由[0,1]×[0,1]矩形表示。区域是该矩形的布尔函数,当且仅当坐标位于该区域时才返回true。然后你有两个图像,img1img2,它们是从坐标到像素的映射(或理论上的映射)。

您想要为img1reg以外的坐标img2返回一个新图像,该图像等于reg。图像是从坐标到像素的映射,因此您实际上正在返回一个函数。

类型为Region -> Image a -> Image a -> Image a,但请记住Image aPosition -> a,而RegionImage Bool,所以这真的是(Position -> Bool) -> (Position -> a) -> (Position -> a ) -> Position -> a。我们在Position之前需要最后的-> a参数,并将其作为三个函数参数的参数。

因此,

paste :: Region -> Image a -> Image a -> Image a
-- Is really: (Position->Bool) -> (Position->a) -> (Position->a) -> Position -> a
paste reg overlay bg coords | reg coords == True = overlay coords
                            | otherwise          = bg coords

带有测试用例的较长版本:

module Image where

type Image a = Position -> a
type Position = (Float, Float) -- [0,1]×[0,1]

data Color = RGB Int Int Int -- Pixels have values in [0,255]×[0,255]×[0,255].
  deriving Show
-- Not efficient for real-world use, but will do for now.

type Region = Image Bool
type ColorImage = Image Color

paste :: Region -> Image a -> Image a -> Image a
-- Is really: (Position->Bool) -> (Position->a) -> (Position->a) -> Position -> a
paste reg overlay bg coords | reg coords == True = overlay coords
                            | otherwise          = bg coords

allRed :: ColorImage
allRed _ = RGB 255 0 0

allWhite :: ColorImage
allWhite _ = RGB 255 255 255

unitCircle :: Region
unitCircle (x,y) = sqrt (x'*x' + y'*y') <= 0.5
  where x' = x - 0.5
        y' = y - 0.5

redCircleOnWhite :: ColorImage
redCircleOnWhite = paste unitCircle allRed allWhite

您可以在REPL中测试redCircleOnWhite