为什么这个Functor实例不正确?

时间:2018-05-14 01:46:21

标签: haskell functional-programming

我写了这段代码:

newtype Pixel a = Pixel (a,a,a) deriving (Show)

instance Functor [Pixel Int] where
    fmap f [] = []
    fmap f [Pixel(a,b,c)] = [Pixel(f a, b, c)]

我希望仿函数应用于Pixel类型的第一个元素,但我一直收到此错误:

New.hs:17:18: error:
• Expecting one fewer arguments to ‘[Pixel Int]’
  Expected kind ‘* -> *’, but ‘[Pixel Int]’ has kind ‘*’
• In the first argument of ‘Functor’, namely ‘[Pixel Int]’
  In the instance declaration for ‘Functor [Pixel Int]’

我对此问题很遗憾,有没有办法在整个列表中应用仿函数?或者我是否需要为单个Pixel类型设置仿函数,然后然后遍历列表?

3 个答案:

答案 0 :(得分:5)

根据我的理解,您将获得一个像素列表,并且您想要更改每个像素的第一个分量(即红色分量)。因此,您需要以下功能:

changeAllPixels :: [Pixel Int] -> [Pixel Int]

问:我们如何更改列表的每个元素? 答:我们使用map

changeAllPixels = map changeOnePixel

changeOnePixel :: Pixel Int -> Pixel Int

我们只想更改红色组件。因此,我们有:

changeOnePixel = changeRedComponent doSomething

changeRedComponent :: (a -> a) -> Pixel a -> Pixel a
changeRedComponent f (Pixel (r, g, b)) = Pixel (f r, g, b)

doSomething :: Int -> Int

现在您只需要实施doSomething。例如,如果要反转红色组件,则可以按如下方式实现doSomething

doSomething x = 255 - x

请注意,我们没有将Pixel作为Functor的实例。这是因为我们只想更改红色组件并单独留下绿色和蓝色组件。但是,我们使用map作为列表的fmap

我认为你遇到的最大问题是你不能很好地理解仿函数。你可能应该花一些时间熟悉它们。

答案 1 :(得分:2)

实际上,auto [a, b] = func1(x, y, z); std::tie(a, b) = func2(x, y, z); 已经有[Pixel Int]的实例,因为它是一个列表Functor。列表[]的{​​{1}}实例在GHC base中定义(它使用Functor的定义)。现在您只需要一个可以应用于该列表的每个元素的函数。

[]

map通常是针对某种容器类型定义的。它需要一个函数并将其应用于容器的内容。然后,当您在具有fmap show [(Pixel 0 0 0),(Pixel 1 0 0), (Pixel 0 1 0)] 实例的容器上调用Functor时,编译器将检查该函数是否可以应用于该容器的元素。

如果您仍然对Functors感到困惑,我建议您使用本教程:Functors, Applicatives, And Monads In Pictures

答案 2 :(得分:1)

您的语法有点偏离,fmap将函数应用于数据类型,并告诉它如何。要更改像素列表的值,您需要在列表上映射(fmap f)。 试试这个实现。

instance Functor Pixel where
    fmap f (Pixel (a,b,c)) = Pixel (f a, b, c)

编辑这不会起作用,因为a,b,c需要属于同一类型,而仿函数允许类型为a->b的函数。

正如@AlexisKing所述,您应该使用fmap,而是编写类似mapPixelFirst :: (a -> a) -> Pixel a -> Pixel a的函数。然后将此功能映射到用户列表,不要使用fmap。