Meilleure interface pour la composition destructrice opérateurs, Partie II
-
07-09-2020 - |
Question
Voir mon précédent question sur la composition des opencv opérateurs pour une explication de ce qui se passe.
J'ai pensé à une nouvelle interface qui permet de composer des destructeurs d'opérations binaires dans une sorte de composable façon:
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
Avec cela, je peux facilement exprimer la " soustraction de la gaussienne de l'opérateur:
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
Pour moi, cela semble une assez alternative sûre, même si elle n'est pas optimale dans le sens, qu'il n'aurait probablement ré-utiliser également l'image clonée si une opération après soustraire cette.Mais il semble toujours comme une solution de rechange acceptable à la pure et unoptimized version:
mulScalar 5 $ gaussian (11,11) img `subtract` img
-- Or with nicer names for the operators
5 * gaussian (11,11) img - img
Questions
- Est-ce une structure raisonnable, en premier lieu?
- Est-il une raison pour préférer la structure dans le précédent question?
- Comment voulez-vous étendre cette mesure à mettre en œuvre une opération de " trouver la valeur minimale de l'image, de la soustraire de l'image, puis de multiplier l'image avec sa gamme (j'.e max-min).'
- Dois-je les scinder en plusieurs questions à la place?
La solution
Continuant à partir de hammar commentaire, vous pouvez simplement utiliser kleisli composition pour commencer, en évitant le IOP tout à fait.J'ai gardé ImageOp comme un type synonyme pour plus de clarté.Aussi, je me suis spécialisé à toujours retourner l'appareil, et changé quelques autres type de signatures en conséquence, de sorte que nous avons tapé la différence entre une mutation des fonctions (de retour de l'unité) et cloining fonctions (de retour d'une nouvelle valeur), et une fonction apOp
qui s'applique à une mutation de la fonction et renvoie la muté valeur, de sorte que nous pouvons chaîne mutations facilement.
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)
Modifier
Si vous en avez envie, vous pouvez écrire &#&
parfaitement comme suit:
f &#& g = uncurry (liftM2 (,)) . (apOp f &&& withClone (apOp g))
Ce qui, je pense, est un bon argument en faveur de ce style étant assez expressif.