2

我收集实时信号,计算派生信号并将原始数据和派生数据存储在循环缓冲区中,因此我只保存最后一百万个样本。

有时我需要序列化所有信号的当前值。所以我需要类似的东西:

type D0 a = M.Map SignalType D1

data D1 a = D1 
    { foo :: M.Map DoorType a
    , bar :: D2 a
    , baz :: a
    }

data D2 = D2 
    {
        quux :: a
    ,   zoo :: a
    }

data MyData = D0 SignalBuffer 

data CurrentSignals = D0 SignalValue

SignalBuffer是一个序列SignalValue。它可以是一个未装箱的浮点数组。Haskell 可以Functor为我派生实例,因此我可以使用从每个fmap中获取最后一个并将结构传递给以进行序列化。SignalValueSignalBufferAeson

如何实现循环缓冲区 API,SignalBuffer以便在新滴答到达时将新值推送到所有缓冲区?我想节省内存,所以我想我必须使用未装箱的数组。使用可变的未装箱数组(STUArray?)是否有利,因此数组更新不会堆积在内存中?是否可以在此设置中使用可变数组?我已经准备好改变MyDataCurrentSignals无论做什么工作。

我知道如何实现循环缓冲区,问题是如何优雅地将更新应用于MyData.

我在想类似的东西

type UpdateFunc a = MyData -> SignalValue -> Modifier SignalBuffer

updateAllBuffers :: D0 UpdateFunc -> Modifier MyData

一些信号是其他信号的“卷积”(不是真正的卷积,而是一种类似的处理)。要更新信号的缓冲区,我需要访问其他信号的缓冲区——这就是 UpdateFunc 接受MyDataSignalValue返回缓冲区修改函数的原因。

updateAllBuffers然后“拉链”D0 UpdateFuncMyData获得新的MyData.

当然,我已经准备好使用Modifier适合我任务的任何东西——它可以是一个函数、一个单子值等。

4

1 回答 1

0

我不完全理解你想用上面的代码做什么,但你可以使用IOVectorData.Vector.Unboxed.Mutable高性能数组创建一个循环缓冲区:

{-# LANGUAGE GADTs #-}

import Data.IORef (IORef, newIORef, readIORef, writeIORef)
import Data.Vector.Unboxed.Mutable (IOVector, Unbox)
import qualified Data.Vector.Unboxed.Mutable as V

data CircularBuffer a where
  CircularBuffer :: Unbox a =>
    { start :: IORef Int -- index for getting an element
    , end   :: IORef Int -- index for putting an element
    , array :: IOVector a
    } -> CircularBuffer a 

newCircularBuffer :: (Unbox a) => Int -> IO (CircularBuffer a)
newCircularBuffer size = CircularBuffer <$> newIORef 0 <*> newIORef 0 <*> V.new size 

putCircularBuffer :: Unbox a => a -> CircularBuffer a -> IO ()
putCircularBuffer newEndValue (CircularBuffer _start end array) = do
  endIndex <- readIORef end
  V.write array endIndex newEndValue
  writeIORef end $! (endIndex + 1) `mod` V.length array

getCircularBuffer :: Unbox a => CircularBuffer a -> IO a
getCircularBuffer (CircularBuffer start _end array) = do
  startIndex <- readIORef start
  startValue <- V.read array startIndex
  writeIORef start $! (startIndex + 1) `mod` V.length array
  pure startValue

然后,您可以制作一个类似函数的映射(尽管它是 IO),它将一个函数应用于CircularBuffer's 数组中的每个项目。

于 2019-11-19T20:34:45.813 回答