Reputation: 128
I'm trying to read data as Doubles from stdin, manipulate them and write them as well. What I've come up with so far is:
import qualified Data.ByteString.Lazy as B
import Data.Binary.IEEE754
import Data.Binary.Get
-- gives a list of doubles read from stdin
listOfFloat64le = do
empty <- isEmpty
if empty
then return []
else do v <- getFloat64le
rest <- listOfFloat64le
return (v : rest)
-- delay signal by one
delay us = 0 : us
-- feedback system, add delayed version of signal to signal
sys us = zipWith (+) us (delay us)
main = do
input <- B.getContents
let hs = sys $ runGet listOfFloat64le input
print $ take 10 hs
The idea is to fead data to the program which is then passed through a feedback system before it is written to stdout. Although right now it just prints the first 10 values.
This works but does not seem to evaluate lazily. I.e it has to read all the input into memory. So:
dd if=/dev/urandom bs=8 count=10 | runhaskell feedback.hs
will work just fine but:
dd if=/dev/urandom | runhaskell feedback.hs
will not. My guess is it's the listOfFloat64le
function that makes things not work properly. So how do I create an iterable to pass into my sys
function without having to read everything into memory?
I'm not a very experienced haskeller.
Upvotes: 4
Views: 402
Reputation: 11218
This seems a standards problem where you can easily use something like pipes
or conduits
. You can make stdin
as the source and stdout
as the sink and apply the transformer as a conduit
.
Upvotes: 0
Reputation: 128
I took another route by instead splitting the ByteString at intervals of 8 bytes and mapping over it instead:
import qualified Data.ByteString.Lazy as L
import Data.Binary.IEEE754
import Data.Binary.Get
-- delay signal by one
delay us = 0 : us
-- feedback system, add delayed version of signal to signal
sys us = zipWith (+) us (delay us)
-- split ByteString into chunks of size n
chunk n xs = if (L.null xs)
then []
else y1 : chunk n y2
where
(y1, y2) = L.splitAt n xs
main = do
input <- L.getContents
let signal = map (runGet getFloat64le) (chunk 8 input)
print $ take 10 (sys signal)
This seem to work atleast but I don't know what the performance is like.
EDIT: I switched from chunk
to chunker
which uses runGetState instead:
chunker :: Get a -> L.ByteString -> [a]
chunker f input = if (L.null input)
then []
else val : chunker f rest
where
(val, rest, _) = runGetState f input 0
And using it like: let signal = chunker getFloat64le input
Upvotes: 1
Reputation: 13253
See this question. Looks like Binary become more strict than it was long time ago when I used it.
Upvotes: 0