Pergunta

Veja meu anterior pergunta sobre a composição de operadores opencv para uma explicação do que está acontecendo.

Eu pensei em uma nova interface que permite compor operações binárias destrutivas de uma forma combinável:

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 

Com isso posso expressar facilmente o 'subtrair o operador gaussiano':

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

Para mim, esta parece ser uma alternativa bastante segura, embora não seja ideal no sentido de que provavelmente poderia reutilizar também a imagem clonada se alguma operação após a subtração exigir isso.Mas ainda parece uma alternativa aceitável à versão completamente pura e não otimizada:

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

Questões

  1. Esta é uma estrutura razoável em primeiro lugar?
  2. Existe uma razão para preferir a estrutura do anterior pergunta?
  3. Como você estenderia isso para implementar uma operação 'encontre o valor mínimo na imagem, subtraia-o da imagem e depois multiplique a imagem por seu intervalo (ou seja, máximo-mínimo).'
  4. Devo dividi-las em várias perguntas?
Foi útil?

Solução

Continuando com o comentário de hammar, você pode simplesmente usar a composição kleisli para começar, evitando completamente a PIO.Mantive ImageOp como sinônimo de tipo para maior clareza.Além disso, especializei-o para sempre retornar a unidade e alterei algumas outras assinaturas de tipo de acordo, para que tenhamos uma diferença digitada entre funções de mutação (retornando unidade) e funções de clonagem (retornando um novo valor) e uma função apOp que aplica uma função mutante e retorna o valor mutado para que possamos encadear mutações facilmente.

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) 

EditarSe você quiser, você pode escrever &#& ordenadamente da seguinte forma:

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

O que, creio eu, é um bom argumento para que esse estilo seja bastante expressivo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top