2011-08-11 12 views
10

Tengo un tipo Image que es básicamente una c-matriz de flotadores. Es fácil crear funciones como map :: (Float -> Float) -> Image -> Image, o zipWith :: (Float -> Float -> Float) -> Image -> Image -> Image.Aplicativo sin un functor

Sin embargo, tengo la sensación de que también sería posible proporcionar algo que se parece a una instancia aplicativo en la parte superior de estas funciones, lo que permite manipulaciones a nivel de píxel más flexibles como ((+) <$> image1 <*> image2) o ((\x y z -> (x+y)/z) <$> i1 <*> i2 <*> i3). Sin embargo, el enfoque ingenuo falla, ya que el tipo de imagen no puede contener elementos distintos de flotantes, por lo que es imposible implementar fmap como tal.

¿Cómo podría implementarse esto?

+3

¿Por qué no permites que una imagen contenga otra cosa que flotadores? Claro, su 'display :: Image Float -> IO()' solo podría tomar imágenes con flotadores, pero para otras funciones como 'map' no importaría. –

+1

El tipo de imagen es una matriz c que debe pasarse a funciones c, sin pasar demasiado tiempo haciendo conversiones. Además, supongo, que si pudiera tener funciones como funciones, tener unos pocos millones de funciones parcialmente evaluadas después de llamar a "puro" sería bastante desagradable en cuanto a rendimiento. (Implementar 'pure' al tener una Imagen de funciones también es problemático ya que no puede conocer el tamaño de la imagen.) – aleator

+0

puede definir su tipo como' tipo Image = CArray Float' y crear una instancia de Functor para CArray con fmap siendo su función de mapa, y se asegura de que no se puede hacer un CArray de nada más que flotador de CArray (no exportar el constructor, por ejemplo) –

Respuesta

15

Al leer los comentarios, estoy un poco preocupado de que el tamaño esté debajo de la alfombra aquí. ¿Hay un comportamiento sensato cuando los tamaños no coinciden?

Mientras tanto, puede haber algo que pueda hacer con sensatez en las siguientes líneas. Incluso si sus matrices no son fáciles de hacer polimórficas, puede hacer una instancia Applicative como esta.

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) 

(Los entusiastas en cuenta que me he tomado el producto de la (Int ->) aplicativo con la inducida por el maxBound, min) monoide (.)

podría hacer una correspondencia limpia

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

por proyección y tabulación? Si es así, puedes escribir código como este.

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

Por otra parte, si se desea envolver ese patrón como un operador sobrecargado,

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

es un ejercicio de programación estándar en la clase de tipos! (Pregunte si necesita más información).

El punto crucial, sin embargo, es que la estrategia de envoltura significa que no necesita simular con sus estructuras de matriz para poner la superestructura funcional en la parte superior.

+0

¡Genial! Más o menos exactamente lo que estaba buscando. No puedo entender por qué no lo vi yo mismo. ¿Quisiste decir hacer N instancias para N funciones de aria diferentes, o algo más inteligente por el comentario de clase de clase? – aleator

+2

Solo debe necesitar dos instancias: un caso base para arity 0 (o 1 si necesita una entrada de matriz para vincular el tamaño de la salida), y un caso de paso. Espera (el código lamentable es sospechoso en los comentarios) ... 'class NAry ff fi | ff -> fi, fi -> ff donde {naryHelp :: (ArrayLike (Float -> ff)) -> (Imagen -> fi); nary :: (Float -> ff) -> (Imagen -> fi); nary = naryAyuda. puro}; instancia NAry Float Image donde {naryHelp f i = alIm $ f <*> imAL i}; instancia NAry ff fi => NAry (Float -> ff) (Imagen -> fi) donde {naryHelp fi = naryHelp (f <*> imAL i)} ' – pigworker

+1

Solo un pensamiento adicional:' ArrayLike' es básicamente como las matrices de retraso de reparación , ¿derecho? Por lo menos, es fácil ver cómo podría insertar fácilmente cálculos paralelos en 'alIm' y definir' force' como 'imAl. ALIm'. – aleator

6

¿Cómo esperaría realizar operaciones en píxeles en una imagen? Es decir, para ((+) <$> image1 <*> image2), ¿le gustaría realizar todas las operaciones en Haskell y construir una nueva imagen resultante, o tendría que llamar a las funciones C para hacer todo el procesamiento?

Si es la primera, la respuesta del cerdo es el enfoque que tomaría.

Si, en cambio, se requiere que todas las manipulaciones de imágenes se manejen a través de C, ¿qué tal crear una pequeña DSL para representar las operaciones?

+0

La imagen sería manipulada principalmente por C. Lo que busco es una forma de realizar operaciones simples a nivel de píxel en haskell: cuesta trabajo bajar hasta c para agregar una función simple como atan2. – aleator

6

que obtendrá un tipo de composición mucho más Image si se generaliza el tipo "pixel" de Float y se extienden desde finitos & discretas de dominio (arrays) a infinito & dominio continuo. Como una demostración de estas generalizaciones, consulte el documento Functional Images y una galería correspondiente de (muestreos finitos de) example images. Como resultado, obtiene instancias de Monoid, Functor, Applicative, Monad y Comonad. Además, los significados de estas instancias están completamente determinados por las instancias correspondientes para las funciones, que satisfacen el principio de clase semántica morfismos, como se describe en el documento Denotational design with type class morphisms. La sección 13.2 de ese documento describe brevemente las imágenes.

Cuestiones relacionadas