用于编写破坏性运算符的更好界面,第二部分

发布于 2024-11-08 03:55:02 字数 1557 浏览 3 评论 0原文

请参阅我之前关于编写 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

问题

  1. 首先,这是一个合理的结构吗?
  2. 是否有理由更喜欢上一个问题中的结构?
  3. 您如何扩展它来实现“找到图像中的最小值,从图像中减去它,然后将图像与其范围相乘(即最大值-最小值)”的操作。
  4. 我应该将这些问题分成多个问题吗?

See my earlier question about composing opencv operators for an explanation of what is going on.

I thought up a new interface that allows to compose destructive binary operations in a kind of composable way:

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 

With this I can easily express the 'subtract the gaussian operator':

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

To me this seems like a quite safe alternative, though it is not optimal in the sense, that it probably could re-use also the cloned image if some operation after subtract would require this. But it still seems like an acceptable alternative to the completely pure and unoptimized version:

mulScalar 5 $ gaussian (11,11) img `subtract` img
-- Or with nicer names for the operators
5 * gaussian (11,11) img - img

Questions

  1. Is this a reasonable structure in the first place?
  2. Is there a reason to prefer the structure in the previous question?
  3. How would you extend this to implement an operation 'find the minimum value in the image, subtract it from the image and then multiply the image with its range (i.e max-min).'
  4. Should I split these into multiple questions instead?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

所有深爱都是秘密 2024-11-15 03:55:02

继续哈马尔的评论,您可以从使用 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))

我认为,这是这种风格非常具有表现力的一个很好的论据。

Continuing from hammar's comment, you can just use kleisli composition to begin with, avoiding the IOP altogether. I've kept ImageOp as a type synonym for clarity's sake. Also, I specialized it to always return unit, and changed some other type signatures accordingly so that we have a typed difference between mutating functions (returning unit) and cloining functions (returning a new value), and a function apOp that applies a mutating function and returns the mutated value so that we can chain mutations easily.

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) 

Edit
If you feel like it, you can write &#& neatly as follows:

f &#& g = uncurry (liftM2 (,)) . (apOp f &&& withClone (apOp g))

Which, I think, is a good argument for this style being pretty expressive.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文