2012-09-08 8 views
6

He visto un número de respuestas a las preguntas aquí en Stack Overflow tratando de encontrar una solución a mi problema al usar la biblioteca Reactive Banana. Todas las respuestas usan magia usando 'mapAccum' que no puedo entender del todo. En cuanto a la documentación de la API, todo lo que encuentro es "Combinación eficiente de accumE y accumB". que no es muy útil.¿Cómo funciona la función mapAccum de Reactive Banana?

Parece que esta función se puede utilizar para comparar los valores de Behavior en el momento de dos eventos consecutivos, que es lo que me gustaría hacer. Pero no tengo claro cómo hacer que funcione.

¿Cómo funciona exactamente mapAccum trabajo?

Respuesta

4

en cuenta que

mapAccum :: acc -> Event t (acc -> (x, acc)) -> (Event t x, Behavior t acc) 

por lo que toma un valor inicial :: acc a acumularse en, y un suceso que produce una función que actualizaciones que se acumularon valor, mientras que la producción de un valor de salida ::x. (Por lo general, haría un evento de este tipo aplicando parcialmente alguna función a través de <$>.) Como resultado, obtiene un nuevo Evento que dispara sus valores x cada vez que aparecen y un Comportamiento que contiene su valor acumulado actual.

Uso mapAccum si tiene un evento y desea realizar una conducta relacionada y evento.

Por ejemplo, en el dominio del problema de your other question, suponga que tiene un evento eTime :: Event t Int que disparó de manera irregular y que quería calcular eDeltaTime :: Event t Int de las diferencias y bTimeAgain :: Behaviour t Int por el tiempo utilizado actualmente:

type Time = Int 
type DeltaTime = Time 

getDelta :: Time -> Time -> (DeltaTime,Time) 
getDelta new old = (new-old,new) 

que podría tener escrito que getDelta new = \old -> (new-old,new) para hacer más claro el siguiente paso:

deltaMaker :: Event t (Time -> (DeltaTime,Time)) 
deltaMaker = getDelta <$> eTime 

(eDeltaT,bTimeAgain) = mapAccum 0 $ deltaMaker 

En este caso, habría bTimeAgain b e un comportamiento con el mismo valor que los eventos en eTime. Esto sucede porque mi función getDelta pasa new directamente sin cambios desde eTime al valor acc. (Si quería bTimeAgain por sí solo, habría utilizado stepper :: a -> Event t a -> Behaviour t a.) Si no necesito bTimeAgain, sólo podría escribir (eDeltaT,_) = mapAccum 0 $ deltaMaker.

4

La función mapAccum es muy similar a la función mapAccumL del módulo Data.List estándar, de ahí el nombre. La documentación en Hackage también incluye un enlace al source code of mapAccum. Junto con el type signature, es de esperar información suficiente para averiguar cómo funciona esta función.

Por otra parte, sólo podría mejorar la documentación. :-) Pero no estoy del todo claro sobre cómo hacerlo sin pegar el código fuente. La segunda parte del resultado es fácil de describir mediante la siguiente ecuación

snd . mapAccum acc = accumB acc . fmap (. snd) 

Pero la primera parte no tiene una buena ecuación de este tipo.

podría escribir una descripción con palabras:

La función mapAccum acumula un estado de tipo acc mediante la aplicación de las funciones contenidas en el segundo argumento de tipo Event t (acc -> (x,acc)). La función devuelve un evento cuyas ocurrencias son los valores x y un comportamiento que realiza un seguimiento del estado acumulado acc. Dicho de otra manera, esta es una máquina Mealy, o un autómata de estado.

pero no estoy seguro de si estas palabras realmente ayudan.

+0

Tres comentarios, 1) Creo que es genial que el usted, el autor, están aquí respondiendo a las preguntas. 2) La notación gratuita de puntos es difícil de leer si no está seguro de lo que hace la función porque no hay nombres. 3) Donde me perdí fue entendiendo que el 'acc' dado a la función en el evento de entrada era un buen lugar para almacenar el historial de eventos. Seguí tratando de poner el valor que estaba tratando de encontrar allí, cuando debería haber ido en la parte 'x'. –

1

Nota: Estoy usando una respuesta para que pueda escribir con formato de código

Aquí está mi intento de documentar la función:


mapAccum ::     acc -- Initial Accumulation Value 
    -> Event t (acc -> (x, acc)) -- Event stream that of functions that use the previous 
           -- accumulation value to: 
           -- a) produce a new resulting event (x) and, 
           -- b) updates the accumulated value (acc) 
    -> (Event t x, Behavior t acc) -- fst) a stream of events from of a) above 
           -- snd) a behavior holding the accumulated values b) above 

Esta función es el análogo a la mapAccumL función del módulo Data.List. Es una combinación eficiente de accumE y accumB. El Behavior resultante es útil, entre otras cosas, para mantener el historial de eventos previos que podrían ser necesarios para calcular los valores (x) de una secuencia de eventos.

Ejemplo: calcular la media Roling de los 5 últimos eventos

rolingAverage :: forall t. Frameworks t => Event t Double -> Event t Double 
rolingAverage inputStream = outputStream 
    where 
    funct x xs = (sum role/5, role) where role = (x:init xs) 
    functEvent = funct <$> inputStream -- NB: curries the first parameter in funct 
    (outputStream,_) = mapAccum [0,0,0,0,0] functEvent 
Cuestiones relacionadas