10

我有一个类型Image,它基本上是一个 c 数组的浮点数。很容易创建诸如map :: (Float -> Float) -> Image -> Image或之类的函数zipWith :: (Float -> Float -> Float) -> Image -> Image -> Image

但是,我有一种感觉,在这些函数之上提供看起来像应用程序实例的东西也是可能的,从而允许更灵活的像素级操作,例如((+) <$> image1 <*> image2)((\x y z -> (x+y)/z) <$> i1 <*> i2 <*> i3)。然而,天真的方法失败了,因为 Image 类型不能包含除浮点数以外的东西,因此无法实现fmap

这怎么可能实现?

4

3 回答 3

15

阅读评论,我有点担心这里的尺寸在地毯下。尺寸不匹配时是否有明智的行为?

同时,您可以按照以下思路明智地做一些事情。即使你的数组​​不容易实现多态,你也可以Applicative像这样创建一个实例。

data ArrayLike x = MkAL {sizeOf :: Int, eltOf :: Int -> x}

instance Applicative ArrayLike where
  pure x                 = MkAL maxBound  (pure x)
  MkAL i f <*> MkAL j g  = MkAL (min i j) (f <*> g)

(爱好者会注意到我已经将应用程序的乘积与 ( , ) 幺半群(Int ->)诱导的乘积相结合。)maxBoundmin

你能做一个干净的通信吗

imAL :: Image -> ArrayLike Float
alIm :: ArrayLike Float -> Image

通过投影和制表?如果是这样,您可以编写这样的代码。

alIm $ (f <$> imAL a1 <*> ... <*> imAL an)

此外,如果您想将该模式包装为重载运算符,

imapp :: (Float -> ... -> Float) -> (Image -> ... -> Image)

这是类型类编程的标准练习!(询问您是否需要更多提示。)

但是,关键点是包装策略意味着您不需要为了将功能性上层结构放在上面而对数组结构进行猴子。

于 2011-08-11T12:34:14.617 回答
6

您希望如何对图像中的像素执行操作?也就是说,对于((+) <$> image1 <*> image2),您是想在 Haskell 中执行所有操作并构造一个新的结果图像,还是必须调用 C 函数来完成所有处理?

如果是前者,猪工的答案就是我会采取的方法。

相反,如果需要通过 C 处理所有图像操作,那么创建一个小的 DSL 来表示这些操作怎么样?

于 2011-08-11T13:56:28.353 回答
6

Image如果您将“像素”类型Float从有限和离散域(数组)推广到无限和连续域,您将获得更多的组合类型。作为这些概括的演示,请参阅论文功能图像和相应的(有限采样)示例图像库。因此,您会得到MonoidFunctorApplicativeMonad和的实例Comonad。而且,这些实例的含义完全由函数对应的实例决定,满足语义类型类态射的原则,如论文Denotational design with type class morphisms中所述. 该论文的第 13.2 节简要描述了图像。

于 2011-08-14T20:08:31.003 回答