题
看我之前的 问题 关于编写 opencv 运算符以解释正在发生的事情。
我想出了一个新的接口,允许以一种可组合的方式组合破坏性的二进制操作:
newtype IOP a b = IOP (a -> IO b)
instance Category IOP where
id = IOP return
(IOP f) . (IOP g) = IOP $ g >=> f
(&#&) :: IOP (Image c d) e -> IOP (Image c d) f
-> IOP (Image c d) (Image c d,Image c d)
(IOP f) &#& (IOP g) = IOP $ op
where
op i = withClone i $ \cl -> (f i >> g cl >> return (i,cl))
runIOP (IOP f) img = withClone img f
这样我就可以轻松地表达“减去高斯运算符”:
subtract :: IOP (Image c d, Image c1 d1) (Image c d)
mulScalar :: d -> IOP (Image c d) (Image c d)
subtractScalar :: d -> IOP (Image c d) (Image c d)
gaussian :: (Int, Int) -> IOP (Image GrayScale D32) (Image GrayScale D32)
(gaussian (11,11) &#& id) >>> subtract >>> mulScalar 5
对我来说,这似乎是一个非常安全的替代方案,尽管从某种意义上来说它不是最佳选择,如果减去后的某些操作需要这样做,它可能也可以重新使用克隆图像。但它似乎仍然是完全纯净且未优化版本的可接受的替代方案:
mulScalar 5 $ gaussian (11,11) img `subtract` img
-- Or with nicer names for the operators
5 * gaussian (11,11) img - img
问题
- 首先,这是一个合理的结构吗?
- 有没有理由更喜欢前面的结构 问题?
- 您如何扩展它来实现“找到图像中的最小值,从图像中减去它,然后将图像与其范围(即最大值-最小值)相乘”的操作。
- 我应该将这些问题分成多个问题吗?
解决方案
继续哈马尔的评论,您可以从使用 kleisli 组合开始,完全避免 IOP。为了清楚起见,我将 ImageOp 作为类型同义词。另外,我将它专门化为始终返回单位,并相应地更改了一些其他类型签名,以便我们在变异函数(返回单位)和克隆函数(返回新值)和函数之间存在类型差异 apOp
它应用变异函数并返回变异值,以便我们可以轻松链接变异。
type ImageOp c d -> Image c d -> IO ()
(&#&) :: ImageOp c d -> ImageOp c d -> (Image c d) -> IO (Image c d, Image c d)
f &#& g = \i -> withClone i $ \cl -> f i >> g cl >> return (i,cl)
apOp :: ImageOp c d -> Image c d -> IO (Image c d)
apOp iop x = fmap x (iop x)
subtract :: Image c d -> ImageOp c1 d1
mulScalar :: d -> ImageOp (Image c d)
subtractScalar :: d -> ImageOp (Image c d)
gaussian :: (Int, Int) -> ImageOp GrayScale D32
myFun = (gaussian (11,11) &#& id) >=> (\(x,y) -> apOp (subtract x) y) >=> apOp (mulScalar 5)
--pointfree
myFun = (gaussian (11,11) &#& id) >=> uncurry (apOp . subtract) >=> apOp (mulScalar 5)
编辑如果你喜欢的话可以写 &#&
整齐地如下:
f &#& g = uncurry (liftM2 (,)) . (apOp f &&& withClone (apOp g))
我认为,这是这种风格非常具有表现力的一个很好的论据。
不隶属于 StackOverflow